# HG changeset patch
# User Peter Gervai <grin@grin.hu>
# Date 1236689398 -3600
# Node ID c7f6b056b673360ceb58585b32fa1e7b8bbdd1b6

First import of vendor version

diff -r 000000000000 -r c7f6b056b673 .config
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.config	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,6 @@
+./Makefile ./Makefile.inc ./Makefile.inc2 ./cdcc/Makefile ./cgi-bin/Makefile ./cgi-bin/chgpasswd ./cgi-bin/common ./cgi-bin/edit-whiteclnt ./cgi-bin/list-log ./cgi-bin/list-msg ./cgi-bin/webuser-notify ./dbclean/Makefile ./dblist/Makefile ./dccd/Makefile ./dccd/dump-clients/Makefile ./dccifd/Makefile ./dccifd/dccif-test/Makefile ./dccifd/dccif.pl ./dcclib/Makefile ./dccm/Makefile ./dccproc/Makefile ./dccsight/Makefile ./dns-helper/Makefile ./gmake.inc ./homedir/Makefile ./homedir/dcc_conf ./include/kludge.h ./misc/Makefile ./misc/cron-dccd ./misc/crontab ./misc/dcc-stats-collect ./misc/dcc-stats-graph ./misc/dcc-stats-init ./misc/dcc.m4 ./misc/fetch-testmsg-whitelist ./misc/fetchblack ./misc/fetchids ./misc/list-clients ./misc/newwebuser ./misc/rcDCC ./misc/start-dccd ./misc/start-dccifd ./misc/start-dccm ./misc/start-grey ./misc/stats-get ./misc/stop-dccd ./misc/uninstalldcc ./misc/updatedcc ./rrd-combine/Makefile ./srvrlib/Makefile ./thrlib/Makefile ./cdcc.8 ./dbclean.8 ./dblist.8 ./dcc.8 ./dccd.8 ./dccifd.8 ./dccm.8 ./dccproc.8 ./dccsight.8 ./FAQ.html ./INSTALL.html ./cdcc.html ./dbclean.html ./dblist.html ./dcc.html ./dccd.html ./dccifd.html ./dccm.html ./dccproc.html ./dccsight.html 
+config.log
+confdefs.h
+config.cache
+config.status
+ck2ip
diff -r 000000000000 -r c7f6b056b673 .manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,43 @@
+.config
+CHANGES
+FAQ.html.in
+INSTALL.html.in
+LICENSE
+Makefile.in
+Makefile.inc.in
+Makefile.inc2.in
+RESTRICTIONS
+cdcc.0
+cdcc.8.in
+cdcc.html.in
+configure
+dbclean.0
+dbclean.8.in
+dbclean.html.in
+dblist.0
+dblist.8.in
+dblist.html.in
+dcc.0
+dcc.8.in
+dcc.html.in
+dcc.ide
+dccd.0
+dccd.8.in
+dccd.html.in
+dccifd.0
+dccifd.8.in
+dccifd.html.in
+dccm.0
+dccm.8.in
+dccm.html.in
+dccproc.0
+dccproc.8.in
+dccproc.html.in
+dccsight.0
+dccsight.8.in
+dccsight.html.in
+gmake.inc.in
+win32.mak
+win32.makinc1
+win32.makinc2
+.manifest
diff -r 000000000000 -r c7f6b056b673 CHANGES
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CHANGES	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,2254 @@
+Changes to the Distributed Checksum Clearinghouse source.
+
+    2009/02/26 02:52:46 Rhyolite Software DCC 1.3.103-1.295 $Revision$
+
+1.3.103
+    Quiet RedHat versus Debian error message from rcDCC reported by Ken Rea.
+    Deal with corrupt /var/dcc/map reported by Steve Martin instead of
+	calling abort().
+    Fix error in libexec/fetch-testmsg-whitelist reported by Horst Scheuermann
+	and William Taylor.
+    Tweak ./configure and makefiles to try to avoid the mysterious,
+	unreproducable linking problem reported by John Levine.
+    Fix bug with `./configure --with-max-log-size=0` reported by
+	Valentin Schmid.
+    `./configure --with-max-log-size=KB` now also applies to dccproc log files.
+    Generate man pages with /var/dcc and other directories replaced by
+	local ./configure choices.  This is intended to help the FreeBSD
+	package	and similar redistributions.
+
+1.3.102
+    Fix build error reported by Steve Martin in dnsbl.c on MacOS X and other
+	systems without a resolver library found by ./configure.
+
+1.3.101
+    Fix inconsistent declaration of grey_on in dccd/dump-clients/dump-clients.c
+	reported by Bobby Rose.
+
+1.3.100
+    Support groups of DNS blacklists that can be independently enabled
+	in per-user whiteclnt files.
+    Follow Petar Bogdanovic's suggestion to make ./configure assume that 
+	`su -` and the default file ownership on NetBSD should be like FreeBSD
+    Another tweak to reduce spurious DCC Reputations for 127.0.0.1.
+    Dccifd in query mode assumes one recipient and so always generates an
+	X-DCC header.
+    Tweak proof of concept per-user whitelist cgi scripts in cgi-bin.
+    Improve long term client request rate computation to improve how public
+	DCC servers handle too active clients.
+    Count anonymous clients ignored by `dccd -uFOREVER` among `cdcc stats`
+	"bad IDs" to more easily detect local clients that lack client-IDs
+	and passwords.
+    `misc/hackmc -M` now reports mail rejected with the sendmail
+	FEATURE(`badmx') to the DCC with counts of "MANY"
+
+1.3.99
+    Fix typo in Makefile.inc for NetBSD and OSF1 reported by Petar Bogdanovic.
+
+1.3.98
+    Change the DCC server to not sign responses to anonymous clients with
+	the client's sequence numbers in protocol version 9.
+    Add `dccd -T wlist` and `cdcc "trace wlist on"` to help find failures
+	by clients to whitelist IP addresses and other checksums
+	in /var/dcc/whitelist.
+    Let whitelisting by the MTA, DCC server, or other whiteclnt lines override
+	"option spam-trap-accept" and "option spam-trap-reject" whiteclnt
+	lines as suggested by Horst Scheuermann.
+    Finally document in the man pages parameters including %CIP that can be
+	used in dccifd and dccm rejection messages.
+
+1.3.97
+     Fix "pthread_mutex_lock(cwf): Invalid argument; fatal error" reported
+	by Steve Martin.
+
+1.3.96
+    Add `cdcc "clock check" to help detect broken clocks at DCC servers.
+    Fix intermittent complaints about whiteclnt.dccw reported by Gary Mills.
+    `cdcc clients` now indicates clients that have pegged a server's
+	anti-DoS delays.
+
+1.3.95
+    Stop rare "fcntl(F_SETLKW F_WRLCK info  -1): Bad file descriptor" complaints
+	when dccm and dccifd start.
+
+1.3.94
+    Fix core new dump in version 1.3.93 of dccm with aborted mail messages.
+
+1.3.93
+    Make the default value for the `dccm -j` and `dccifd -j` job limit
+	as large as possible.  This makes -j settings unnecessary.
+    Dccproc and cdcc time out after about 1 minute when the /var/dcc/map
+	file is not unlocked.
+    Add "option spam-trap-accept" and "option spam-trap-reject" to
+	whiteclnt files.  I think these are the best way to build
+	DCC spam traps.
+
+1.3.92
+    Improve the hash function used in the DCC server database.
+    Replace -Bno-envelope for dccm, dccproc, and dccifd with
+	-Bno-client and -Bno-mail_host for Tony Del Porto.  It seems that
+	Spamhaus' PBL should generally not be applied to SMTP envelope
+	Mail_From domain names to avoid rejecting mail received through an
+	ISP smart-host but with sender domain name hosted on a dynamically
+	assigned IP address.  The now undocumented -Bno-envelope implies
+	-Bno-client and -Bno-mail_host.
+    Fix the @configsuffix@ mechanism in homedir/Makefile.in as suggested
+	by Craig Green.
+    Switch to -lpthread threads on FreeBSD starting with 6.2 because of
+	recent problems with libc_r threads.
+    Dccproc should not require a "option DNSBL-on" line in /var/dcc/whiteclnt
+	to pay attention to DNSBL hits.  The -B settings on the dccproc
+	command line are sufficient to show that the user wants DNSBL checking.
+    Fix bug in compression of DCC Reputation reports.
+
+1.3.91
+    Fix mechanism that should prevent dccd from starting dbclean for a quick
+	cleaning about the time the cron job runs.
+    Let DNSBL target addresses be CIDR blocks to improve the use of Spamhaus' 
+	lists.
+    Fix DNSBL bug that caused false positives reported by Ray Gardener.
+    Tweak homedire/Makefile.in for the gento folks.
+    Fix recent compiling bug with Borland on WIN32 reported by Tommy Barberis.
+
+1.3.90
+    Fix updatedcc problem reported by Chris Magnuson.
+	Updatedcc failed after shutting down the localhost DCC server and
+	finding no working server and when the environment variable
+	DCC_UPDATEDCC_FAST is not set to "yes".  The easiest work-around
+	is to add the public DCC servers to the local /var/dcc/map file
+	with `cdcc "add dcc1.dcc-servers.net RTT+1000 ms"`  Besides working
+	around the updatedcc problem, that uses the public DCC servers
+	as backups for the local server.
+
+1.3.89
+    Repair compile problem on Solaris
+
+1.3.88
+    Repair rate limiting on dccd syslog complaints.
+    Relax dccd load sharing enough to prevent spurious timeouts by
+	keepalive timers and some troubles with flood connections.
+
+1.3.87
+    Add `./configure --enable-64-bits` to compile 64-bit DCC server code
+	for Solaris or Linux PowerPC.  If you are using dccm, you will
+	need to build a 64-bit sendmail milter library.
+    Fix complaint from `cdcc "new map"` about the new file being empty.
+    Fix bug in `./configure --with-installroot=DIR` and `make install`
+	reported by Pavel Urban.
+    Fix at least some causes of "continue not asking Greylist" complaints
+	from dccm and dccifd.
+    Make dbclean on Linux systems with lots of RAM even closer to -F.
+
+1.3.86
+    Disable automatic 64-bit compilation for Solaris again
+
+1.3.85
+    Fix Redhat Enterprise 5.1 build bugs in 1.3.84 reported by Mark Thomas.
+    Fix old glitch in building for 64-bit Solaris systems.
+
+1.3.84
+    Allow very large DCC database hash tables, including that used
+	for greylisting.
+    Add `./configure --with-configsuffix=.str` to improve FreeBSD port.
+    Add `rcDCC -m {dccd|dccm|...}` to improve FreeBSD port.
+    Add magic comments to rcDCC to make it work with SUSE insserv.
+
+1.3.83
+    Deal with build problem on FreeBSD 7.0 reported by Craig Green.
+    Keep client IP addresses as old as 7 days in /var/dcc/dccd_clients
+	and /var/dcc/grey_clients
+
+1.3.82
+    Fix automagic upgrade of old /var/dcc/map files reported by James Carlson
+	and Earl Killian.  The bug was new with 1.3.81.
+
+1.3.81
+    improve SMTP status messages from dccm and dccifd
+    improve dbclean handling of less frequent spam
+    do something like `dbclean -F` on systems that lack mmap(MAP_NOSYNC).
+	This should help recent versions of Linux that thrash themselves
+	much as Solaris always has.
+    perhaps fix the "Deadlock situation detected/avoided" messages long
+	but infrequently seen on Solaris.
+    fix a bug reported by Edward Toton in the mechanism that works around a
+	missing cron-dccd cron job.
+    fix problem in cron-dccd reported by Dean Maluski when a greylist server
+	is running but no DCC server.
+
+1.3.80
+    fix bug with `dccm -t` log thresholds reported by Bart Dumon
+
+1.3.79
+    remove -t arg. for dbclean
+    change lines in log files for DNSBL hits to include IP address from
+	the DNSBL
+    probe a DNSBL only once for several -B results with distinct SMTP
+	4yz or 5yz rejection messages
+    reduce dccifd memory on some Linux systems by 4 MByte
+    do not use set-UID privileges outside the ./configure --homedir=DIR
+	directory
+    fix bug in 1.3.78 in sizing the window for large (>100 MByte) greylist
+	databases found by Tomasz Potega
+
+1.3.78
+    Fix failure to reduce default dbclean expirations when working around
+	a missing cron-dccd cron job.
+
+1.3.77
+    Improve dccd load limiting, including while catching up on flooding.
+
+1.3.76
+    Add yet more system log tracing with `dccd -d` for the determination of
+	memory limits.
+    Tweak duplicate flooded report detection.
+
+1.3.75
+    Fix false duplicate detection of flooded checksums introduced in
+	version 1.3.74.
+    Make the rep-total default threshold be 20, matching the documentation.
+
+1.3.74
+    Repair rate limiting of dccd system log messages.
+    Another fix for detecting duplicate bulk mail reports.
+
+1.3.73
+    Correct count of reputation hits.
+
+1.3.72
+    Fix holes in the detection of duplicate flooded reports.
+    Fix quick database cleaning to not run dbclean 2 hours or less before
+	the usual cron cleaning.
+    Fix bug in counting DCC operations by the free DCC servers.
+
+1.3.71
+    Fix confusion in daily log messages between incoming and outgoing
+	flood error messages.
+
+1.3.70
+    /var/dcc/libexec/dcc-stats-graph no longer combines RRD files to
+	generate a graph. Instead the new /var/dcc/rrd-combine should be used
+	to generate a combined file that is then graphed.
+    Fix dccproc to report mail to the DCC server that DCC Reputations has
+	marked as spam.
+    Remove SOCKS flooding input bug that I added in 1.3.67.
+    Fix a failure by DCC Reputation servers to fail to detect flooded
+	duplicate reports.
+
+1.3.69
+    Fix embarrassing build bug in 1.3.68 reported by Chris Pollock.
+
+1.3.68
+    Enhance /var/dcc/libexec/list-clients
+    Increase flooding listen() queue to try to deal with connection timeouts.
+    dccifd should pay attention to thresholds in /var/dcc/whiteclnt
+    Fix new fix for reputation report counting.
+
+1.3.67
+    Occassionally run a quick dbclean on the server database when the database
+	gets too big.
+    Report "connection refused" flooding problems in `cdcc "flood stats ..."`
+    `cdcc stats` on DCC Reputation servers report the number of client
+	reputation hits.
+    updatedcc -K does not try to download more than once per week.
+	-K is assumed if stdin is not a tty.  Some installations seem to
+	have cron jobs that run updatedcc several times per day.
+    SOCKS flooding only doubles instead of quadruples the backoff or delay
+	before retrying connections.
+    Add more tracing of flood state changes to try to find the stickiness
+	with Solaris.
+
+1.3.66
+    Fix SOCKS flood crash introduced in 1.3.65 and reported by Tomasz Potega.
+
+1.3.65
+    Another tweak to the negotiation of DCC Reputations.
+    Restore recently lost logging of flooding error messages.
+    Fix missing reset of keepalive timer.
+    Include flooding position in `cdcc "flood stats ..."`
+    Fix rm and rmdir complaints from cron-dccd on Solaris reported
+	by Mark Thomas.
+    SUBMIT whiteclnt entries now also turn off DCC Reputation checking.
+	There are better ways to turn off mail from a local SMTP client
+	DCC Reputations.
+    Fix looping whitelisted flooded report bug reported by John L.
+	This bug might be related to crashes complaining
+	"ifp->ibuf_len=-111; fatal error"
+
+1.3.64
+    Make ./configure and so updatedcc complaints about bad memory sizes
+	warnings instead of fatal errors.
+
+1.3.63
+    Correct error in `cdcc "flood list"` announced negotiation of DCC
+	Reputations.
+
+1.3.62
+    Let "option threshold type,val" lines in whiteclnt file accept "all"
+	and "cmn" for "type" as with `dccproc -c` and `dccm -t` and `dccifd -t`.
+    Use yet another scheme in updatedcc to detect download failures that
+	won't force unneeded downloads.
+    Fix dccifd man page about the location of the socket as suggested by
+	Carl Byington.
+    Fix several rare or potential bugs related to broken TCP connections with
+	DCC flooding including one that has caused a core dump.
+
+1.3.61
+    Fix problems with Sun Studio 12 compilers reported by Rob McMahon.
+    `updatedcc -K` or cron mode is silent when things go ok,
+	 or at least less chatty.
+
+1.3.60
+    Fix confusion in flooding connection accounting.
+    Work around new Fedore Core 6 gcc Fortify buffer over-non-flow bug
+	reported by Joseph Breu by reducing the size of server-to-server
+	messages by 1 byte.
+    Reduce the number of socket() and bind() system calls in dccm and dccifd.
+    Close unused sockets in dccm and dccifd after bursts of mail such
+	as dictionary attacks.
+    Prevent complaints during flooding from between commercial and free
+	versions about bad protocol versions.
+
+1.3.59
+    Fix crashing in dccifd reported by John M. Crawford.
+    Fix problem in flooding server-ID assertions.
+
+1.3.58
+    Fix bug in recent versions of `/var/dcc/libexec/dcc-stats-graph -d`
+	reported by Kevin W. Gagel.
+    Deal with multiple A RR answers from DNSBLs such as Spamhaus' ZEN
+	for dccifd, dccproc, and dccm -B.
+    Turn off a "close(socket): Connection reset by peer" message from
+	dccifd in proxy mode when postfix gets anxious and closes early.
+
+1.3.57
+    Fix bug in libexec/fetchblack adding a local blacklist file
+	reported by Krzysztof Snopek.
+    Make the system host name be the default value of `dccifd -D`
+	so that local user name for per-user logs and whiteclnt files
+	is "user" given SMTP recipient address "user@host.example.com"
+	on the system named user.host.example.
+    Support wildcards so that `dccifd '-D*example.com'` will take
+	"user" as the local name for per-user logs and whiteclnt files
+	vien SMTP recipient address user@host.exaple.com.
+    Fix bug in version 1.3.56 of dbclean in computing the hash table size
+	when upgrading from 1.3.42 reported by Domenico Diacono.
+
+1,3,56
+    Dccd continues parsing /var/dcc/blacklist after a bad line instead
+	of stopping.
+    Change dcc-stats-graph to not use --alt-y-mrtg with rrdtool version 1.2.
+    Do not save dccd client list when running with -Gon.
+    Dccd continues to inflate queue wait for 5 minutes after flooding
+	resumes so that the database will be good for clients.
+    Add -K to updatedcc for "cron mode" to not install code, restart daemons,
+	or otherwise disturb things by installing the same version.
+    Improve server queue delay measurement when the system is too slow
+	to keep up with incoming floods.
+    Do not use MAXHOSTNAMELEN for domain name lengths because on Linux
+	it is only 64 bytes.  This implies a new version of the /var/dcc/map
+	file.  Old versions of the file are automatically upgraded, but that
+	implies problems if you install old versions of the DCC client
+	programs.
+    Dccm, dccifd, and dccproc delete all old X-DCC headers instead of only
+	those with the same brand name as the current DCC server to fix
+	problem reported by Frank Tegtmeyer.
+    Fix unrecognized data /var/dcc/dccd_clients message.
+    Improve DCC server hash table size estimation to help servers with
+	1 GByte or less or more than 3 GByte of RAM.
+    Fix "flooding not stopped before ADMN DB UNLOCK" problem reported by
+	Tomasz Potega.
+    dcc-stats-graph no longer labels "Spam Ratio" graphs with '%' because
+	recent versions of rrdtool graph no longer understand "--units %%"
+
+1.3.55
+    Dccd falls back on foreground DNS resolution of flooding peer names
+	when fork() fails, perhaps because of a lack of swap space.
+    Fix structure alignment bug introduced in 1.3.51/2.3.51 and seen in
+	Solaris on SPARC CPUs using `gcc -O` reported by Stephan Schulz.
+
+1.3.54
+    Fix bug in proof-of-concept CGI script "LogOut/In" button introduced in
+	1.3.48 on Apache without mod_unique_id.
+    Fix new bug in proof-of-concept list-log CGI script reported by
+	Krzysztof Snopek.  It seems that in Solaris`ls -f` does not work on
+	a list of files.
+    Make `dccd -F` the default on Solaris to speed up the DCC server there.
+
+1.3.53
+    More speed for dbclean on FreeBSD.
+    Fix recent damage to `dccproc -a` and dccproc -f`.
+    Fix -B "name too long" problem reported by Daniel Gehriger and
+	Giulio Cervera.
+    Fix bogus mail rejection by dccifd in proxy mode reported by
+	Daniel Gehriger.
+    Reduce BIND timeout for each lookup to whatever remains of the
+	-Bset:url-secs=X limit.
+
+1.3.52
+    Fix bug in updatedcc found and diagnosed by Asgeir.
+    Speed up dbclean on FreeBSD.
+
+1.3.51
+    Fix recently added bug with greylisting in dccifd reported by
+	Daniel Gehriger.
+    Fix database corruption bug in `dccd -F` added in version 1.3.49.
+    Fix bugs in queue delay reported by DCC servers to anonymous clients.
+	This change is important for the public DCC servers.
+    What the nightly cron job, /var/dcc/libexec/cron-dccd, to try to
+	restart dccd if it is not running but is turned on.
+    Include anonymous client queue delay in `cdcc stats` queue delay report.
+    Close hole that allowed deleting or adding hosts in /var/dcc/maps.
+    Reduce minimum default reduced dbclean expiriation durations to
+	1 hour and 1 day from 2 hours and 2 days to help systems with
+	1 GByte or less RAM.
+    Change cgi-bin/webuser-notify to use sendmail and include a
+	"Precedence: bulk" header so that the vacation program won't
+	respond to the CGI cron script's mail messages.
+    Change cgi-bin/webuser-notify to handle per-user log subdirectories
+	generated by "option log-subdirectory-*" in whiteclnt files.
+
+1.3.50
+    Fix client random selection of public DCC servers.
+    /var/dcc/blacklist also affects flooding peers.
+
+1.3.49
+    Change dbclean to use a dccd optimization and be faster on FreeBSD
+	systems that have less that 4 GByte of RAM.
+    Set the GID of dccifd, dccm, and dccproc log files and subdirectory
+	to be the same as the parent directory if running as root and
+	if necessary.
+    Possibly fix pthread_mutex_lock(user_log) bug on MaxOS X reported by
+	Steve Martin.
+
+1.3.48
+    Turn off automatic generation of 64-bit DCC servers.
+
+1.3.47
+    Remove support for external filters as part of the fix for the
+	thundering herd problem in `dccm -B` reported by Gary Mills.
+    Check NS IP addresses in DNS blacklists (DNSBLs) before MX IP addresses
+	for dccproc, dccifd, and dccm -B.
+    Fix `dccm -tsubstitute...` problem reported by Ludger Bolmerg-Berliner
+    Try to compile for 64-bit pointers on Solaris to use more than 2 GBytes
+	if available.
+    Significantly improve speed on large FreeBSD DCC servers.
+    Make dbclean automatic -e/-E adjustments much more stable to
+	significantly help DCC servers on Mondays and Tuesdays.
+    Fix bad Body and missing Fuz1 and Fuz2 checksums for dccifd and dccm
+	when previous messages had bad MIME encapsulation reported by
+	Gary Mills and Harel Tassa.
+
+1.3.46
+    Do not greylist mail from SMTP submission clients marked by
+	"submit IP" lines in /var/dcc/whiteclnt.
+    Stop race with idle DNSBL helper processes.
+    More adjustments to help deal with large databases.
+    Deal with ./configure problem with BIND resolver on some versions
+	of Linux reported by Daniel Gehriger.
+    Change header checksums to ignore all instead of only some occurrences
+	characters matching [<>'"]
+    Dccm uses SMFIP_RCPT_REJ in sendmail 8.14 to detect dictionary attacks
+	and adjust the DCC Reputation of attackers.
+    Fix a very rare infinite loop in the MIME decoding code in DCC clients.
+    Add "option log-subdirectory-{day,hour,minute}" to whiteclnt files
+	to create per-user log files in subdirectories like the subdirectories
+	used for /var/dcc/log with dccm, dccifd, and dccproc -l.
+    Detect too-small file size resource limits in dccd and dbclean.
+    Compile with -D_FILE_OFFSET_BITS=64 on Linux as suggested by
+	Dmitry Konovalov.
+    Compile with -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 as advised by
+	James Carlson.
+    Add dccm and dccifd -Bset:maxjobs=X
+    Suppress ENOENT errors from recvmsg() on Tru64-UNIX observed by
+	Alberto D'Ambrosio.
+    Fix printf(null) crash in dccifd acting as a proxy for CommuniGate
+	reported by Charles Chappell.
+
+1.3.45
+    Fix memory leak in dccm and dccifd when DNSBLs (-B) are used.
+    Improve performance on current UNIX-like systems that have madvise()
+	with large DCC server databases.
+    Mention the "incompatible whitelists" message in the FAQ.
+
+1.3.44
+    Remove some quoting in homedir/Makefile to try to work around Linux
+	"improvements."
+
+1.3.43
+    Make "option forced-discard-nok" in /var/dcc/whiteclnt the default for
+	dccm as well as dccifd in proxy mode.
+    Add whiteclnt type of IP address entry "submit".
+	It is intended to be applied to the IP addresses of SMTP submission
+	clients such as web browsers that cannot tolerate 4yz temporary
+	rejections of mail, but that cannot be trusted to not send spam.
+    Let body and reputation checksum thresholds be set in the global and
+	per-user whiteclnt files.  "Never" is a valid threshold and turns
+	off rejections for a checksum.
+    Overhaul proof of concept per-user whiteclnt CGI scripts to handle
+	per-user checksum thresholds.
+    Rationalize /var/dcc/libexec/dcc-stats-graph -tTITLE
+    Fix bugs with -eNEVER and -Enever in dbclean.
+    Add "submit" to "mx" and "mxdcc" values for IP addresses whiteclnt files
+	to mark SMTP clients that are submitting new messages and that
+	do not understand 4yz rejections for individual recipients.
+    Fix dccifd crash on bogus long recipient names diagnosed by Tomasz Potega.
+    /var/dcc/libexec/fetchblack is not as noisy when the sources of the
+	public DCC server blacklist are down for long periods.
+    Limit the database window to less than 2 GBytes on all 32-bit systems
+	including Solaris.  Previous versions assumed that Solaris would
+	do the right thing if it allowed large files.
+    Fix bugs in the "skipping asking DCC server" mechanism that made it
+	too forgiving.
+    Remove the `dccd -t` thresholds in favor of simple constants.
+    Dccm, dccifd, and dccproc now emit X-DCC headers for locally white-
+	and blacklisted messages even when no DCC server responds.
+    Automatically compensate for incompatibility in newer versions of
+	rrdtool.
+    Prefer poll() to select() on Linux.
+    Increase the computed limit on `dccm -j` by not dedicating two FDs
+	to each thread for per-user log files but instead doing some locking.
+
+1.3.42
+    Correct wrong count of `cdcc "clients -i 10.11.12.13"`
+    Change rcDCC.in back to allow the use of /var/run for PID files.
+
+1.3.41
+    Finally fix ancient missing quote in start-dccm.
+
+1.3.40
+    Make bad password-IDs in /var/dcc/flod a serious error that is reported
+	even when tracing is off.
+    Fix missing ';;' in libexec/logger as suggested by James Carlson.
+    Restore `start-dccm -c` that was removed from version 1.3.39 with the
+	mistaken idea that -c was not in use.
+    Fix bug counting clients of public DCC servers introduced in 1.3.39.
+    Adding -d to DCCD_ARGS or GREY_DCCD_ARGS causes dccd to say how it
+	determines the size of available memory.
+    Deal with sysctl(HW_PHYSMEM) on amd64 FreeBSD 6.* need for an 8-byte value
+	without breaking sysctl(HW_PHYSMEM) on FreeBSD 5.* that demands a
+	4-byte value.
+    Allow databases larger than 3 GBytes on 64-bit systems.
+
+1.3.39
+    Fix bug that caused `cdcc "clients -V"` to sometimes report version
+	numbers of '?'.
+    Change dccd to prefer recycling an old, almost idle client rate-limiting
+	block instead of the oldest block.
+    Modify /var/dcc/libexec/list-clients based on a suggestion from Chris Myers.
+
+1.3.38
+    Add `cdcc "clients x.y.z.w/p"`
+
+1.3.37
+    Fix problems in /var/dcc/libexec/list-clients and with `cdcc clients`
+	with IPv6 addresses reported by Vincent Schonau.
+
+1.3.36
+    Fix bad ./configure check to see if `xargs` needs and can use -r
+	reported by Mark Thomas.
+
+1.3.35
+    Fix dccm crash reported by John Doherty.
+
+1.3.34
+    Fix bug in `make install` on Solaris introduced in 1.3.33.
+
+1.3.33
+    Work around change to `sort` collating sequence in Fedore Cort 5 reported
+	by Jakob Hirsch.
+
+1.3.32
+    Fix dccd crash as suggested by Wolfgang Breyha.
+    Do not try to stat() missing whiteclnt files more often than once every
+	5 seconds.  This should significantly reduce the number of stat()
+	system calls on busy systems using dccifd and SpamAssassin.
+    Fix some problems with determining the mail sender through MX forwarders
+	for second and subsequent mail messages in an SMTP session.
+    Recognize some more qmail variations of Received headers for obtaining
+	IP addresses.
+    Add `cdcc "clients -V"`.
+    Optionally in dccm and by default in dccifd in proxy mode temporarily
+	reject SMTP recipients that might be forced to have spam discarded
+	instead of rejected because it must be delivered to other
+	recipients.
+
+1.3.31
+    Mention /var/dcc/libexec/uninstalldcc in the installation instructions.
+    Change dccm and dccifd per-user log message for mail that is now being
+	accepted after being temporarily rejected for some other recipient
+	to "accept after greylist embargo" from "accept", as requested by
+	Spike Ilacqua.
+    Fix failure to reset "continue not asking" counter problem reported
+	by Breno Moiana.
+    Reduce default value of `dbclean -e` from 2 days to 1.
+    Modify error messages to try to find some clues about the Solaris
+	"deadlock avoided" problem.
+
+1.3.30
+    Fix leak in dccd blacklist.
+    Change client-server protocol so that `cdcc clients` gets more than
+	16 bits of NOP counts.
+    updatedcc and fetchblack try two FTP and HTTP servers.
+    do not use stdio to parse whiteclnt files to deal with Solaris'
+	255 limit on stdio file descriptors.
+    add /var/dcc/libexec/uninstalldcc
+
+1.3.29
+    Fix dblist.c compiling problem in 1.3.28 on some versions of
+	Linux reported by Thomas Schwanhaeuser and Nigel Horne.
+
+1.3.28
+    Turn off use of futimes() on Linux to resolve bug diagnosed by
+	Wolfgang Breyha.
+    Fix two locking problems dccm and dccifd that might cause the crashes
+	reported by Gary Mills.
+    Reduce dccifd and dccm thread stack size.
+    Fix bug that kept some DCC Reputations from being compressed in
+	the database.
+    Change the default DCC Reputation rejection message to the equivalent of
+	-r '%s bad reputation; see http://commercial-dcc.rhyolite.com/cgi-bin/reps.cgi?tgt=%s'
+
+1.3.27
+    Fix an odd case where flooding connections between DCC servers were
+	not being shut down.  I think it only happened when a firewall
+	or something else systematically filtered TCP FINs.
+    Use setresgid() for setegid() on HP-UX to fix problem reported by
+	Giacomo Fazio.
+
+1.3.26
+    Compression reputation reports from the same week instead of from
+	the same half day.  This significantly reduces the size of the
+	database on systems using DCC Reputations.
+    Restore the flood rate limiting based on TCP windows, but now just
+	ignore the bogus EAGAIN complaints from some versions of Solaris.
+    Fix server whitelists broken in 1.3.21.
+
+1.3.25
+    Fix dccproc crash when given a bogus env_From value diagnosed by
+	Jeff Mincy.
+    Fix greylist triple checksum in dccm and dccifd log files.
+    Fix the default expiration of DCC Reputation checksums at 30 days and
+	2 days unaffected by the system's amount of RAM.
+    Expire FUZ1 checksums in reports that also have FUZ2 checksums.  This
+	significantly reduces the size of the DCC server database.
+    Remove new flood output rate limit using SO_SNDBUF because it causes some
+	versions of Solaris to generate persistent EAGAIN errors for send().
+    Fix at least some of the dccifd memory leak reported by Chris Mikkelson.
+	It may be the same as the leak reported by Gunther Richter.
+    Limit worst case DCC client delays exclusive of delays caused by waiting
+	for DNS blacklists or external filters are limited to about 16 seconds.
+
+1.3.24
+    Dccproc starts dccifd after 500 uses at least as fast as 0.1/second.
+	With luck SpamAssassin will notice and switch to dccifd.
+    Look for libsmutil.a in /usr/lib on Linx for old RedHat
+	as suggested by Jason Balicki.
+    Fix X-DCC header misplaced by dccproc reported by James McNutt.
+
+1.3.23
+    Fix bug in dccm, dccproc, and dccifd that tripled effective DCC target
+	counts on messages being retransmitted after greylisting.  I
+	introduced this bug in version 1.3.21, which was released 3 days ago.
+
+1.3.22
+    Fix "POSSIBLE ATTACK" complaint from sendmail about trailing '\n'
+	on headers added by dccm reported by Paul Ganci.
+
+1.3.21
+    Add a crude cache or database to the proof-of-concept scripts in
+	cgi-bin/common to speed them up on large log directories.
+	The goal is to handle log directories with 40,000 files within a
+	second or two.  The caches files are built incrementally.
+    Invert the sort order of list-log in cgi scripts.
+	This change to cgi-bin/common should be upward compatible because
+	it is controlled by additional parameters to the Perl functions.
+    Complain about contradictory or ignored entries in whitelists, including
+	"from" checksums that are normally ignored in server whitelists.
+    Fix dccd craziness with gre (or any) interfaces with the same IP addresses
+	as other interfaces observed Craig Green.
+    Fix infinite loop in dccproc triggered by a To: header of more than
+	20480 bytes and with a '\n' character in byte #20478 observed by
+	James McNutt.
+    Ignore a few failures by select() apparently caused by SOCKS libraries.
+    Increase default DNSBL timeouts to -Bset:msg-secs=40 and -BURL-secs=11
+
+1.3.20
+    Fix dbclean expiration of reputation checksums
+
+1.3.19
+    Changed the midnight dccm and dccifd system log message to disclose
+	spam passed from whiteclnt-listed MX servers.
+    MX servers should now be listed in /var/dcc/whiteclnt with lines like:
+		mx	ip	10.2.3.4
+		mx	ip	10.5.6.0/28
+		mxdcc	ip	10.7.8.9
+	"MX" marks the IP address of one of your mail systems that should
+	be ignored in initial Received: headers and when reported by
+	sendmail to dccm.  "MXDCC" marks IP addresss of your mail systems
+	that run DCC clients and that will have already reported mail
+	to the DCC.  Continue using "OK" whitelist entries for mail systems
+	that you trust to never send or forward unsolicited bulk email.
+    Allow custom DNS blacklist SMTP rejection messages.  See
+	-Bset:rej-msg=X in the dccifd and dccm man pages.
+
+1.3.18
+    Fix `dccm -aIGNORE` crash reported by Juergen Georgi.
+    Fix detection of duplicates reputation reports.
+    Initial support for "mx ip" entries in whiteclnt files.
+    Fix compression by dccd of delayed reputation reports.
+
+1.3.17
+    People have broken wget by changing the old "--non-verbose" to
+	"--no-verbose". That would have ok, except they do not understand
+	what they were doing enough to support the old form.  Reportedly
+	"-nv" works on both versions.
+    `cdcc stats` on DCC Reputation servers includes an additional line
+	about reputation results.
+    Adjust logged reason when spam is ignored after greylist as suggested
+	by Spike Ilacqua.
+    Replace `dccproc -R` with `dccproc -r N`  (Of course, `dccproc -R`
+	is still recognized.)
+
+1.3.16
+    Fix bug introduced in 1.3.15 that caused greylist retransmissions
+	to be counted by the DCC server as separate messages contributing
+	to the "bulkness" of a message.
+    If /var/dcc/log is neither readable nor searchable by 'other', then
+	create log files with the same 'group' permissions as the directory
+	instead of the old default of 600.  This allows the application of
+	cgi scripts to the main whiteclnt and log files.
+    `dccd -T ALL` no longer works.  It never made much sense and continues
+	to cause confusion.
+    Dccifd in proxy mode no longer requires blanks after colons in SMTP
+	commands.  Problem reported by Martin Pala.
+    Fix dccifd proxy mode crash on mail messages without bodies reported
+	by Martin Pala.
+    Complain about bad host name and other errors in whiteclnt files
+	every 30 minutes instead of every 5 minutes.
+    Allow whiteclnt files to be read-only with a new locking scheme.
+    Use Milter.macros.eom in sendmail 8.13 so that the ${dcc_isspam}, and
+	${dcc_notspam}, ${dcc_userdir} macros can be set by sendmail.cf
+	rules that examin headers.  This might also allow some hack_mc
+	settings to be used with delay_checks.
+    As suggested with discussions with Martin Pala, the thresholds for
+	quick flooding among servers are now non-linear.
+
+1.3.15
+    When "option MTA-first" in a dccm and dccifd whiteclnt file, determinations
+	of (not) spam by the MTA are consulted first and so can be overidden
+	by the whiteclnt files.  This allows individual	users to override a
+	sendmail access.db file.
+    Correct the SMTP rejection message in per-user log files for dccm and
+	dccifd, especially when dccifd is acting as a proxy.
+    Fix bug reported by James Carlson that kept./configure from turning
+	on SOCKS.
+
+1.3.14
+    Keep dccd flooding threshold at or above 10.
+
+1.3.13
+    Make default maximum server memory size 2000 MByte on all except IRIX
+	and Solaris.
+    Fix bug in 1.3.12 that causes dccd flooding thresholds to be 0 if
+	dccd is started by hand or without a dccm rejection threshold.
+
+1.3.12
+    Fix packaging error in 1.3.11.
+
+1.3.11
+    Dccproc should complain if the -w whiteclnt file is bad.
+    misc/dcc.m4 can add a sendmail.cf rule that assigns a single
+	whiteclnt file and log directory to mail forwarded to another system.
+	With that the cgi-bin scripts can be used to control dccm for
+	mail forwarded to another system with a single web user.
+
+1.3.10
+    Delete "message" checksum in greylist database when spam causes
+	an embargo to be restored.  This fixes some counts after a
+	greylist whitelisting has been revoked.
+    Make the default owner and group for files be root:wheel on FreeBSD
+	As always, use the DCC_OWN and DCC_GRP environment variables with
+	`make install` or updatedcc -e to override those defaults.
+
+1.3.9
+    Dccifd and dccproc treat the bogus SMTP client IP address of 0.0.0.0
+	from SpamAssassin as if it were absent, which allows it to look in
+	in Received headers.
+    Add "rcvd-nxt" option to the dccifd MTA protocol to parse later
+	Received: headers.
+
+1.3.8
+    Repair greylisting broken in 1.3.7.
+
+1.3.7
+    Log files now say "Restore #x" instead of "Embargo #x" when a greylist
+	embargo is restored because the message is spam.
+    Don't use -a with `fetch` in updatedcc, fetchblack, and 
+	fetch testmsg-whitelist because it does not know when to stop trying
+	to reach a dead FTP server.
+    Add "any" to dccm, dccifd, and dccproc -B DNSBL result address as
+	suggested by Giulio Cervera.
+
+1.3.6
+    Try to deal with UNIX kernel problems cause bogus failures of mmap().
+    Fix bug related to --disable-dccifd reported by Krzysztof Snopek.
+    Stop relying on `su - -c` to start daemons as requested by Dean Hollister.
+    Fix server-ID mapping in /var/dcc/flod to use the first relevant mapping
+	as the documentation says instead of the last one.
+    Fix recent bug in cron-dccd that stopped emptying client per-user log
+	directories.
+
+1.3.5
+    Untangle `dccd -a` as well as local host name and IPv4 vs. IPv6 options
+	in /var/dcc/flod
+    Fix automatic /var/tmp/map file upgrading for Windows.
+	Actually c:\\program files\dcc\map
+    Improve dccd work-around for missing /var/dcc/libexec/cron-dccd cron job
+	on memory-short DCC servers.  This still not a substitute for the
+	cron job.  *USE THE CRON JOB*.
+    Repair dccifd and dccm midnight log entry bug reported
+	by Christopher Bodenstein.
+    Fix recent bug in cron-dccd that stopped emptying /var/dcc/log.
+
+1.3.4
+    Fix another dccm, dccifd, and dccproc bug related to bogus long
+	URLs from a core dump on Spike Ilacqua's system.
+    Fix installroot homedir/Makefile as suggested by Pavel Urban and Paul Ganci.
+    Add `cdcc "src x.y.z.w" to help DCC clients navigate firewalls.
+	This change requires a change in the format of the /var/dcc/map file.
+	However, the DCC clients should all automatically and invisibly
+	upgrade old files.
+    Fix `dccd -a` bug reported by John Levine.`
+
+1.3.3
+    Work around bogus use of "::" in line 184 of /usr/share/mk/bsd.lib.mk"
+	in ancient FreeBSD as reported by Lento Yip.
+
+1.3.2
+    Improve logging of failures by external filters.
+    Fix new use of pstat_getstatic() on HP-UX to obtain physical memory size.
+
+1.3.1
+    Allow dccproc, dccm, and dccifd to use an external filter.  See the
+	./configure --with-xfltr=FILE --with-xfltr-cflags=opt and
+	--with-xfltr-ldflags=opt, the discussion of -X in the man pages,
+	and the toy filter in thrlib/xfltr_sample.c.
+	See also /var/dcc/dcc_conf-new
+    Automatically decrease the number of DNS blacklist helper processes for
+	dccm and dccifd when fewer are needed.
+    Use pstat_getstatic() on HP-UX to determine the size of physical memory.
+    Dccifd refuses to run on HP-UX 11.00 to keep SpamAssassin from stalling
+	while dccifd cannot hear.
+
+1.3.0
+    New license.
+    cron-dccd tells dbclean -6 if needed.
+    Treat EHOSTUNREACH or "No route to host" like ECONNREFUSED in DCC
+	clients and do not complain about every instance.
+    Deal better with DCC servers with floppy multi-homing such as behind
+	some NAT boxes.
+    Deal more gracefully with Linux systems without IPv6 support but
+	with `cdcc "IPv6 on"`.
+    Fix crash in dccm, dccifd, and dccproc while computing FUZ2 checksum on
+	URLs with very long host names in HTML mail messages.
+    Fix dccifd missing SMTP reply code in dccifd rejections in proxy mode.
+    `dccifd -ddd` logs SMTP transactions in proxy mode.
+    Dccifd man page has example -o configuration for use as a Postfix
+	before-queue filter.
+    Restart dccm and dccifd after core dumps.
+
+1.2.74
+    Make clients prefer real IPv6 address to embedded or mapped IPv4
+	addresses.
+
+1.2.73
+    Fix crash in dccsight as suggested by Vincent Schonau.
+    Fix handling of log files for senders without reverse DNS in
+	cgi-bin/common as suggested by Vincent Schonau.
+    Add IPv4 and IPv6 o-opts to /var/dcc/flod lines to deal with the change
+	in the default from "YES" to "NO" in the FreeBSD ipv6_ipv4mapping
+
+1.2.72
+    Do not make DNS blacklist checks if the MTA already knows the message
+	is or is not spam.
+    Add -lresolv to $LIBS for Mac OS X Darwin as suggested by Joel Brogniart.
+    Change DNS blacklist checking to check MX servers as well.
+    Add -B settings to control which DNS blacklists are used for the envelope
+	or body, and whether MX servers should be checked.
+    SIGUSR1 causes dccm and dccifd to send their statistics to the system log.
+    Use getaddrinfo() and getnameinfo() when getipnodebyname() and
+	getipnodebyaddr() are not available.  This might make IPv6 work
+	on Linux systems.
+    Note that to make dccd listen to IPv6, -6 must be added to DCCD_ARGS
+	and GREY_DCCD_ARGS in /var/dcc/dcc_conf.  This requirement has long
+	been present to defend against systems that only pretend to
+	understand IPv6.  Perhaps it is time to remove it.
+    Add `dccifd -o` to use a subset of ESMTP so that dccifd can be used
+	as a "before-queue" filter or SMTP proxy by Postifx.
+    Probably fix a rare dccm and dccifd crash.
+
+1.2.71
+    Fix stray greylist queries reported by Vincent Schonau.
+    Fix conflict between checking DCC and greylist servers on Linux
+	reported by Vincent Schonau.
+
+1.2.70
+    Turn on IP TOS bits for DCC server-to-server flooding.
+    Add -B to dccproc, dccm, and dccifd to consult DNS blacklists.
+	This feature and greylisting are valuable supplements
+	to DCC target counting.  However, greylisting is generally
+	significantly better where greylisting can be used.  Most
+	dccproc and many dccifd installations cannot use greylisting.
+
+1.2.69
+    Add `misc/hackmc -r` like -R but to reject instead of discard bad
+	relay attempts.
+    Fix bogus X-DCC header added after a fatal problem with the DCC server
+	as suggested by Harald Daeubler.
+    Release a single tarball equivalent to the old dcc-dccd-*.tar.Z that
+	contained the DCC server and all clients.
+
+1.2.68
+    Fix counting of blacklisted DCC clients by dccd.
+
+1.2.67
+    Remove bonehead optimization in dccd of /var/dcc/blacklist in 1.2.66.
+    Deal with stdargs in gcc 3.4 on AMD 64-bit systems that do not allow
+	a va_list arg to be passed to two different subfunctions, but
+	without breaking things on the many systems that lack va_copy().
+
+1.2.66
+    Overhaul dccd rate-limiting.  Instead of imposing a penalty time
+	on overactive clients, simply rate-limit them.
+    Reduce limit on dccd error messages about clients from 2/second to
+	0.1/second.
+
+1.2.65
+    Fix accounting of NOPs from blacklisted clients.
+    Fix race that caused "??" server-IDs in X-DCC headers.
+    Do not count requests from blacklisted clients against the rate limits
+	as suggested by Sven Willenberger.
+
+1.2.64
+    Correct ">XXXX clients" from `cdcc stats`
+    Removed redundant declarations of mapfile_nm and rl_anon as
+	suggested by Andreas Jochens.
+
+1.2.63
+    Possibly fix dccm crash reported by Ludger Bolmerg.
+
+1.2.62
+    Fix bug in `dccd -a10.2.3.4` diagnosed by John Levine.
+
+1.2.61
+    Fix file descriptor leak in getifaddrs() replacement.
+
+1.2.60
+    Fix bug reported by John Levine on systems such as BSD/OS 4.3
+	without getifaddrs() introduced in 1.2.59 with tracking changes
+	in network interfaces.
+
+1.2.59
+    Fix broken `dccd -G0` reported and diagnosed by Chris Mikkelson.
+    Track changes in network interfaces on most modern flavors of UNIX
+	as suggested by James Carlson.
+    Fix two MIME decoding bugs as suggested by George Schlossnagle.
+    Treat '>' as a blank instead of punctuation for FUZ2 checksums.
+    Don't re-use va_list in stdargs functions to work around a characteristic
+	of gcc for AMD 64 bit systems.  gcc 64-bit stdargs
+	reportedly passes va_list by reference instead of by value.
+    Fix date labels in graphs generated by dcc-stats-graph as suggested by
+	Kevin Gagel.
+
+1.2.58
+    Work around new bug in FreeBSD 4.10 mechanism to disconnect UDP
+    sockets reported by Daniel V Klein.  The symptom of the bug is
+    that DCC servers appear down to clients running on FreeBSD 4.10
+
+1.2.57
+    Invoke WSACleanup() after using WSAStartup() on Windows systems
+    as suggested by Carl Stehle.
+
+1.2.56
+    Fix /var/dcc/ids delay= extension.
+
+1.2.55
+    Add `./configure --with-max-db-mem=X` to limit the size of the
+	database window.
+    Extend /var/dcc/ids format to allow authenticated clients to be delayed
+	as `dccd -U` delays anonymous clients.
+    Add `./configure --with-kludge=FILE`.
+
+1.2.54
+    Fix problem with flooding among greylist servers using `dccd -Gweak-IP`
+	reported by Valentin Chopov.
+
+1.2.53
+    Restore `dccsight -G grey-cksum` because the proof-of-concept CGI
+	scripts use it.
+    On OS X, use owner and group of daemon:daemon for programs and use
+	dccmaninstall in `make install` as suggested by Jason Schwarz.
+
+1.2.52
+    Fix problem in start-dccm and start-dccifd with Solaris /bin/sh
+	reported by Gary Mills
+    Work around bug in OpenBSD HTONL() and NTOHL() reported by Jeff Drinkert.
+    Change wlist to rebuild the .dccw hash table unless given -Q.
+
+1.2.51
+    Fix cause of "packet length 44 too small" complaints by DCC servers.
+	With an empty mail body and no useful headers, DCC clients were
+	sending empty requests to DCC servers.
+    Add `cdcc "debug TTL=x"` to help find firewalls that filter DCC requests.
+    Use shared libmilter.so in dccm as suggested by James Carlson.
+    Fix Body checksum when MIME boundary crosses buffer boundary from
+	Richard Lyons.
+    Stop crash in dccm reported by Krzysztof Snopek.
+    Deprecate misc/dccdnsbl.m4 and change misc/hackmc to work with
+	FEATURE(dnsbl) and FEATURE(endnsbl) in modern sendmail.
+    Make it compile on Mac OS X and DragonFly FreeBSD.
+    Reduce the size of greylisting databases.
+    Separate DCC query mode for dccm and dccifd from greylist query mode.
+    Add `dccd -G weak-IP` to whitelist not only a {sender,target,IP address}
+	after passing the greylist embargo, but anything from the IP address.
+	Use this facility with caution; it might be a bad idea.
+
+    The last change requires that all greylist clients and servers
+	be upgraded simultaneously.
+
+1.2.50
+    Fix `dccifd lhost,lport,rhost/bits` on systems that have IPv6.
+    Change homedir/make-dcc_conf to track changes in
+	`./configure --with-rundir=x --libexecdir=y`
+	as suggested by Josef T. Burger.  This change will not be effective
+	until upgrading from 1.2.50 to later versions.
+    Deal with tiny FD_SETSIZE reported by Christian Becker.
+    Fix dccifd, dccm, and dccproc core-dump caused by missing whiteclnt file
+	reported by Henrik Edlund.
+
+1.2.49
+    Fix infinite loop in computing DCC clients computing checksums of
+	large, deeply nested MIME messages reported by Clive Cleland.
+
+1.2.48
+    Add "option dcc-off" and "option dcc-on" to per-user whitelist files
+	as suggested by Spike Ilacqua.
+    Make /var/dcc/libexec/fetch-testmsg-whitelist deal with cron processes
+	that set $PATH without /usr/local/bin
+
+1.2.46
+    Fix infinite packet flood from DCC clients including dccproc observed
+	by Benji Spencer, Clive Cleland, and Andrew Kent.  I introduced
+	this serious bug with the WIN32 changes in 1.2.33.
+    Fix "option greylist-off" bug introduced in 1.2.39 and reported by
+	Spike Ilacqua.
+    Defend dccd against too many clients.
+
+1.2.45
+    Fix dccd database "window" size computation bug that chose 3 GBytes
+	on systems with less than 512 MByte.
+    Fix `cdcc "stats all"` to use the right host name from Leandro Santi.
+    Increase `dccd -R` default rate limits for all anonymous clients
+	and for individual authenticated clients..
+    updatedcc understands -V x.y.z unless no old version x.y.z is available.
+
+1.2.44
+    Fix bug in dbclean -e and -E default reductions that made them 50% less
+	instead of more conservative compared to 1.2.39.
+    Speed up dbclean on systems with mmap(MAP_ANON).
+    Clean some uninitialize variable complaints from purify reported by
+	Praveen Nimmagadda.
+    Minor adjustments to deal with Solaris's VM system.    
+
+1.2.43
+    Fix typo in -e and -E default reductions.
+
+1.2.42
+    More adjustments to deal with Solaris's VM system.
+    Use all except 384 MByte of physical memory on systems with more than
+	768 MByte.  On systems with 768 MByte or less, use half.
+    Tweak WIN32 makefiles.
+
+1.2.41
+    Restore TZ in update/misc after setting it to GMT to keep the
+	Solaris FTP from going crazy.
+
+1.2.40
+    Include win32.makin2 that was missing from 1.2.39.
+    Do not respond to clients when the database is broken to ensure that
+	they switch to another server.
+    Add missing "option greylist-on" support to cgi-bin/edit-whiteclnt
+    Remove DCC_PROTO_HOMEDIR support for building RPM or other packages
+	and add `./configure --installroot=DIR`
+    Adjust threshold for `dbclean -F` to keep Solaris systems from spending
+	hours in dbclean.
+    Add `dbclean -f` to turn off default `dbclean -F` on Solaris for
+	installations where the file system has been tuned for the
+	large, randomly accessed file that is a DCC database.
+
+1.2.39
+    Suppress syslog messages from cdcc noted by Krzysztof Snopek.
+    Suppress complaints about madvise(MADV_WILLNEED).
+    Tweak WIN32 porting aids.
+
+1.2.38
+    Turn off the use of madvise(MADV_WILLNEED) on systems such as BSD/OS
+	that claim to have it but don't.
+    -Gweak did not in 1.2.37 work as reported by Valentin Chopov.
+
+1.2.37
+    Tweak WIN32 #ifdefs.
+    Change -G for dccm and dccifd to require -Gon.   Add kludges in
+	start-dccm and start-dccifd to convert the old -G to -Gon.
+    Add "-G noIP" and '-G IPmask/xx' to ignore all or part of the SMTP
+	client IP address in the greylist triple.
+    `dblist -G` no longer works.  Use `dbclist -Gon`
+    Fix bug where dbclean increased the number of reasons to stop flooding
+	by 1 and then decreased it by 2 reported by Bernard Gardner.
+    Use madvise(WILLNEED) for database buffers on systems with plenty
+	of RAM as suggested by Robert Milkowski.
+    Adjust scripts including /var/dcc/libexec/start-dccd to deal with
+	POSIX compliance of `expr` in FreeBSD 5.1.
+    Change FUZ2 checksum to know about Polish.
+
+1.2.36
+    Fix updatedcc for systems that have only make in $PATH but where
+	it is really gmake.
+
+1.2.35
+    Turn off the use of poll() instead of select() on Linux systems.
+
+1.2.34
+    Fix bugs in the  ./configure mechanisms to use poll() instead of
+	select() on FreeBSD reported by Valentin Chopov.
+
+1.2.33
+    Adjust default dbclean expirations based on available RAM and
+	the size of the database.
+    Make dcclib, cdcc, and dccproc build for WIN32 with Borland C++ 5.02
+	or FreeCommandLineTools.exe.  The former can use the dcc.ide file
+	and the latter can use win32.mak.
+    Fix use of SO_LINGER on Solaris.
+    More changes to ease compiling cdcc for WIN32.
+    Possibly fix dccd amnesia about flooding peers.
+    Use poll() instead of select() on FreeBSD.
+    Fix updatedcc to use gmake if ./configure insisted.
+    Entirely remove "--prefix" from ./configure to stop people from
+	mistakenly assuming that ./configure is what the Free Software
+	Foundation dictates it should be.
+    Change /var/dcc/libexec/start-dccd to stop dbclean when dccd starts
+	and so prevent a deadlock between dbclean and dccd.
+    Fix problem with updatedcc on systems with only gmake reported by
+	James Carlson.
+    Fix problem with large greylist whitelist files reported by John Levine.
+    Reduce number of write() system calls per operation in dccifd.
+
+1.2.32
+    Fix corruption of `dccm -U` directory introduced in 1.2.31 and
+	reported by Spike Ilacqua.
+
+1.2.31
+    Make all dccd databases "big."  If you have not previously used
+	`./configure --enable-big-db` then when version 1.2.31 of
+	dccd is started, it will run dbclean to rebuild the database.
+	This will make the hash table about 9% bigger and so 9% slower.
+    Make the "ms" units optional for RTT adjustments in `cdcc add` or
+	`cdcc load` as suggested by Hernan A. Perez Masci.
+    Add ${dcc_userdir} sendmail.cf macro as suggested by Valentin Chopov.
+	This lets you use the full power of sendmail.cf rewrite rules
+	to control per-user whitelist and log directories.  See the
+	dccm man page.
+    Make `dbclean -F` the default on Solaris systems with plenty of RAM
+	and automatically turn it off when there is not enough RAM to hold
+	the entire database.
+
+1.2.30
+    Let `dccd -C` take arguments for dbclean, so that the following line
+	in dcc_conf works:
+		DCCD_ARGS="'-C$DCC_LIBEXEC/dbclean -F'"
+    Make pthread_detach() failures in dccifd non-fatal.
+    Add mechanism to cgi-bin/list-msg to whitelist-for-greylisting
+	(sender,IP-address,recipient) checksums.  This mechanism
+	requires that the 1.2.30 versions of dccd and dccsight be
+	installed.
+    Change dccifd to record the message headers in the log file even when
+	the MTA fails to provide the message body.
+
+1.2.29
+    FUZ2 checksums character entity references in URLs in HTML.
+    Insert checksums of greylist triples of whitelisted messages
+	into the greylist database.
+    If greylisting is turned on, then include greylist checksums in dccm and
+	dccifd log files even for whitelisted and blacklist messages.
+    Dccm and dccifd log some messages that are whitelisted for greylisting
+	or otherwise not currently embargoed but were in the past.
+    Change whiteclnt "log all-grey" and "log no-grey" options to
+	"option greylist-log-off" and "option greylist-log-on".
+	(Of course the old strings continue to work.)
+    Add whiteclnt option "option greylist-off" and "option greylist-on"
+	to control greylisting for greylistig for an individual user.
+	Note mail in SMTP transactions that involve other users for which
+	greylisting has not been turned off can still temporarily rejected.
+    Add "option log-all" log everything for an individual user.
+    The proof-of-concept CGI scripts handle the new whiteclnt options.
+    Fix false "many" from dccproc when switching DCC servers reported by
+	Rutger ter Borg.
+    Remove 1.2.28 change in the dccd "xx MByte window" message that
+	displayed the minimum of the physical file size and the mmap() window
+	size.  The message now contains the mmap() window size..
+    Fix `dccd -FG,` core dump reported by Aleksander Dzierzanowski.
+    Make "skip asking" in `cdcc info` output a comment as suggested by
+	Gunther Heintzen.
+    Speed up flooding among greylist servers.
+
+1.2.28
+    Reduce the number of msync() system calls, significantly speeding up
+	dbclean and dccd on FreeBSD.
+    Fix "window" size computation for dccd and dbclean on systems with
+	more than 4 GByte of RAM.
+    Adjust flooding threshold for greylist dccd.
+    Changed the dccifd man page to say that dccifd looks at the first
+	Received: header for the sender's IP address if was not provided
+	by the dccifd client.
+    Remove the recommendation for whitelisting the Habeas Mark from the
+	sample dcc_conf file.
+    Detect bogus -G args reported by Aleksander Dzierzanowski.
+
+1.2.27
+    Use fsync() and msync() on /var/dcc/flod.map in dccd to try to
+	get Linux to send the file to the disk.
+    Use setsockopt(SO_LINGER) to speed shutdown on FreeBSD.
+
+1.2.26
+    Fix `dccd -F`.  Adding -F to DBCLEAN_ARGS in /var/dcc/dcc_conf on
+	Solaris systems with enough RAM to hold most of the database
+	can make dbclean twice as fast and reduces its effects on other
+	processes.  Addcing -F to DCCD_ARGS might have similar effects.
+    The use of MAP_NOSYNC on systems that support it including FreeBSD
+	reduces the effects of dccd on other processes.
+
+1.2.25
+    Fiddle with /var/dcc/libexec/dcc-stats-graph.
+    Add `dccd -F` like `dbclean -F`
+
+1.2.24
+    Generate /var/dcc/dcc_conf-new whether it is needed or not.
+    Add `dbclean -F` to work around Solaris performance bugs with large
+	files and mmap().
+    Add `dccd -Gweak` to not require a retransmission of the same message
+	to end a greylist embargo.
+    Add option "no-reject" to the dccifd-MTA protocol to be similar to
+	`dccm -aIGNORE` for greylisting while ignoring DCC results.
+
+1.2.22
+    Fix stray hostnames reported in dccifd log file headers observed by
+	Chris Mikkelson.
+    Fix empty dccm syslog complaints seen on Solaris.
+    Fix core-dump with bogus hostname in whiteclnt file observed by
+	Joe Ilacqua.
+    Updatedcc starts to shut down dccd early to avoid problems restarting
+	it on slow systems at the end.
+
+1.2.21
+    Fix dbclean every 20 seconds possibly observed by Kelsey Cummings.
+    Stop frequent running of dbclean from dccd to clean up greylisting records.
+
+1.2.20
+    Fix cdcc core dump with non-existent /var/dcc/map files.
+    Change updatedcc to not use "set -e" because some versions of
+	bash have tantrums if you unset a variable that is not set.
+
+1.2.19
+    Fix "too many CIDR blocks" problem reported by John Doherty.
+
+1.2.18
+    Fix infinite loop in dccm reported by Gary Mills.  This error might
+	also have caused dccm to crash.  It should affect dccd as well
+	as the DCC client programs.  I introduced it in version 1.2.15.
+
+1.2.17
+    Fix IP whitelist entries in DCC server databases broken in 1.2.15 and
+	1.2.16.
+
+1.2.16
+    Fix bug in libexec/updatedcc with blanks in CFLAGS reported by
+	Aaron Paetznick.  This only fix uses of updatedcc after
+	1.2.16 has been installed.
+
+1.2.15
+    Ensure the mtime of /var/dcc/whiteclnt.dccw files changes to stop
+	endless reparsing on some Linux systems.
+    Resolve inconsistency in dccproc whitelist vs. -t many noted by
+	Dawn Endico by making whitelisting always produce an exit code of 0.
+    Put absolute path on cdcc in misc/stats-get as pointed out by
+	Kevin Gagel.
+    Make dccm and dccifd log file size configurable as suggested by
+	Furlan Campos.
+    Fix bug reported by Jim Carroll that kept mail from being rejected
+	when first sent to a spam.
+    Allow large CIDR blocks to be white- or blacklisted in whiteclnt files.
+    Fix dccifd -p bug reported by Christopher Bodenstein.
+    Fix "continue not asking greylist" log message that should be
+	"continue not asking DCC" reported by Jorg Bielak.
+    Fix ./configure script in dccproc tarball that was creating a bogus
+	RUNDIR value for dccifd.
+    Add `./configure --with-max-log-size=KB` as requested by Furlan Campos.
+    The proof of concept CGI scripts now
+	deal with per-user logs for user that receive enough spam that their
+	    log directories have 20,000 entries.
+	support the per-user greylist log options for whiteclnt files
+	    described in the dcc man page near the description of "include"
+	support locking of per-user whiteclnt files with a line of
+	    "#webuser locked"
+    Fix output file data corruption bug reported by Chris Mikkelson.
+    Change default dccm greylist SMTP status code as suggested by Gary Mills.
+
+1.2.14
+    Adjust homedir/make-dcc_conf for Solaris as suggested by Gary Mills.
+
+1.2.13
+    Tweak libexec/updatedcc for Solaris.
+
+1.2.12
+    Report checksums in greylist embargoed mail to a DCC server while
+	waiting for the embargo to expire.
+    Recognize more than 1 GByte of RAM on Solaris systems.
+    Fix bug in stats-get not counting queries pointed out by Yury Razbegin.
+    Change the default greylist -G "white" value from 30 to 63 days.
+    dccm and dccifd now include the greylist triple checksum in per-user
+	log files.
+    Fix bugs in dccifd C interface routine pointed out by Stephen Misel.
+    Fix dccd whitelist bug observed by Gary Mills.
+    Fix bug in 1.2.8 through 1.2.11 that stops flooding of brand new
+	reports of bulk mail that is not spam.
+    Automatically generate /var/dcc/dcc_conf-new from existing dcc_conf
+	to aid installation of greylisting.
+
+1.2.11
+    Resume looking for native sendmail milter libraries on more than
+	FreeBSD.
+
+1.2.10
+    Fix some compiler warnings on Solaris.
+    Fix "only 256 open files allowed" message from dccid on Solaris
+	reported by Turgut Kalfaoglu.
+    Use poll() instead of select() in dccifd if possible.
+    Fix error in libexec/start-dccd reported by Valentin Chopov.
+    Look for native sendmail milter and install man pages on
+	recent versions of NetBSD as suggested by Josef T. Burger.
+
+1.2.9
+    Turn off database hash debugging accidentally turned on in 1.2.8.
+
+1.2.8
+    Fix serious bug in resolving DCC server host names by dccm and dccifd.
+    Add "temporary" to default greylist rejection messages.
+    Fix greylisting of null messages.
+    Add misc/fetch-testmsg-whitelist
+    Improve compression/suppression of flooded checksums to reduce the
+	database size and bandwidth requirements 10-50% for DCC servers that
+	see fewer than 20K DCC ops/day.  Tests have produced conflicting
+	results.  The full effects are not seen unless flooding peers
+	install this version.
+
+1.2.7
+    Fix greylist flooding problem reported by Valentin Chopov.
+    Add whitelists to greylist servers as requested by Bobby Rose.
+    Change `dccm -r` to also set the greylist rejection message and to
+	optionally interpolate the queue-ID and SMTP client IP address
+	as requested by Gary Mills.
+    Fix 1.2.x bug that treated all flooded checksums to a new database
+	as stale until a local checksum has been added and dbclean run.
+
+1.2.6
+    Complete the fix for "badly signed NOP response" in the DCC server.
+	To be effective, the public DCC servers will need to use
+	version 1.2.6.
+
+1.2.5
+    Fix "badly signed NOP response" bug diagnosed by Philipp Buehler and
+	Thorsten Janssen.
+    Turn off connect() on DCC client UDP sockets for Linux 5.2.
+    Use sysconf() on Solaris and Linux and sysctl() on BSD systems to
+	estimate the size of physical memory.  This may reduce the need
+	for `./configure --with-db-memory=X`
+    Add `./configure --with-DCC-MD5` to use the MD5 code in the DCC source
+	instead of any local library.
+    Fix dblcean "repairing" the database because "was [it] not closed cleanly."
+
+1.2.4
+    Fix core dump in creating X-DCC header as suggested by James Carlson.
+    Fix dccif.c for `./configure --disable-IPv6` and stats-get for
+	deleting /dev/null when interrupted as suggested by Yury Razbegin.
+
+1.2.3
+    Fix leak in greylist server.
+    Improve some obscure error messages from dccd.
+    Address IPv6 problem on RedHat 5.2 reported by Darren Nickerson.
+    Check /proc/meminfo on Linux for hints on real memory size to
+	work around the Linux mmap() bugs and resulting dccd performance
+	problems as suggested by Dave Lugo.
+
+1.2.2
+    Avoid `chown` in start-dccm and start-dccd.
+    Fix stop-dccd as noted by Michael Ghens.
+    Add greylist installation instructions to INSTALL.html and INSTALL.txt.
+
+1.2.1
+    Fix typo in default map.txt noted by Michael Ghens.
+    Fix undetected hash table size overflow noted by Leandro Santi.
+    Fix "BRAND" error in start-dccd.
+    Fix inflation of target counts on greylisted messages.
+    Dccd is off in the default dcc_conf.
+    Reduce default greylist embargo to 4.5 minutes.
+
+1.2.0
+    Many changes to support a form of Greylisting.
+	See http://projects.puremagic.com/greylisting/
+	and the dccd and dccm man pages.  Greylisting is probably not
+	ready for prime time in 1.2.0.
+    Change flod.map file format to allow `cdcc "flood stats 123"` to say
+	"not connected since" as suggested by by James Carlson.
+    Fix bogus "overwriting existing entry" error message noted by
+	Dallas Engelken.
+    As suggested by Leandro Santi, prevent false alarms about bogus packets
+	received by DCC clients.
+    Improve 24-hour averaging of client operations by dccd.
+    Fixes to dccd/rl.c from Leandro Santi.
+
+1.1.45
+    Fix error in sample homedir/map.txt file.
+    Fix problem in starting flooding.
+    Fix error in misc/dcc-stats-init.
+
+1.1.44
+    Fix core dump observed by Stephen Misel.
+    Suppress error message from rcDCC and start-dccm when dccm is not
+	installed as noted by Kevin Gagel.
+
+1.1.43
+    Fix core dump reported by James Carlson.
+
+1.1.42
+    Fix dbclean progress reporting bug noted by Vladimir Samoilov.
+    Improve misc/na-spam to catch another kind of quote leader.
+    Drop anonymous requests that would be delayed by more than the maximum
+	possible RTT.
+    Add application layer keepalives to flooding.
+
+1.1.41
+    Sort IP addresses in `cdcc rtt`.
+    Improve response of the client code to broken servers.
+
+1.1.40
+    Adjust client failure "fail_more()" backoff mechanism.
+    Add commas to misc/dcc.m4 as suggesed by Spike Ilacqua.
+
+1.1.39
+    Fix missing changes to dcc.m4.
+
+1.1.38
+    Fix `make install` file ownership as noted by Gary Mills.
+
+1.1.37
+    Deal with certain obfuscating URLs.  This change includes some
+	URLs in Fuz2 checksums and removes parts of some URLs from Fuz1
+	checksums.  This should reduce much of the need for the dubious
+	    many hex FUZ2: 00000000 00000000 00000000 00000000
+	whiteclnt entry.
+    Decode RFC 822 and MIME entity headers to control quoted-printable
+	and base64 decoding instead of the previous adaptive algorithm.
+	This also involves decoding nested MIME multipart messages.
+	A side effect of this is to change the checksums computed for
+	some mail.
+    Use poll() on Solaris to avoid failures from select() on large FDs.
+    Smuggle the Mail_From value to dccm with a ${dcc_mail_host} macro
+	so the mail_host checksum is valid despite sendmail smart relays.
+	This requires rebuilding sendmail.cf with the new dcc.m4.
+    Move part of the server-failing timer into /var/dcc/map so that
+	dccproc processes can share it.
+    Fix dccifd bug in handling detecting the end of headers reported
+	by Tim Clymo.
+    Fix `dccd -u` which was almost entirely broken.  Extend `dccd -u`
+	to inflate the delay for busy anonymous clients.
+    Make server selection more stable despite network problems.
+    Fix some cases of false alarms of database corruption by dbclean.
+	This fix is important where dbclean complains about `repairing` the
+	database.
+    Clear dccd queue delay when the server is idle.  This should help
+	dccd on BSD/OS after dbclean runs.
+    Avoid `chown` and `chgrp` with `configure --disable-sys-inst`.
+    Add `dccproc -x exitcode` as suggested by Paul Wright.
+    `cdcc clients` displays counts of NOPs to catch misconfigured firewalls
+	at clients.
+
+1.1.36
+    Add optional DCCM_ENABLE and DCCD_ENABLE to /var/dcc/dcc_conf
+    Look for libmilter.a where it is in some versions of Linux.
+    Add "eval" to start-dccd, start-dccm, and start-dccifd when
+	not using a separate UID to allow quoted blanks in
+	`dccm -r "rejection messages"`.
+
+1.1.35
+    Deal with name space pollution in Solaris as suggested by Isaac Saldana.
+
+1.1.34
+    Fix libexec/dcc-stats-collect and libexec/stats-get as noted
+	by Valentin Chopov.
+    Adjust FUZ2 length thresholds to catch more HTML obfuscated spam.
+    Reduce some stalling of dccd on BSD/OS when dbclean starts.
+    Resolve conflict between start-dccifd and dccifd by making the default
+	location for the dccifd PID file the same as for the dccm file
+    Fix dccifd to remove stray X-DCC headers.
+    Fix start-dccifd to pay attention to DCCIFD_ARGS in dcc-conf.
+
+1.1.33
+    Fix rare core-dump in dccd that more frequently corrupts the database.
+    Do not loop forever as the result of some database corruption.
+    Turn off by default dccd blacklist event tracing.
+    Increase the limit on the size of white-listed CIDR blocks from /24 to
+	/20 or 1024 IP addresses.  Every IP address whether specified
+	separately or with a CIDR block requires a separate entry in a
+	client DCC whitelist hash table.  The hash table is limited to
+	about 80K entries.
+    Add '-T' to misc/hackmc to trust or white-list mail authenticated
+	by SMTP AUTH or START TLS.
+    Server blacklisting suppresses "bad client or server-ID" error messages.
+    add /var/dcc/libexec/stats-get produce server statistics as noted by
+	Daniel Klein.
+
+1.1.32
+    Do not count MIME content-type image bytes when deciding whether
+	to generate FUZ2 checksums.
+    Unlink dccm and dccifd PID files before trying to (re)create them.
+    Dccm watches milter "contexts" more closely for corruption.
+    Add an optional count to `cdcc clients`.
+    Dbclean tries harder to restore dccd flooding.
+    Initialize wtgts in dccproc as noted by Leandro Santi.
+
+1.1.31
+    Fix core dump in ckfuz1.c noted by Gary Mills.
+
+1.1.30
+    Allow blanks in MIME boundaries.
+    Possibly fix compiler "initialization type mismatch" warnings noted by
+	Gary Mills.
+
+1.1.29
+    Fix dccm core dump in ckfuz1.c noted by Sven Willenberger.
+
+1.1.28
+    Fix dccm core dump in dcc_ck_body0() noted by Valentin Chopov.
+
+1.1.27
+    Add to HTML character references known by the Fuz2 checksums.
+
+1.1.25
+    Notice "Content-Type: text/html" headers to pay attention to HTML
+	even in mail without <html> tags.
+    Tweak the Fuz2 checksum to ignore some Microsoft delivery notifications.
+    Adjust Fuz1 checksum to be more consistent on URLs.
+    Fix date bug reported by Krzysztof Snopek in `cdcc clients`.
+    Include an indication that the client was blacklisted in `cdcc clients`.
+    Change MIME decoding somewhat as suggested by Leandro Santi.
+    Fix bug in dccd client blacklist.
+    Add `dccm -g not-all`.
+
+1.1.24
+    Add `cdcc "clients -s"` to sort by the number of requests.
+    Add /var/dcc/blacklist of blocks of IP addresses refused by dccd.
+    Remove -lpthread from $(DPADD) in dccm and dccifd Makefiles for Solaris
+	with gmake to try to deal with problem observed by Krzysztof Snopek.
+
+1.1.23
+    Fix handling of &amp; in the middle of words in HTML.
+    Change dccifd to respond with DCCIF_RESULT_REJECT or 'R' when
+	queried about spam.
+    Fix typo in detection of non-compiler on SunOS.
+    Add `./configure --disable-dccifd` as suggested by Krzysztof Snopek.
+
+1.1.22
+    Fix inconsistencies in fuzzy checksums computed by dccm and dccproc.
+    More ./configure script changes to try to deal with problems on
+	a Solaris system with GCC and some undetermined oddities.
+
+1.1.21
+    Suppress repeated messages about unauthorized server IDs of peers.
+    Install cdcc, dccproc, and so forth in $HOME/bin by default
+	if /usr/local/bin is not writable and $HOME/bin exists.
+    Fix infinite loop in decoding invalid HTML character references.
+
+1.1.20
+    Fuzzy checksums ignore all text before initial MIME boundary and
+	after terminal MIME boundary.
+    Add support for Spanish thanks to Leandro Santi.
+    Shuffle hostname resolving code to try to fix what may be a race
+	in the Linux pthread_create() as discovered by Karl Grindley.
+    Reduce default value of `dbclean -e` from 7 to 2 days.  Mail that does
+	not reach the local bulk threshold within 2 days is probably not
+	spam and if it is, it will almost certainly reach a bulk threshold
+	at some other server in the network.
+
+1.1.19
+    fix missing env_From handling in dccifd/dccif.pl observed
+	by Nathan Neulinger
+    set mode of dccifd socket to 0666 as suggested by Nathan Neulinger
+
+1.1.18
+    fix `dccproc -c` logging bug noted by Brad Volz.
+    fix ./configure to pick UID and GUID out of `id` with --disable-sys-inst
+    multiply the `dccd -u` delay by 4 when flooding is off or broken to
+	steer clients away from DCC servers without working links.
+    radically reduce the number of wsync() calls to speed systems with
+	lame mmap() support including BSD/OS 4.2.
+    fix setting of file descriptor limit in dccm and dccifd as noted
+	by Gary Mills
+    change configure script to deal with change in gmake version string
+	discovered by Aaron Paetznick
+
+1.1.17
+    add DCC interface daemon, dccifd, similar to dccm for SpamAssassin and
+	Perl filters and MTAs other than sendmail.  This is only an initial
+	release soliciting comments about its interface.  Its interface
+	may change in 1.1.18.  The new lines of homdir/dcc_conf must be
+	added to /var/dcc/dcc_conf to turn it on.
+    `dccm -a IGNORE` says "would have rejected" in the log messages
+	for Sven Willenberger
+    generate sample client-ID password for localhost server in /var/dcc/ids
+	and /var/dcc/map
+    dccd only complains about unknown server-IDs when "IDS" tracing is
+	turned on.
+    DCC clients check for new server DNS records every other hour
+	instead of every hour
+    compute the same Base64 result for 32-character lines with or without '\r'
+    close unlikely, theoretical per-user log file FD leak in dccm.
+    dccproc passes header lines (including continuations) longer than
+	20 KBytes
+    fix bugs in misc/newwebuser as noted by Furlan Campos.
+    dccm deletes all X-DCC headers of the right brand name to foil
+	tricky spammers.
+    dccproc defaults the -T tmpdir to the -l logdir
+
+1.1.16
+    fix long HELO values in dccm from Leandro Santi.
+    fix /var/dcc ownership installation bugs noted by John Reames.
+    let count of clients seen within 24 hours be more than 1000.
+    change misc/na-spam, the news.admin.net-abuse.sightings gateway script
+	to use dccproc log files instead of generating its own.
+    don't allow ':' in DCC server "brandnames".
+    recover misplaced change to misc/dcc.m4 to fix need to use
+	FEATURE(`delay_checks')
+    fix apparently harmless quoting error in dcc.m4
+    fix `cdcc "flood stats all"` when the server's peers are not ordered
+	by their IDs.
+    decode Base64 with invalidly long lines.
+
+1.1.15
+    change graph generating shell scripts, including making the
+	database size RRA use "MIN" instead of "MAX".  A shell script
+	that can convert existing RRDs is available.
+    make `misc/hackmc -O` apply to all uses of the sendmail access DB
+	instead of only the envelope Mail_From value.
+
+1.1.14
+    tweak graph generating shell scripts.
+    add `cdcc "flood stats all"` and `cdcc "flood stats clear all"`
+
+1.1.13
+    tweak graph generating shell scripts including fixes from Jack Bates.
+    adjust autoconf mechanism to try to deal with systems with inet_ntop()
+	but without IPv6.
+
+1.1.12
+    move dccd statistics to the flod.map so they're preserved despite
+	restarting dccd.
+    add shell scripts to generate RRD graphs.
+    make rate-limits run-time parameters for `dccd -R`.
+
+1.1.11
+    fix dccm bugs with handling a non-responsive server.
+    change misc/hackmc to modify sendmail.cf to reject unauthorized relay
+	attempts with a temporary failure when they are supposed to be sent
+	to the DCC but dccm is not running.  This prevents leaking relay
+	relay spam.  You must use the new hackmc script to install this
+	change in sendmail.cf.
+    remove "# whitelisted" from `cdcc stats` output to give more room
+	for totals.
+    prevent empty dccproc log files as noted by Krzysztof Snopek.
+    even fatal errors should cause dccproc to exit with 0 to avoid
+	rejecting mail, as noted by Krzysztof Snopek.
+    When server hostnames have common IP addresses, prefer the server
+	with the non-anonymous client-ID, noted by Krzysztof Snopek.
+
+1.1.10
+    try to deal with truncated per-user dccm logs on Solaris.
+    reduce threshold at which Fuz2 checksums are computed to capture
+	more spam.
+    force the use of gcc on Solaris.
+    try not to wait for the syslog console messages to resolve a dccm
+	crash on Solaris.
+
+1.1.9
+    make `cdcc "flood list"` unpriviledged, but disclose only server-IDs
+	to strangers.
+
+1.1.8
+    fix "invalid database address" problems on SPARC systems with
+	./configure ----enable-big-db
+    %-encode quotes in URLs generated by the CGI scripts.
+    fix `cdcc "id=X"` for X>65535.
+    increase path length limit to 24.
+    add `dblist -I server-ID`.
+
+1.1.7
+    fix man page installation on AIX.
+    work around connect() bugs on AIX41 and OpenUNIX.
+
+1.1.6
+    fix encoding of quote characters in the CGI scripts
+    look for sendmail 8.12.1 libsm.a that is required by that version of
+	the Milter code.
+    make the configure script again find the FreeBSD MD5 library.
+
+1.1.5
+    fix bugs in white-list links in the CGI scripts.
+    increase 8-hop flooding path limit to 16.
+    changes from Mark Moraes to compile dccproc under Cygwin on Windows 2000
+    the DCC source compiles on OpenUNIX 8.0.1 thanks to Larry Rosenman.
+    reduce the chances of duplicate or missing entries in the list
+	from `cdcc clients` as suggested by Dave Lugo.
+    add `dblist -C` to limit the listing to reports with specified checksums
+	as suggested by Sam Leffler.
+    `dccm -r "4xx ..." now produces a proper SMTP "temporary failure".
+    deal with /usr/include/md5.h that seems to be RedHat 7.3 but that does
+	not compile by itself.
+
+1.1.4
+    fix dccproc and dccm tarballs broken in 1.1.3.
+
+1.1.3
+    add a "VERSION:" string to the start of dccm and dccproc log files.
+    fix memory leak in dccm observed by Gary Mills.
+    fix core-dump in dccd with unreadable /var/dcc/flod noted by Sam Leffler.
+    add prototype CGI scripts for per-user white lists.
+    rate limit and improve log messages about read-only whiteclnt files.
+    allow null passwords for server-IDs in /var/dcc/ids that are used only
+	as markers so that no DCC server is accidentally started with the
+	password "unknown"
+    install initial /var/dcc/map file using dcc.dcc-servers.net
+    install empty server database as suggested by Andrew Macpherson.
+    create /var/dcc/log during installation.
+    adjust the `dccd -u` default to minimize rejecting DCC queries from
+	nearby anonymous clients.
+    convert upper to lower case in dccm per-user white list directories as
+	suggested by Andrew Macpherson.
+    allow null passwords as place-keepers in /var/dcc/ids.
+    rebuild gmake .d depend files when include/dcc_config.h changes so
+	that bad things don't happen when a header file disappears and
+	the configuration changes to match.
+    fix "log-del" option in /var/dcc/flod to log checksum delete requests.
+    add "del" and "no-log-del" options to /var/dcc/flod lines.
+    change the defaults for flooding delete requests to log them ("log-del"),
+	not send them ("no-del" among o-opts) and
+	reject them ("no-del" among i-opts).
+    change misc/hackmc to be usable in typical Makefiles that generate
+	.mc files.  It now feeds a single set of .mc files to m4
+	to produce a single .cf file on stdout instead of a set of .mc files.
+	It also no longer includes ../m4/cf.m4
+    use native sendmail milter libraries on FreeBSD 4.6
+    IDs in /var/dcc/ids can be placeholders without passwords
+
+1.1.2
+    fix `dccm -W`.
+    the recipient mailbox resolved by sendmail can be used as an white list
+	value by dccm.  This simplifies white-listing when the system
+	has more than one name.
+
+1.1.1.
+    add -follow to cron-dccd in case user log directories are beyond
+	symbolic links.
+    fix "resource temporarily unavailable" message from dccproc
+	reported by Henrik Lewander.
+    fix `dccm -W` problem in 1.1.0 reported by Mark Motley.
+
+1.1.0
+    "substitute" whitelist header entries must start with the name of
+	the header.  This is incompatible with previous versions.
+    remove `dccm -a REJECT_ONLY`
+    add per-user whitelists and logs to dccm.  See `dccm -U`. Use the
+	DCCM_USERDIRS variable in the new homedir/dcc_conf file to turn on.
+	To generate per-user log files without leaking informatio about
+	Bcc addresses, the format of all log files has changed slightly.
+	Look for "bulk" in the X-DCC line instead of the final "targets" line.
+    add `dccproc -E` to add dccm log file style envelope lines to log files.
+    fix cleaning of hourly DCC log files as suggested by Gary Mills.
+    X-DCC header lines contain the string "bulk" when the message is bulky.
+    add the "mail_host" as a possible "subsitute header" for dccm.
+    several of the mailing lists in the sample white list now require that
+	dccproc or dccm use `-S sender`	or dccm use `-S mail_host`.
+	This removes hostnames from the sample whitelist, because they
+	can take a long time to resolve or fail to resolve.
+    change env_To: lines in dccm log files to include the sendmail "mailer"
+	and address.  Also add the resolved "mail_addr" and "mail_host"
+	to dccm log files.
+    allow common dccproc white list files owned by the DCC user to be in
+	subdirectories of the DCC home directory instead of only the
+	DCC home directory.
+    use Rgethostbyname() in clients only when `cdcc SOCKS on` is sent
+	and in the server only for flooding peers that are flagged with
+	"SOCKS" in /var/dcc/flod.
+    use gethostid() and hash the local host name instead of gethostbyname()
+	to generate the DCC client host ID
+    consider an entire report of checksums obsolete if the fuzziest checksum
+	is obsolete for dbclean or flooding.  This reduces the database
+	size and flooding bandwidth by another factor of 2.
+    fix `./configure --mandir=/tmp/foo` to put the man pages into
+	/tmp/foo/man8/dccm.8 and similar places on other systems instead
+	of /tmp/foo8/dccm.8 as suggested by Michael Grant.
+    add `configure --disable-sys-install` to simplify and make a non-system
+	installation (e.g. by a user with a shell account) safer (no suid).
+    stop frequent complaints about bad flooding passwords in most cases.
+
+1.0.53
+    fix bug in fuzzy checksums that was not handling long Base64 lines.
+    increase the thresholds for computing the Body and Fuz2 checksums
+	and decrease the threshold for the Fuz1 checksum.
+    limit work-around for broken Linux threads that need signals delivered
+	to the process group to Linux systems to avoid breaking dccm
+	on Solaris 2.6 systems.
+    add `cdccc "flood stats ID".  Part of this involves a change
+	to format of the flod.map file.  It will be automatically rebuilt.
+    fix `cdcc clients` and `cdcc stats` operations to do better with more
+	than 64 active clients.
+    fix core-dump in dccd found by James Carlson.
+    use Rgethostbyname() when SOCKS is configured.
+    fix private (not owned by the dcc user) whitelist files for dccproc.
+    notice and report missing incoming flood connections.
+    detect and complain about duplicate definitions in /var/dcc/ids
+
+1.0.52
+    fix bug in dbclean that was inflating instead of compressing some
+	reports.
+    fix spurious emergency execution of dbclean by dccd.
+    deal with missing h_nerr and h_errlist[] in Solaris 2.6 as
+	suggested by Gary Mills.
+    fix use of old $DCCM_RUNDIR in rcDCC as suggested by Gary Mills.
+    fix extra blank in dcc.m4 as suggested by Gary Mills.
+    generate dcc.m4 with the local choice for /var/dcc/run with configure.
+    use dcc_inet_ntop() on systems that do not understand IPv6 to fix
+	a problem on Solaris 2.6 discovered by Gary Mills.
+
+1.0.51
+    fix man pages on FreeBSD.
+    fix file descriptor leak in dccd when using SOCKS.
+    `cdcc "flood check"` forces dccd to re-resolve hostnames for flooding
+	peers that are failing.
+
+1.0.50
+    Improve automatic dbclean-ing by dccd.
+
+1.0.49
+    Check the log directory for dccproc and dccm after changing to the
+	home directory.
+
+1.0.48
+    Split old records in the database so they compress better.
+    Reduce bandwidth required for flooding by summarizing checksum counts.
+    Fix configure in the partial packages, dcc-dccm-*.tar.Z and
+	dcc-dccproc-*.tar.Z
+    Fix `dccd -K no-IP`.
+    Fix error messages for `dccproc -c type,thold` and add "never" as in
+	`dccproc -c all,never`
+    Fix yet another bug in dcc_mkstemp().
+    Add "NEVER" to -c for dccproc and -t for dccm.
+    Enhance `dccm -l logdir` and `dccproc -l logdir` to scatter log files
+	among directories for systems dealing with more than 500,000
+	mail messages per day.
+    Dccm log files are now named "tmp.XXXXXX" until it is known that they
+	are needed and they are renamed to msg.XXXXXX or they are deleted.
+
+1.0.47
+    Make "-n brand" optional for dccd.
+    decode Base64 before computing checksums.
+    remove `cdcc pck` and `cdcc delck body` operations.
+    add configure parameter --with-bad-locks to deal with Solaris mmap()
+	vs. fcntl() locking problems.
+    Dccm and dccproc shold keep only the last of several locally specified
+	header checksums.
+    Because people have been confused by env_To checksums being ignored
+	in server whitelists, they are now reported as errors.
+
+1.0.46
+    Fix garbage in dccm log file names.
+    When run as root, dbclean avoids changing the owner of the database files.
+    Add logging to dccproc in the style of dccm.
+    Accept hex checksums to allow whitelisting message bodies, and especially
+	"empty" bodies contianing more than 1 KBytes generated by
+	Outlook Express.
+    Improve compression of old entries in the database.
+    Add checking of "substitute" headers.  See -S in the dccm and dccproc
+	man pages.
+
+1.0.45
+    Repair incoming flood duplicate detection broken in 1.0.44.
+
+1.0.44
+    Dccd now tries to fix the database when it starts
+	dccd also marks the database potentially inconsistent while it
+	is running and until it stops cleanly.  Graceless shutdowns
+	are now detected and the database is automatically checked with
+	`dbclean -R` before dccd resumes operation.
+    Add Fuz2 checksums to the default lists along with Fuz1 and Body
+    By default, dccd does not keep non-body checksums in the database.
+	The previous behavior can be restored with -Kall.
+    Double the maximum size of the database's hash table
+    Teach dccd to run dbclean to expire checksums so things work even
+	if the cron job doesn't
+    Misc/hackmc -D adds a local rule to reject mail from SMPT clients
+	without reverse DNS to the DCC
+    Suppress messages from dccd for EINVAL the second connect().
+	This is lame, but both FreeBSD and Linux answer the second connect()
+	on a non-blocking socket after an ICMP Unreachable or timeout
+	with EINVAL.
+    Speed up dbclean for large databases
+	It is only about 2.5 times faster on linux 2.2.14-5.0
+	Large systems handling more than 200,000 messages/day should
+	use --with-db-memory=500000000 or whatever is the appropriate number.
+	This change combined with the -K changes mentioned above produce
+	an overall speed-up of about 10 times for busy, not large systems.
+    Add configure parameter --enable-big-db to support server databases
+	with up to 2 billion instead of 16 million entries in the hash table
+    Fix problem with env_To white-listing reported by Mark Motley
+    Add -H to dccproc to emit only the header
+    Fix dccd to pass -L parameters to dbclean so that log messages from
+	automatic invocations of dbclean are not lost
+
+
+1.0.43
+    add prototype Fuz2 fuzzy body checksum and remove the subject checksum
+	See INSTALL.{html,txt} about DCC_RPT_SUBJECT if you want to
+	restore Subject checksums in in you DCC clients.
+    fix SOCKS connection and re-connection of flood stream.
+    add -c thresholds to dccproc and make its exit code indicate whether
+	they are exceeded.  This should eliminate common needs to parse
+	the output of dccproc.
+    fix dccd iflod_send_pos() core-dump seen during very high network losses
+	to flooding peer.
+    deal with Linux bug in not allowing connect() after a previous
+	connect() to 127.1.
+    fix dccm core-dump on some platforms when whitelist hostnames fail to
+	resolve.
+    deal with Linux `bash` vs. `su`
+    handle duplicate local interfaces on Linux
+    make `cdcd "flood rewind"` require a remote server-ID
+
+1.0.42
+    fix dccm crash while dealing with white list.
+
+1.0.41
+    fix bug introduced in 1.0.37 that broke `cdcc add`
+    include truncated getifaddrs() for systems that do not have it to improve
+	the default behavior of dccd with multihoming.
+    fixes for syntax errors in misc/{rcDCC,stop-dccd} from Michael Ghens
+    make `dccm -W` less confusing.
+    change hackmc to report mail with bogus DNS senders to the DCC
+
+1.0.40
+    fix for syntax error in /var/dcc/libexec/cron-dccd from Dave Lugo
+    deal with slow dccd response to dbclean
+
+1.0.39
+    keep dccd from going crazy with a crazy value for -q
+
+1.0.38
+    fix dccd core dump with Dave Lugo's help.
+    improve dccd host name resolving helper process.
+    improve misc/na-spam.
+    `cdcc 'stats clear'` now also clears the list of clients seen by dccd.
+    add a path of server-IDs to flooded checksum reports.
+    increase the number of checksums recognized by the server.
+    fix pthread error on SunOS and possibly AIX.
+    use absolute path for `cdcc` in /var/dcc/libexec/stop-dccd as suggested
+	by Sam Leffler.
+    improve fuzzy ignoring of MIME multipart boundaries.
+
+
+1.0.37
+    deal with lack of -s in SunOS `logger`.
+    dccd now has a helper process to wait for slow DNS servers to resolve
+	the names of flooding peers.
+    Deleting and restarting the DCC server's database now causes dccd to
+	ask peers to re-flood their checksums.  This new feature required
+	changing the flooding protocol.  DCC servers using the new protocol
+	talk to servers using the old protocol after the old servers start
+	their streams or with an explicit tag in the /var/dcc/flod file.
+    `sendmail -bs` is used by some mail user agents such as pine.  In such
+	cases the sendmail milter interface gives filters such as dccm a null
+	pointer to what should be an IP address and a pointer to the
+	string "localhost".  Dccm now acts as if such mail arrived from
+	IP address 127.1.  This makes the common white list entry
+	"ok IP localhost" effective for such mail.  Note that dccm deletes
+	X-DCC header lines with its own brand from white listed messages,
+	because they would otherwise be wrong and a potential vulnerability
+	to bad guys.
+    Fix `dccproc -o ofile` to include the X-DCC header in ofile.  If this fix
+	is a problem, see `dccproc -C`
+    add /var/dcc/libexec/na-spam and ng-spam to gather spam from
+	news.admin.net-abuse.sightings
+    fix start-dccd, start-dccm, and cron-dccd to support multiple dccd
+	daemons in separate home directories.
+
+1.0.36
+    support for OSF1.
+    handle msync() with only two parameters in old BSD/OS.
+    try to fix rare core-dump in dccm whitelist parsing.
+    fix error in misc/dccdnsbl.m4 noted by Michael Ghens.
+    fix autoconf errors for SunOS noted by Sam Leffler
+    add "log-del" option to /var/dcc/flod file
+    fix recent damage to DCC{D,M}_ARGS in start_dcc{d,m}
+
+1.0.35
+    add DCC_LOG_FACILITY to dcc_conf as suggested by Sam Leffler.
+	You must install the new homedir/dcc_conf with your parameters
+	to use it.
+    fix recently introduced bug that kept dccd from automatically
+	running dbclean to expand the database.
+    document the output of the dblist program in its man page.
+    `configure --with-rundir` can be used to override the use of /var/run/dcc
+	for the PIDs of DCC daemons, sockets, and so forth.
+    `configure --with-uid=dcc` creates Makefiles and scripts to install
+	and start DCC programs as the user "dcc"
+
+
+1.0.34
+    support for IRIX
+    fix bug in setting libexecdir for configure
+    change `cdcc stats` to show cumulative report counts
+    increase maximum number of flooding peers from 16 to 32
+	and make it a compile-time parameter
+    change $UID in misc/start-dcc{d,m} to the avoid reserved variable in
+	RedHat 6.2 as suggested by Michael Ghens
+    fix bug in cron-dccd found by Michael Ghens and Dave Lugo
+    remove mechanism for configuring the DCC home directory by setting
+	an environment variable before invoking `make`
+    change the default value of the -u anon-delay parameter for dccd to 0.
+    add "flood list" operation to `cdcc`
+    look for sendmail for dccm in a FreeBSD "ports" package
+
+1.0.33
+    support for HPUX thanks to Richard Rauenzahn.
+    check against "$USER" instead of "root" in start-dccm and start-dccd
+	as suggested by Luke Crawford.
+    make the server rate limits configurable at compile-time.
+
+1.0.32
+    fix bug in local white lists that ignored changes in the count field
+    by default, start-dccm no longer tells dccm to reject based on
+	message-ID checksums
+    fix recently introduced bug that kept flooding off after the
+	hash table needs to be expanded.
+
+1.0.31
+    add SOCKS support.
+    dccproc only logs errors unless given -d.  This should fix problems
+	in some mail systems using dccproc caused by network problems.
+    fix permissions bugs related to using private map files
+    the dcc_notspam sendmail macro used by dccm with -o must be non-empty
+	to be considered "set".
+
+1.0.30
+    fix man page installation on OpenBSD.
+    fix bug in starting incoming floods on systems with IPv6 interfaces
+	but without what DCC recognizes as IPv6 support such as OpenBSD.
+    deal with systems such as OpenBSD with lame mmap() support.
+    speed up recognition of changes in the /var/dcc/flod file.
+    use DCCM_REJECT_AT in /var/dcc/dcc_conf to also set the default
+	flooding threshold used by dccd when it is started by
+	/var/dcc/libexec/start-dccd
+    add configure switches to not build dccm and the server
+    `dccd -u` turns off `cdcc stats` from anonymous systems to avoid telling
+	strangers how many mail messages a small DCC server has seen.
+
+1.0.29
+    fix start-dccd to deal better with non-standard DCC home directories.
+    dccproc is now like dccm and treats a missing Message-ID header.
+	as if it were present and with a null value.
+    do the right thing for DCC servers running on platforms where
+	gethostname() fails completely on a short buffer instead of
+	giving a prefix of the hostname.
+    detect and quit on null hostname from gethostname().
+
+1.0.28
+    improve the handling of an already running daemon in by misc/start-dccd
+    support mapping of ranges of server IDs when flooding reports
+    yet more changes to deal with quoted-printable.  These changes
+	generally cause the fuz1 checksum to differ.
+    remove need for FEATURE(delay_checks) when reporting sendmail access_db
+	hits to DCC server
+    change body checksum to ignore '>' in "\n>From" because the '>' is
+	often added for old UNIX MUAs.
+    improve response of dccproc to 20KByte or larger To: headers.
+    make `cdcc "file map2; load map2.txt"` act the same as
+	`printf "file map2\nload map2.txt" | cdcc`
+    dccm now treats a missing Message-ID header as if it existed but with
+	a null value.
+
+1.0.27
+    change example scripts to deal with `expr` exiting with 1 and stopping
+	them on Solaris
+    fix client IDs larger than 65535
+    detect and complain server IDs offered to `cdcc` as client IDs
+
+1.0.26
+    if dccm is already installed, try to build it even if the sendmail
+	milter library is not available to prevent silent failures to
+	install new versions of dccm.
+
+1.0.25
+    fix confusion if a quoted-printable sequence overlaps a buffer boundary.
+    do not give up on remote servers if a local server responds with
+	an ICMP unreachable error.
+
+1.0.24
+    minimize interpreting '=' in a URL as quoted-printable to make dccproc
+	and dccm compute the same fuzzy checksums more often.
+
+1.0.23
+    fix confusion in dccproc about whether an initial line of a message
+	that starts with blanks is a continuation of the last header line
+
+1.0.22
+    fix infinite loop and packet spew from dccproc when the clock jumps
+    backward or jumps forward more than 1000 seconds.
+    fix syslog process name on Solaris and AIX
+    `dccproc -R` picks IP address out of standard Received: lines
+    fix bugs in decoding quoted printable with broken soft ends of lines
+
+1.0.21
+    repair DCC server whitelist broken in 1.0.20
+
+1.0.20
+    support for Solaris
+    describe ways to connect spam traps to the DCC in INSTALL.html
+    move parameters from start-dccd, start-dccm, and cron-dccd to a common file
+    add misc/rcDCC start-up script for Solaris and Linux
+    fix byte-order bug in flood header server ID which requires changing
+	the flood protocol.  To flood to version 1.0.19 or older versions
+	of dccd, specifiy version 4 in the flod file line.
+    removed locking file /var/dcc/map.lock
+    change handling of spam sent simultaneously to white-listed and unlisted
+	targets.  See the discussion of the new "REJECT_ONLY" action in the
+	dccm man page.
+
+1.0.19
+    improve `cdcc stats` flood formatting
+    fix `cdcc "host domain.com; stats all"`
+    change dccproc to use the value of the Return-Path: header for the
+	envelope-From checksum if the header is present and -f is not used.
+    fix `dbclean -S -N` when the whitelist is empty
+    add rough support for NetBSD.
+    mention dccd in the INSTALL file.
+    fix for parsing "-L error,LOCAL1.ERR" from Vincent Schonau
+
+1.0.18
+    add "clients -n" to cdcc
+    add -C to dccproc
+
+1.0.17
+    add dccsight
+
+1.0.16
+    try again to deal with getifaddrs() without freeifaddrs().
+    fix bug introduced in 1.0.15 that causes dccproc to require
+	a white-list
+    fix corruption of /var/dcc/map when dccproc is run with stderr not
+	open and when the DCC server first fails to answer.
+
+1.0.15
+    make the sendmail {dcc_isspam} and {dcc_notspam} macros consistently
+	override what dccm and the DCC server determine
+
+1.0.14
+    deal with systems that have getifaddrs() but not freeifaddrs().
+    fix bogus response from server when a duplicate request from an
+	anonymous client arrives before the original request has been
+	scheduled to be answered.
+    fix obscure double-trip bug in threaded client library.
+    accept "rpt-ok" as well as "rpt_ok" in the ids file.
+    fix /var/dcc/flod option scanning bug by dccd.
+    'dccd -u 999999' turns off access by anonymous or unauthenticated clients.
+    add -W to dccm to cause only explicitly listed targets to be protected
+	by the DCC
+    add a "reject" server-ID translation target in the flods file to
+	not send or receive the reports of some servers.
+
+1.0.13
+    add RTT adjustment to cdcc load and add operations to allow a client
+	to prefer servers despite worse RTT's
+
+1.0.12
+    in dccm count two intead of one open file for each active job against
+	the system imposed limit on open files for automatically setting
+	the value of -j for dccm and for automatically changing the soft
+	resource limit.
+    use the GNU autoconfig install script instead of `install -d` to create
+	$(HOMEDIR)/libexec because GNU autoconfig does not detect install
+	programs that do not understand -d
+    rate limit complaints by dccd about unrecognized server IDs
+
+1.0.11
+    dccm tolerates null sender IP addres and hostname from `sendmail -bs`
+	from sendmail 8.11.3 but perhaps not from 8.12.
+    change -p for dccd and dbclean to -a to allow specification of entire
+	server addresses.
+    by default, dccd listens on separate UDP sockets so that clients receive
+	responses from the same IP address to which they send requests.
+
+1.0.10
+    fix "bogus oflod complaint length 0" nonsense from server
+    `cdcc stats` counts the clients seen in the last 24 hours, but
+       `cdcc clients` displays all that fit in the cdcc buffer even if
+	older than 24 hours
+    the `configure` script looks at `make -v` to guess whether to generate
+	gmake or make makefiles
+    include list of common "dictionary attack" user names among the sample
+	homedir files
+
+1.0.9
+    body checksums ignore effects of quoted-printable encoding
+    deal with versions of gmake that do not understand ?=
+    improve "clients" request of cdcc
+
+1.0.8
+    fix rate limiting bugs in the server
+    fix local env-To whitelist
+
+1.0.7
+    fix locking bug when client whitelist file cannot be opened
+    use `install -c` to not delete misc scripts
+    fix server flood stalls when there are many stale or whitelisted
+	reports
+
+1.0.6
+    fix bug in alternate dccm argv[0] in start-dccm
+    fix bug in noticing changes to included white lists
+
+1.0.5
+    install cron-dccd, start-dccd, and start-dccm in $(HOMEDIR)/libexec
+
+1.0.4
+    fix server core-dump for repeated invalid admin. opcodes while
+	tracing is enabled.
+    add "clients" request to `cdcc`
+    add "stats all" request to `cdc
+    add homedir/start-dccm.sh
+    /var/run/dccm.pid and /var/run/dccm depend on argv[0]
+    white-lists can use "include pathname"
+    dccm -o overrides -s
+    dccm -o and -s have default values
+    move /var/run/dccm and /var/run/dccm.pid to the directory /var/run/dcc
+	and change the sendmail "feature" file misc/dcc.m4 to match
+
+1.0.3
+    improve flood ID mapping
+    remove need to explicitly build before `make install`
diff -r 000000000000 -r c7f6b056b673 FAQ.html.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FAQ.html.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,1384 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<HTML>
+<HEAD>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <TITLE>DCC FAQ</TITLE>
+    <META http-equiv="Content-Style-Type" content="text/css">
+    <STYLE type="text/css">
+        <!--
+        BODY {background-color:white; color:black}
+        UL.FAQlist {margin-left:10%; margin-right:10%}
+        DL.FAQbody {margin-left:5%}
+        DT {font-weight:bolder}
+        .small {font-size:smaller}
+        IMG.logo {width:6em; vertical-align:middle}
+        -->
+    </STYLE>
+</HEAD>
+
+<BODY>
+<H1>Distributed Checksum Clearinghouse (DCC) Frequently Answered Questions</H1>
+
+<P>
+<A HREF="http://www.rhyolite.com/dcc/FAQ.html">Current versions</A>
+of this list can be found among the
+<A HREF="http://www.rhyolite.com/dcc/">http://www.rhyolite.com/dcc/</A>
+web pages and their <A HREF="http://www.dcc-servers.net/dcc/FAQ.html">mirror</A>
+at
+<A HREF="http://www.dcc-servers.net/dcc/">http://www.dcc-servers.net/dcc/</A>.
+
+
+<UL class="FAQlist">
+
+<LI><A HREF="#what-is-it">
+What is the Distributed Checksum Clearinghouse or DCC?</A>
+<LI><A HREF="#license">
+Is the DCC source free?</A>
+<LI><A HREF="#source">
+Where can I get DCC source?</A>
+<LI><A HREF="#binary">
+Where can I get DCC RPMs, packages or other binary forms?</A>
+<LI><A HREF="#fuzzy-personalize">
+Do the fuzzy checksums ignore <Q lang="en-us">personalizations</Q>?</A>
+<LI><A HREF="#system-load">
+How much bandwidth, disk space, and computing does the DCC require?</A>
+<LI><A HREF="#need-server">
+Do I need to run a DCC server?</A>
+<LI><A HREF="#crash">
+What happens to my mail if the DCC break?</A>
+<LI><a HREF="#mark-only">
+How do I mark spam without rejecting it?</A>
+<LI><A HREF="#bad-man">
+Why doesn't the man command find the man pages?</A>
+<LI><A HREF="#sendmail-only">
+Must sendmail be used with DCC?</A>
+<LI><A HREF="#smtpd">
+Can the DCC be used with smtpd?</A>
+<LI><A HREF="#exim">
+Can the DCC be used with Exim?</A>
+<LI><A HREF="#other-MUAs">
+How can the DCC be used with mail user agents?</A>
+<LI><A HREF="#spamass">
+Can the DCC be used with SpamAssassin or other spam filters?</A>
+<LI><A HREF="#dcc-delay">
+How long must SpamAssassin or an MTA wait for DCC results?</A>
+<LI><A HREF="#root-needed">
+Must I have the root password to use DCC?</A>
+<LI><A HREF="#firewall-ports2">
+Why don't the public DCC servers work?  Do I need a client-ID?</A>
+<LI><A HREF="#firewall-ports">
+Which ports do I need to open in my firewall?</A>
+<LI><A HREF="#cleaning1">
+Why does the dccd database grow without bound?</A>
+<LI><A HREF="#cleaning2">
+The dccd database is corrupt.  What should I do?</A>
+<LI><A HREF="#cleaning3">
+How can I stop the log directories from overflowing?</A>
+<LI><A HREF="#bad-locks">
+Why do my DCC clients including cdcc and dccproc
+complain about <Q lang="en-us">Resource temporarily unavailable</Q>?</A>
+<LI><A HREF="#maxprocs">
+Why does dccifd or dccm complain about
+<Q lang="en-us">thread_create() failed: 11, try again</Q>? or
+<Q lang="en-us">pthread_create(): Cannot allocate memory</Q>?</A>
+<LI><A HREF="#max-work">
+Why does dccm or dccifd complain about
+<Q lang="en-us">too many simultaneous mail messages</Q>?</A>
+<LI><A HREF="#server-pick">
+Why doesn't my DCC client pick my local DCC server?</A>
+<LI><A HREF="#IDs1">
+If I have a server-ID, do I need a DCC client-ID, or vice versa?</A>
+<LI><A HREF="#IDs2">Why does my DCC server complain about
+ "rejected server-IDs" among flooded checksum reports?</A>
+<LI><A HREF="#server-rate-limits">
+Why does my DCC server refuse to accept more than 50 operations per second?</A>
+<LI><A HREF="#private-server">
+How do I keep strangers from using my DCC server?</A>
+<LI><A HREF="#dccm-log1">
+How can I determine why dccm reported
+a message as spam or with a recipient count of "MANY"?</A>
+<LI><A HREF="#dblist1">
+How can I see what checksums my server has heard from its clients?</A>
+<LI><A HREF="#whitelist13">
+How do I stop DCC false positives?</A>
+<LI><A HREF="#whitelist1">
+Why is mail from my favorite mailing list marked with an
+<I>X-DCC</I> header line that says it is spam?</A>
+<LI><A HREF="#whitelist11">
+Why are acknowledgments of spam reports mistakenly
+marked as spam by DCC?</A>
+<LI><A HREF="#x-dcc-header1">
+Why are some checksums missing from my <I>X-DCC</I> header lines?</A>
+<LI><A HREF="#whitelist9">
+How do I maintain client whitelists?</A>
+<LI><A HREF="#whitelist2">
+Do I need both server and client whitelists?</A>
+<LI><A HREF="#whitelist3">
+When the whitelist file used by dccd, dccm or dccifd is changed,
+what must be done to tell the software about the change?</A>
+<LI><A HREF="#whitelist14">
+How do I test a whiteclnt file?</A>
+<LI><A HREF="#reg-exps1">
+Can I use wild cards or regular expressions in DCC whitelists?</A>
+<LI><A HREF="#whitelist10">
+How do I whitelist mail from a legitimate
+bulk mailer using its name or SMTP headers such as Mailing-List or the
+Habeas SWE headers?</A>
+<LI><A HREF="#incompat-whitelists">
+Why does dccm or dccifd complain about "incompatible whitelists"?</A>
+<LI><A HREF="#whitelist4">
+Why do legitimate mail messages have
+<I>X-DCC</I> header lines that say they are "bulk", "many", or spam?</A>
+<LI><A HREF="#whitelist5">
+Are IP address blocks in whitelists used by dccproc?</A>
+<LI><A HREF="#whitelist6">
+Why is dccproc is ignoring <I>env_from</I> whitelist entries?</A>
+<LI><A HREF="#delck">
+What if I make a mistake with
+dccproc&nbsp;-t&nbsp;many and report legitimate mail as spam?</A>
+<LI><A HREF="#whitelist8">
+Can the sendmail "spamfriend" mechanism tell
+dccm to not check mail sent to some addresses?</A>
+<LI><A HREF="#whitelist12">
+How do I tell dccm to not check mail for an entire domain?</A>
+<LI><A HREF="#false-positives">
+How can I avoid polluting the databases of DCC servers with
+checksums of my mail that is not spam?</A>
+<LI><A HREF="#spamtrap">
+Can DCC be fed with <Q lang="en-us">spam traps</Q>?</A>
+<LI><A HREF="#flood3">
+How many flooding peers does my DCC server need?</A>
+<LI><A HREF="#flood1">
+Do I need to tell the operators of other DCC servers
+the password for controlling my server to turn on flooding?</A>
+<LI><A HREF="#flood2">
+How can I figure out why flooding is not working?</A>
+<LI><A HREF="#rtt">
+Why didn't the RTT reported by
+the cdcc&nbsp;info
+operation change when my network topology changed?</A>
+<LI><A HREF="#socks1">
+When my clients are configured to use SOCKS, they do not
+realize immediately when a server is down.</A>
+</UL>
+
+<P>
+<HR>
+
+<DL class="FAQbody">
+
+<DT><A NAME="what-is-it">
+What is the Distributed Checksum Clearinghouse or DCC?</A>
+<DD>
+The DCC or Distributed Checksum Clearinghouse is an anti-spam content filter
+that runs on a variety of
+<A HREF="INSTALL.html#Compatibility">operating systems</A>.
+The idea of the DCC is that if mail recipients could compare
+the mail they receive, they could recognize unsolicited bulk mail.
+A DCC server totals reports of "fuzzy" checksums of
+messages from clients and answers queries about the total counts
+for checksums of mail messages.
+<P>
+See the main <A HREF="dcc.html">DCC man page</A> as well as the
+<A HREF="http://www.rhyolite.com/dcc/#overview">DCC web page</A>
+and its <A HREF="http://www.dcc-servers.net/dcc/#overview">mirror</A>.
+
+
+<P><DT><A NAME="license">
+Is the DCC source free</A>
+<DD>
+The non-commercial Distributed Checksum Clearinghouse source carries a
+<A HREF="LICENSE">license</A>
+that is free only to organizations that do not sell filtering devices or
+services except to their own users and that participate in the global
+DCC network.
+ISPs that use DCC to filter mail for their
+own users are intended to be covered by the free license.
+You can redistribute unchanged copies of the free source, but you <B>may not</B>
+redistribute modified, "fixed," or "improved" versions of the source
+or binaries.
+You also can't call it your own or blame anyone for the results of using it.
+<P>
+Organizations that do not qualify for the free license are welcome to
+inquire about  licenses for the commercial version by email to
+<A HREF="mailto:sales@rhyolite.com">sales@rhyolite.com</A>
+or via the
+<A HREF="http://www.rhyolite.com/cgi-bin/ct.cgi?sb=Commercial+DCC+License">form</A>.
+The commercial version supports
+<A HREF="http://www.rhyolite.com/dcc/reputations.html">DCC
+Reputations</A>.
+<P>
+Please note that organizations that do not qualify for the free DCC license
+have never been allowed to use the public DCC servers.
+
+
+<P><DT><A NAME="source">
+Where can I get DCC source?</A>
+<DD>
+The official DCC source repositories are at
+<A HREF="http://www.rhyolite.com/dcc/source/dcc.tar.Z">www.rhyolite.com/dcc/</A>
+and
+<A HREF="http://www.dcc-servers.net/dcc/source/dcc.tar.Z">http://www.dcc-servers.net/dcc/</A>.
+<P>
+Please do not try to use ancient versions of DCC software dating from early
+2005 and redistributed by third parties including some Linux packagers.
+Those versions do not detect bulk mail as well as more recent versions.
+Installations using those old versions also have problems using the
+public DCC servers that often make it necessary to add their IP addresses
+to the blacklist that protects the public DCC servers.
+Even worse, all known Linux redistributions of DCC software have been
+changed in ways that break things, including the
+<A HREF="misc/updatedcc.in">libexec/updatedcc</A> shell script that could
+otherwise be used to fetch, configure, compile, install, and restart
+a current version.
+<P>
+When installing DCC software, please consider the installation instructions
+in the
+<A HREF="INSTALL.html">INSTALL.html</A> file included with
+the source or in the
+<A HREF="http://www.dcc-servers.net/dcc/INSTALL.html">on line source trees</A>.
+
+
+<P><DT><A NAME="binary">
+Where can I get DCC RPMs, packages or other binary forms?</A>
+<DD>
+There are no official distributions of DCC binaries,
+whether simple a.out files, RPM Package Manager (RPM) packages,
+or BSD style ports or packages (pkg).
+There are many unofficial sources of DCC binaries, including
+Linux RPMs and BSD style packages.
+<P>
+As of 2008, the FreeBSD packages are not too far out of date and
+include a working version of the
+<A HREF="misc/updatedcc.in">libexec/updatedcc</A> shell script that
+fetches, configures, compiles, installs, and restarts
+a current version.
+<P>
+As far as known in 2008, all DCC RPMs offered by Linux distributors
+are based on DCC software from 2005 and <STRONG>should not</STRONG> be used.
+
+
+<P><DT><A NAME="fuzzy-personalize">
+Do the fuzzy checksums ignore <Q lang="en-us">personalizations</Q>?</A>
+<DD>
+Yes, they ignore many <Q lang="en-us">personalizations</Q> and
+<Q lang="en-us">hash busters</Q>.
+
+
+<P><DT><A NAME="system-load">
+How much bandwidth, disk space, and computing does the DCC require?</A>
+<DD>
+The UDP packets used by a DCC client to obtain the checksum totals
+from a DCC server for a mail message generally use less bandwidth than
+the DNS queries required to receive the same message.
+A DCC client needs very little disk space.
+<P>
+Bulk messages are usually logged by DCC clients.
+On systems receiving a lot of mail, the mechanisms for automatically
+creating new log directories every minute, day, or hour
+can keep any single log directory from becoming too large.
+See the <A HREF="dccm.html#OPTION-l">dccm</A>
+and
+<A HREF="dccproc.html#OPTION-l">dccproc</A>
+man pages.
+<P>
+About 1.4 GBytes/day are exchanged between each pair of DCC servers.
+Each server has 3 or 4 peers.
+The resulting database is about 3 GBytes with the default expiration
+parameters..
+However, while <A HREF="dbclean.html">dbclean</A> is deleting old checksums,
+there are three copies of the database.
+The DCC clients and server do not need many CPU cycles,
+but the daily executions of <A HREF="dbclean.html">dbclean</A>
+on a system with a DCC server
+require a computer with at least 2 or 3 GBytes of RAM.
+In 2006,
+a DCC server prefers 4 GBytes of RAM and can use 6 GBytes.
+12 to 18 GBytes of disk space are also needed.
+<P>
+DCC servers used by clients handling 100,000 or more messages per day
+need to be larger.
+Each additional 100,000 messages/day need about 100 MBytes of disk space
+and system memory, given the default expiration used by
+<A HREF="dbclean.html#OPTION-e">dbclean</A>.
+
+
+<P><DT><A NAME="need-server">Do I need to run a DCC server?</A>
+<DD>
+A mail system that processes fewer than 100,000 mail messages per day
+uses less of its own bandwidth and the bandwidth of other DCC servers
+by using the <A HREF="http://www.dcc-servers.net/dcc/#public-servers">public
+DCC servers</A>.
+Each mail message needs a DCC transaction that requires
+about 100 bytes, and so 100,000 mail messages/day imply about 10
+MBytes/day of DCC client-server traffic.  Each DCC server needs to
+exchange "floods" or streams of checksms with 4 other servers.  Each
+flood is currently about 1.4 GBytes/day for a current total of about
+3 GBytes/day.
+<P>
+When normally installed by the included Makefiles, DCC clients are
+configured to use the
+<A HREF="http://www.dcc-servers.net/dcc/#public-servers">public DCC servers</A>
+without any additional configuration except opening firewalls to port UDP 6277.
+<P>
+Mail systems that process more than 100,000 mail messages per day
+need local DCC servers connected to the global network of DCC servers.
+The public DCC servers include denial of service defenses which
+ignore requests in excess of about 240,000 per day per client.
+<P>
+It is wrong to resell the CPU cycles, network bandwidth,
+disk space, and, most important, human system administration work of the
+public DCC servers.
+Vendors of "anti-spam appliances" or similar
+that do not steal from the operators
+of the public DCC servers have always run their own DCC servers.
+
+
+<P><DT><A NAME="crash">
+What happens to my mail if the DCC break?</A>
+<DD>
+When in doubt or trouble, the DCC clients including
+<A HREF="dccproc.html">dccproc</A> and <A HREF="dccm.html">dccm</A>
+deliver mail.  They wait only a little while for a DCC server
+to answer before giving up.  They then avoid asking a server for a while
+to avoid slowing down mail.
+<P>
+If the DCC sendmail interface or milter program, dccm, crashes,
+the default parameters in <A HREF="misc/dcc.m4">misc/dcc.m4</A>
+for the sendmail.cf Xdcc line
+tell sendmail to wait only about 30 seconds before
+giving up and delivering the mail.
+<P>
+The DCC client code keeps track of the speeds of the
+servers it knows about, and uses the fastest or closest.
+Every hour or so it re-resolves A records
+and checks the speeds of the servers it
+is not using.  When the current server stops working or gets significantly
+slower, the client code switches to a better server.
+
+
+<P><DT><A NAME="mark-only">
+How do I mark spam without rejecting it?</A>
+<DD>
+Unless given thresholds at which to reject mail,
+<A HREF="dccm.html#OPTION-t">dccm</A>
+and
+<A HREF="dccproc.html#OPTION-c">dccproc</A> do not reject mail.
+When dccm is given a threshold by setting DCCM_REJECT_AT in
+<A HREF="homedir/dcc_conf.in">dcc_conf</A> in the DCC home directory,
+DCCM_ARGS can also be set to <A HREF="dccm.html#OPTION-a">"-a&nbsp;IGNORE</A>
+so that spam is marked but not rejected.
+
+
+<P><DT><A NAME="bad-man">
+Why doesn't the man command find the man pages?</A>
+<DD>
+The nroff source, formated nroff output, and HTML versions of the
+man pages are in the top-level source directory.
+Formatted or nroff source is installed by default somewhere in /usr/local/man
+depending on the target system.
+It may be necessary to add /usr/local/man to the MANPATH environment variable.
+Even with that, SunOS 5.7 sometimes has trouble finding them unless
+<B>man&nbsp;-F</B> is used.
+
+
+<P><DT><A NAME="sendmail-only">
+Must sendmail be used with DCC?</A>
+<DD>
+While the sendmail milter interface, <A HREF="dccm.html">dccm</A>
+and the DCC program interface or <A HREF="dccifd.html">dccifd</A>
+are the most efficient ways to report and check DCC checksums,
+<A HREF="dccproc.html">dccproc</A> is also commonly used.
+
+
+<P><DT><A NAME="smtpd">
+Can the DCC be used with smtpd?</A>
+<DD>
+Yes, <A HREF="dccproc.html">dccproc</A> can be used with Obtuse's smtpd.
+Dave Lugo has contributed a shell script to the
+<A HREF="http://sd.inodes.org/">smtpd-sd project</A>
+which can be used to do DCC checking prior to the end of the SMTP
+DATA command.
+
+
+<P><DT><A NAME="exim">
+Can the DCC be used with Exim?</A>
+<DD>
+There are comments about using <A HREF="dccproc.html">Dccproc</A> with
+<A HREF="http://www.exim.org/">Exim</A>
+in the
+<A HREF="http://www.rhyolite.com/pipermail/dcc/">DCC mailing list archives</A>
+including these messages:
+<UL>
+<LI><A HREF="http://www.rhyolite.com/pipermail/dcc/2002/000203.html">
+2002/000203</A>
+<LI><A HREF="http://www.rhyolite.com/pipermail/dcc/2002/000254.html">
+2002/000254</A>
+</UL>
+<P>
+<STRONG>However</STRONG>, those mailing list messages talked about using
+<A HREF="dccproc.html">dccproc</A> before
+<A HREF="dccifd.html">dccifd</A> was available.
+Dccproc is suitable only for low mail volumes.
+
+
+<P><DT><A NAME="spamass">
+Can the DCC be used with SpamAssassin or other spam filters?</A>
+<DD>
+The DCC can be used with
+<A HREF="http://spamassassin.apache.org/">SpamAssassin</A> as
+well as other spam and virus filters.
+Note that it is more efficient to arrange to use a DCC client daemon
+such as <A HREF="dccm.html">dccm</A> to mark passing mail and check
+<I>X-DCC</I> header lines in the filter than to start and run
+<A HREF="dccproc.html">dccproc</A> on each message.
+<P>
+Some commercial virus and spam filters include DCC clients that
+query public DCC servers or DCC servers operated by the filter vendor
+and that "flood" or exchange bulk mail checksums with public servers.
+Reputable manufacturers of such devices operate their own DCC servers
+connected to global network of DCC servers instead of stealing and then
+selling the CPU cycles, network bandwidth, disk space, and, most important,
+human system administration efforts of the public DCC servers.
+
+<P><DT><A NAME="dcc-delay">
+How long must SpamAssassin or an MTA wait for DCC results?</A>
+<DD>
+DCC clients including dccproc, dccifd, and dccm can wait as long as
+about 16 seconds for an answer from a DCC server.
+Except when an anonymous client triggers the progressive delays that are
+among the defenses against denial of service attacks in the public DCC servers,
+delays are almost always less than 10 seconds.
+Delays for DNS blacklists
+(see <A HREF="dccifd.html#OPTION-B">dccifd&nbsp;-B</A>)
+are additional.
+
+
+<P><DT><A NAME="other-MUAs">
+How can the DCC be used with mail user agents?</A>
+<DD><A HREF="dccproc.html">Dccproc</A> can be used with any mail user
+agent that can check mail headers.
+For example, WD Baseley sent a
+<A HREF="http://www.rhyolite.com/pipermail/dcc/2002/000212.html">note</A>
+to the <A HREF="http://www.rhyolite.com/mailman/listinfo/dcc">DCC
+mailing list</A>
+on how to configure <A HREF="http://www.eudora.com/">Eudora</A> to
+act on X-DCC header lines.
+<P>
+Bharat Mediratta has developed DeepSix for people using mail user agents
+on UNIX boxes connected remote servers such as corporate Exchange servers.
+See his
+project on <A HREF="http://www.sourceforge.net/projects/deepsix">Sourceforge</A>
+as well as his
+<A HREF="http://www.rhyolite.com/pipermail/dcc/2001/000042.html">announcement</A>
+in the DCC mailing list.
+
+
+<P><DT><A NAME="root-needed">
+Must I have the root password to use DCC?</A>
+<DD>
+No, the procmail or sendmail .forward DCC user program,
+<A HREF="dccproc.html">dccproc</A>
+can be installed in an individual ~/bin directory.
+Then <A HREF="cdcc.html">cdcc</A>
+can create a private map file used with
+<A HREF="dccproc.html#OPTION-h">dccproc&nbsp;-h&nbsp;dir</A>
+or
+<A HREF="dccproc.html#OPTION-m">dccproc&nbsp;-m&nbsp;dir/map</A>.
+<P>
+Also see the <A HREF="INSTALL.html#individual-user">DCC installation
+instructions</A>.
+
+
+<P><DT><A NAME="firewall-ports2">
+Why don't the public DCC servers work?  Do I need a client-ID?</A>
+<DD>
+The public DCC servers accept requests from clients using the
+anonymous client-ID.
+Incorrectly configured firewalls often cause problems.
+Traceroute can be used to send UDP packets to test for interfering firewalls.
+See the answer to the <A HREF="#firewall-ports">firewall question</A>.
+<P>
+After firewalls, the most common cause of problems while trying to
+use the public DCC servers is sending too many requests.
+The DCC server daemon, <A HREF="dccd.html">dccd</A>, includes
+defenses against denial of service or DoS attacks.
+Those defenses include progressively delaying responses
+and eventually ignoring requests.
+The ancient version of the DCC client software included in some
+Linux redistributions tries so hard to reach the fastest server
+that it can trigger those DoS defenses.
+
+<P><DT><A NAME="firewall-ports">
+Which ports do I need to open in my firewall?</A>
+<DD>
+DCC traffic is like DNS traffic.  You should treat port 6277
+like port 53.
+Allow outgoing packets to distant UDP port 6277 and incoming packets
+from distant UDP port 6277.
+<P>
+If the command `cdcc info` says no DCC servers are answering,
+you may need to adjust your firewall.
+Also consider the other reasons why the
+<A HREF="#firewall-ports2">public DCC servers can ignore requests</A>.
+<P>
+If you run a DCC server, open incoming connections to local TCP port 6277
+from your flooding peers,
+and outgoing connections to TCP port 6277 on your flooding peers.
+Also open UDP port 6277 to IP address 192.188.61.3 for the DCC server status
+web page.
+<P>
+See also the discussion of Cisco ACLs at
+<A HREF="http://www.dcc-servers.net/dcc/firewall.html">http://www.dcc-servers.net/dcc/firewall.html</A>.
+
+<P><DT><A NAME="cleaning1">
+Why does the</A> <A HREF="dccd.html#FILE-dcc_db">dccd database</A>
+grow without bound?
+<DD><A HREF="dbclean.html">Dbclean</A> should be run every night when the
+system is least busy
+with the <A HREF="misc/cron-dccd.in">misc/cron-dccd</A> script.
+An entry like <A HREF="misc/crontab.in">misc/crontab</A> should be put into
+the crontab file for the user that runs <A HREF="dccd.html">dccd</A>.
+
+
+<P><DT><A NAME="cleaning2">
+The dccd database is corrupt.  What should I do?</A>
+<DD><A HREF="dbclean.html#OPTION-R">Dbclean&nbsp;-R</A>
+will usually repair a broken
+DCC server database.
+However,
+if your server is "flooding" or exchanging checksums with other servers,
+it is often quicker to stop the DCC server,
+delete the
+<A HREF="dccd.html#FILE-dcc_db">@prefix@/dcc_db</A> and
+<A HREF="dccd.html#FILE-dcc_db.hash">@prefix@/dcc_db.hash</A> files
+and restart <A HREF="dccd.html">dccd</A> with the
+<A HREF="misc/start-dccd.in">libexec/start-dccd</A> script.
+When dccd starts, it will notice that the database has been purged
+and ask its flooding peers to rewind and retransmit their checksums of
+bulk mail.
+
+
+<P><DT><A NAME="cleaning3">
+How can I stop the log directories from overflowing?</A>
+<DD>
+Global <A HREF="dccm.html#OPTION-l">dccm</A>
+or <A HREF="dccifd.html#OPTION-l">dccifd</A>
+logging can be entirely
+disabled by setting DCCM_LOGDIR="" or DCCIFD_LOGDIR="" in the
+<A HREF="homedir/dcc_conf.in">dcc_conf</A> file in the DCC home directory.
+Logging for individual users can be disabled by not creating or deleting
+thir log directories.
+However, this not only disables logging of rejected mail, but also logging
+of mail that suffered system failures.
+<P>
+To delete old log files, run the
+<A HREF="misc/cron-dccd.in">misc/cron-dccd</A> script
+daily with an entry like <A HREF="misc/crontab.in">misc/crontab</A>
+in the crontab file for the user that runs <A HREF="dccd.html">dccd</A>
+or <A HREF="dccd.html">dccd</A>.
+The DBCLEAN_LOGDAYS parameter in the
+<A HREF="homedir/dcc_conf.in">dcc_conf</A> file in the DCC home directory
+specifies the age of old log files.
+
+
+<P><DT><A NAME="bad-locks">
+Why do my DCC clients including</A>
+<A HREF="cdcc.html">cdcc</A> and <A HREF="dccproc.html">dccproc</A>
+complain about "Resource temporarily unavailable"?
+<DD>
+Perhaps your operating system has bugs in its implementation of
+<CODE>fcntl</CODE> file locking, particularly for the
+DCC client <A HREF="cdcc.html#FILE-map">map</A> file when it is on
+an NFS file system.
+<P>
+Another common case is using an editor such as some versions of <EM>vi</EM>
+that locks files on the main or a per-user
+<A HREF="homedir/whiteclnt">whiteclnt</A> file,
+
+
+<P><DT><A NAME="maxprocs">
+Why does dccifd or dccm complain about
+<Q lang="en-us">thread_create() failed: 11, try again</Q>?
+or <Q lang="en-us">pthread_create(): Cannot allocate memory</Q>?</A>
+<DD>
+The most common cause of
+<Q lang="en-us">thread_create() failed: 11, try again</Q>
+or <Q lang="en-us">pthread_create(): Cannot allocate memory</Q>
+error messages from <A HREF="dccm.html">dccm</A>
+and <A HREF="dccifd.html">dccifd</A>
+is a too small limit on the maximum number of processes allowed
+the UID running the dccm or dccifd process.
+The "maxproc" limit seen with the `limit` or `limits` shell command
+should be a dozen or so larger than the sum of
+the queue sizes of dccm or dccifd (or both if both are running).
+<P>
+See also the common question and answer about
+<A HREF="#max-work">too many simultaneous mail messages</A>.
+
+
+<P><DT><A NAME="max-work">
+Why does dccm or dccifd complain about
+<Q lang="en-us">too many simultaneous mail messages</Q>?</A>
+<DD>
+Dccm or dccifd can fail to create a thread to deal with an incoming
+mail message if there are no available file descriptors or
+other resources.
+Adding <EM>-d</EM> to DCCD_ARGS or DCCIFD_ARGS in
+<A HREF="homedir/dcc_conf.in">dcc_conf</A> in the DCC home directory
+sends a message to the system log that includes the limit on simultaneous mail
+messages and its source, such as a process resource limit on the
+number of file descriptors.
+<P>
+Another common limit is the maximum number of file descriptors
+allowed by the <EM>select</EM> system call.
+This limit can be escaped by building the sendmail milter library to
+use the <EM>poll</EM> system call.
+
+
+
+
+<P><DT><A NAME="server-pick">
+Why doesn't my DCC client pick my local DCC server?</A>
+<DD>
+The DCC clients including <A HREF="dccm.html">dccm</A>
+and <A HREF="dccproc.html">dccproc</A> pick the nearest and fastest
+server in the list kept in the <A HREF="cdcc.html#FILE-map">@prefix@/map</A>
+file.
+DCC servers not in that list will not be used.
+That list can be viewed with the
+<A HREF="cdcc.html#OPERATION-info">cdcc&nbsp;info</A>
+or
+<A HREF="cdcc.html#OPERATION-RTT">cdcc&nbsp;RTT</A> operations.
+Add to the list with
+<A HREF="cdcc.html#OPERATION-add">cdcc&nbsp;add</A>
+or <A HREF="cdcc.html#OPERATION-load">cdcc&nbsp;load</A>.
+<P>
+A nearby server that seems slower than a more distant server will
+not be chosen.
+The anonymous user delay set with <A HREF="dccd.html#OPTION-u">dccd&nbsp;-u</A>
+is intended to make a server appear slow to "freeloaders."
+The "RTT +/-" value that can be used with
+the <A HREF="cdcc.html#OPERATION-add">cdcc&nbsp;add</A>
+and <A HREF="cdcc.html#OPERATION-load">cdcc&nbsp;load</A>
+operations can be used to force DCC clients to prefer or avoid servers
+except when absolutely necessary.
+
+
+
+<P><DT><A NAME="IDs1">
+If I have a server-ID, do I need a DCC client-ID, or vice versa?</A>
+<DD>
+DCC <A HREF="dcc.html#Client-and-Server-IDs">server and client-IDs</A>
+serve distinct purposes.
+Servers require server-IDs to identify each other in the floods of checksums
+they exchange and to recognize authorized users of powerful
+cdcc operations such as <A HREF="cdcc.html#OPERATION-stop">stop</A>.
+DCC servers require client-IDs to identify paying clients that should
+be given quicker service that anonymous clients, to refuse reports from
+anonymous clients, or to refuse even to answer queries from anonymous
+clients.
+
+
+<P><DT><A NAME="IDs2">
+Why does my DCC server complain about
+"rejected server-IDs" among flooded checksum reports?</A>
+<DD>
+You have turned on IDS tracing, but do not have a
+<A HREF="dccd.html#FILE-ids">@prefix@/ids</A> file that is complete.
+You don't need and probably will not have a complete file unless you
+are assigning DCC server-IDs.
+<P>Redundant paths among DCC servers exchanging
+or flooding reports of checksums would cause duplicate entries in
+each server's database without the mechanism that depends on every DCC server 
+having a unique server-ID.
+With IDS tracing enabled, <A HREF="dccd.html#OPTION-T">dccd</A> complains
+about server-IDs that are not listed in the local
+<A HREF="dccd.html#FILE-ids">@prefix@/ids</A> file.
+
+<P><DT><A NAME="server-rate-limits">
+Why does my DCC server refuse to accept more than
+50 operations per second?</A>
+<DD>
+A common cause of such problems is one of the DCC server's
+defenses against denial of service attacks.
+A DCC server cannot know anything about anonymous clients,
+or clients using client-ID 1 or without a client-ID and matching password
+from the <A HREF="dccd.html#FILE-ids">@prefix@/ids</A> file.
+As far as your server can know, an anonymous client sending many
+operations is run by an unhappy sender of unsolicited bulk mail trying
+to flood your server with a denial of service attack.
+It is easy to tell your client its ID with the
+<A HREF="cdcc.html#OPERATION-add">cdcc add</A>
+or <A HREF="cdcc.html#OPERATION-load">load</A> operations.
+<P>
+The default limits can changed by
+adding an <A HREF="dccd.html#OPTION-R">dccd&nbsp;-R</A> argument
+can be added to DCCD_ARGS in the
+<A HREF="homedir/dcc_conf.in">dcc_conf</A> file in the DCC home directory,
+
+
+<P><DT><A NAME="private-server">
+How do I keep strangers from using my DCC server?</A>
+<DD>
+See the <A HREF="dccd.html#OPTION-u">dccd&nbsp;-u</A> option.
+
+
+<P><DT><A NAME="dccm-log1">
+How can I determine why</A> <A HREF="dccm.html">dccm</A> reported
+a message as spam or with a recipient count of "MANY"?
+<DD>
+Dccm is usually configured to log mail with recipient counts greater
+than the <A HREF="dccm.html#OPTION-t">-t&nbsp;,log-thold,</A>
+as well as mail with some conflicts among
+<A HREF="dcc.html#White-and-Blacklists">whitelist</A> entries.
+Each log file contains a single message, its checksums, its disposition,
+and other information as described in the
+<A HREF="dccm.html#FILE-logdir">dccm man page</A>.
+<P>
+See also the <A HREF="dblist.html#OPTION-C">dblist&nbsp;-C</A> command.
+
+
+<P><DT><A NAME="dblist1">
+How can I see what checksums my server has heard from its clients?</A>
+<DD>
+The <A HREF="dblist.html#OPTION-v">dblist&nbsp;-Hv</A>
+command displays the contents of the database.
+Look for records with your
+<A HREF="dcc.html#Client-and-Server-IDs">server-ID</A>
+with <A HREF="dblist.html#OPTION-I">dblist&nbsp;-I</A>.
+
+
+<P><DT><A NAME="whitelist13">
+How do I stop DCC false positives?</A>
+<DD>
+You are probably not seeing false positives.
+The Distributed Checksum Clearing Houses detect both solicited
+and unsolicited bulk mail, while spam is only unsolicited bulk email.
+For your DCC client, <A HREF="dccm.html">dccm</A>,
+<A HREF="dccifd.html">dccifd</A>, or
+<A HREF="dccproc.html">dccproc</A>, to know to ignore bulk mail messages
+that are solicited, it must be told by entries the main or a per-user
+whitelist or <A HREF="homedir/whiteclnt">whiteclnt</A> file.
+
+
+
+<P><DT><A NAME="whitelist1">
+Why is mail from my favorite mailing list marked with an
+<I>X-DCC</I> header line that says it is spam?</A>
+<DD>
+Sources of solicited bulk mail including mailing lists to which
+you have subscribed should usually be in your DCC client
+<A HREF="dcc.html#White-and-Blacklists">whitelist</A>
+so that they receive no <I>X-DCC</I> header lines.
+
+
+<P><DT><A NAME="whitelist11">
+Why are acknowledgments of spam reports mistakenly marked as spam by DCC?</A>
+<DD>
+There is probably no mistake.
+DCC detect bulk mail and not only unsolicited bulk mail.
+Whether a bulk message is spam depends on whether you solicited or asked for it.
+Some INTERNET service providers have sent literally millions of
+acknowledgments of spam reports, which makes them bulk mail.
+Bulk mail you want to receive should be
+<A HREF="dcc.html#White-and-Blacklists">whitelisted</A>
+in your master or per-user
+<A HREF="homedir/whiteclnt">whiteclnt</A> file.
+
+
+<P><DT><A NAME="x-dcc-header1">
+Why are some checksums missing from my <I>X-DCC</I> header lines?</A>
+<DD>
+If the DCC client was not able to compute a checksum for a message,
+it will not ask the server about that checksum and the checksum will
+not appear in the <I>X-DCC</I> header.
+For example, if <A HREF="dccproc.html">dccproc</A> is not told and
+cannot figure out the IP address of the source of the message,
+that checksum will be missing.
+The <I>Fuz1</I> and <I>Fuz2</I> checksums cannot be computed for
+messages that are too small, and so will be missing for them.
+A checksum will also be missing if the DCC server is configured to not count
+it.
+
+
+<P><DT>Do I need both server and client
+<A NAME="whitelist2" HREF="dcc.html#White-and-Blacklists">
+whitelists</A>?
+<DD>
+The <A HREF="homedir/whitelist">server whitelist file</A>
+used explicitly by <A HREF="dbclean.html#FILE-whitelist">dbclean</A>
+and implicitly by <A HREF="dccd.html#FILE-whitelist">dccd</A>
+is not very useful and probably a bad idea.
+<P>
+The <A HREF="homedir/whiteclnt">client whitelist files</A>
+used by
+<A HREF="dccproc.html#FILE-whiteclnt">dccproc</A>,
+<A HREF="dccm.html#FILE-whiteclnt">dccm</A>,
+and
+<A HREF="dccifd.html#FILE-whiteclnt">dccifd</A>
+are generally required.
+Client whitelists apply only to the stream of mail handled by the
+DCC client,
+while server whitelists apply to reports of mail from all DCC clients
+of the DCC server.
+<P>
+<A HREF="dccproc.html">Dccproc</A> is intended for use by individual users
+with programs such as
+<A HREF="http://www.procmail.org/">procmail</A>.
+Because the global whiteclnt file usually found in the DCC home directory
+is as likely to be used as a private file,
+the file name must be explicitly specified with
+<A HREF="dccproc.html#OPTION-w">dccproc&nbsp;-w&nbsp;whiteclnt</A>.
+A perhaps inconvenient implication is programs such as
+<A HREF="http://spamassassin.apache.org/">SpamAssassin</A> that
+switch unpredictably between dccproc and <A HREF=dccifd.html>dccifd</A>
+might get inconsistent results unless they invoke dccproc with the global
+whiteclnt file.
+
+
+<P><DT><A NAME="whitelist9"></A>
+How do I maintain client
+<A HREF="dcc.html#White-and-Blacklists">whitelists</A>?
+<DD>
+Start by monitoring bulk mail in the
+global log directories specified with
+<A HREF="dccproc.html#OPTION-l">dccproc&nbsp;-l</A>
+and with DCCM_LOGDIR and DCCM_USERDIRS in the
+<A HREF="homedir/dcc_conf.in">@prefix@/dcc_conf</A> file
+for <A HREF="dccm.html#OPTION-l">dccm</A>,
+and
+<A HREF="dccifd.html#OPTION-U">dccifd</A>.
+Then add entries to whitelist files.
+<P>
+The global
+<A HREF="homedir/whiteclnt">@prefix@/whiteclnt</A> file
+and the whitelists specified with
+<A HREF="dccproc.html#OPTION-w">dccproc&nbsp;-w</A> are maintained
+with ordinary text editors.
+<P>
+Per-user whitelists in whiteclnt files
+specified with DCCM_USERDIRS in the
+<A HREF="homedir/dcc_conf.in">@prefix@/dcc_conf</A> file
+are easily maintained with ordinary text editors by the system administrator.
+However, it is often better to let individual users deal with their
+own whitelists.
+The DCC source includes sample CGI scripts
+in the <A HREF="cgi-bin/">cgi-bin directory</A> in the DCC source
+to let individual end-users monitor their private logs of bulk mail
+and their individual whitelists.
+See the <A HREF="cgi-bin/README">README</A> file for those scripts.
+There is also a
+<A HREF="http://www.rhyolite.com/dcc/#cgi-demo">demonstration</A>
+of the cgi scripts.
+
+
+<P><DT><A NAME="whitelist3"></A>
+When the <A HREF="homedir/whiteclnt">whitelist file</A>
+used by <A HREF="dccm.html#FILE-whiteclnt">dccm</A>,
+<A HREF="dccd.html#FILE-whitelist">dccd</A>,
+or <A HREF="dccifd.html#FILE-whiteclnt">dccifd</A>
+is changed,
+what must be done to tell the software about the change?
+<DD>
+The DCC clients notice when their whiteclnt files
+as well as included files change and automatically rebuild the corresponding
+<A HREF="dccm.html#FILE-whiteclnt.dccw">.dccw hash table</A> files.
+<P>
+Changes to the DCC server or dccd
+<A HREF="dccd.html#FILE-whitelist">whitelist</A>
+are not effective until after <A HREF="dbclean.html">dbclean</A> is run.
+<P>
+Some text editors including versions of <EM>vi</EM> lock their files.
+<A HREF="dccm.html#FILE-whiteclnt">Dccm</A>,
+<A HREF="dccproc.html#FILE-whiteclnt">dccproc</A>,
+and <A HREF="dccifd.html#FILE-whiteclnt">dccifd</A>
+are unable to read whitelist files while they are locked.
+
+
+<P><DT><A NAME="whitelist14">
+How do I test a whiteclnt file?</A>
+<DD>
+An easy way to test a DCC client whitelist or
+<A HREF="homedir/whiteclnt">whiteclnt</A> file
+is to feed dccproc with a test message.
+For example, the following shell script would test whether the IP address
+127.0.0.1
+and the SMPT envelope Mail_From value postmaster@example.com are in the
+<EM>whiteclnt</EM> file in the DCC home directory:
+<PRE>
+        #!/bin/sh
+        /usr/local/bin/dccproc -QCw whiteclnt \
+                -a 127.0.0.1 -f postmaster@example.com &lt;&lt;EOF
+        Message-ID: <1234@example.com>
+
+        text
+        EOF
+</PRE>
+If the script produces something like
+<PRE>
+        X-DCC--Metrics: calcite.rhyolite.com; whitelist
+                            reported: 0               checksum         wlist
+                       IP: e475b896 492c60fc efecb432 6e29e3c5            ok
+                 env_From: bef98dc1 cc6ea4d7 b8daf07c a2bfbc9e
+               Message-ID: 26573398 2ab927cd 681a89fa e502496d
+</PRE>
+then you know that SMTP client IP (mail sender) IP address 127.0.0.1
+is whitelisted, but the SMTP envelope Mail_From value is not.
+
+
+<P><DT>
+Can I use wild cards or regular expressions in DCC
+<A NAME="reg-exps1" HREF="dcc.html#White-and-Blacklists">
+whitelists</A>?
+<DD>
+No, regular expressions cannot be used,
+because DCC client and server whitelists are converted to lists of checksums.
+The same basic idea is used for DCC client whitelists
+as for the DCC protocol.
+A DCC client computes the checksums for a message, and then looks
+for those checksums in the local whitelist.
+Depending on the values associated with those checksums,
+the DCC client asks a DCC server about them.
+<P>
+To use regular expressions with the DCC, consider procmail.
+Procmail is included with many UNIX-like systems.
+See also the
+<A HREF="http://www.procmail.org/">Procmail Homepage</A>.
+<P>
+DCC clients can be configured to white- or blacklist
+using called "substitute" headers.
+See <A HREF="dccproc.html#OPTION-S">dccproc&nbsp;-S</A> or
+<A HREF="dccm.html#OPTION-S">dccm&nbsp;-S</A>.
+<P>
+It is also possible to use a sendmail access_db file entries to
+white- or blacklist based on portions of SMTP envelope and
+client IP addresses.
+For example, an access_db file line of "From:example.com OK"
+can be used to tell dccm to whitelist all mail from SMTP clients
+in the example.com domain.
+See the -O argument to the
+<A HREF="misc/hackmc">misc/hackmc</A> script.
+
+
+<P><DT>
+<A NAME="whitelist10">How do I whitelist mail from a legitimate
+bulk mailer using its name or SMTP headers such as Mailing-List
+headers?</A>
+<DD>
+Start by determining an envelope value or SMTP header that distinguishes
+the bulk mail from a sample message or DCC log file.
+The name of the sending computer is the <EM>mail_host</EM> value in
+<A HREF="dccm.html#FILE-logdir">dccm log files</A>.
+If the distinguishing header or envelope value is not among the main
+<A HREF="dcc.html#White-and-Blacklists">DCC whitelist values</A>,
+then a "substitute" value must be used.
+An "ok substitute ..." line must be added to the whitelist file
+and the DCC client program must be told with
+<A HREF="dccproc.html#OPTION-S">dccproc&nbsp;-S</A> or
+<A HREF="dccm.html#OPTION-S">dccm&nbsp;-S</A>.
+There are example whitelist entries in the sample
+<A HREF="homedir/whiteclnt">@prefix@/whiteclnt</A> file.
+<P>
+
+<P><DT><A NAME="incompat-whitelists">
+Why does dccm or dccifd complain about "incompatible whitelists"?</A>
+<DD>
+There are several points during an SMTP transaction when an SMTP server
+can reject a mail message.
+Early points are when the SMTP client specifies the recipients of the
+mail message.
+The last point is after the entire message has been received by the SMTP
+server.
+Spam filters that check mail message bodies must wait until that last point.
+The SMTP protocol does not allow an SMTP server to reject the
+mail message for only some recipients.
+The SMTP server must tell the SMTP client that the message has been
+accepted for all or rejected for recipients.
+This is a problem when the recipients of a single mail message have
+differing
+<A HREF="dcc.html#White-and-Blacklists">DCC thresholds or other parameters</A>
+in their individual whitelist files
+that require that the mail message be delivered to some mailboxes but
+rejected for other mailboxes.
+<P>
+The DCC client programs solve this conflict in one of two ways.
+One is telling the SMTP client
+that the mail message has been accepted for all recipients and then
+discarding instead of delivering the message for mailboxes with parameters
+that make it spam.
+This solution has the disadvantage of not informing senders of the
+refusal to deliver the message.
+The other solution is to temporarily reject recipients with possibly
+incompatible parameters early in the SMTP transaction with the same
+SMTP error status number as too many recipients for a single SMTP transaction.
+This second solution has the advantage of ensuring that senders know
+when their mail is rejected but the disadvantage of sometimes
+requiring as many SMTP transactions as there are recipients for a mail message.
+<P>
+Which solution is used is determined by the
+<A HREF="dcc.html#White-and-Blacklists">forced-discard-ok</A>
+and <A HREF="dcc.html#White-and-Blacklists">forced-discard-nok</A>
+settings in the global and per-user
+<A HREF="dccm.html#FILE-whiteclnt">whiteclnt</A> files.
+Unless all recipients for a mail message agree on the first solution,
+perhaps by <EM>forced-discard-ok</EM> in the main
+<A HREF="homedir/whiteclnt">whiteclnt</A> file,
+the second solution is used.
+
+
+<P><DT><A NAME="whitelist4">
+Why do legitimate mail messages have
+<I>X-DCC</I> header lines that say they are "bulk", "many", or spam?</A>
+<DD>
+There are several possible causes of such problems.
+The first and most obvious is that the mail is solicited bulk mail
+and that the source needs to be added to your
+<A HREF="dcc.html#White-and-Blacklists">whitelist</A>.
+
+<P>Another possible reason is that your individual legitimate mail messages
+have not been marked as spam because their <I>Body</I> or <I>Fuz1</I>
+checksum counts are small, but that the IP address or other checksum
+counts are large.
+The IP address checksum count, for example, is the total of all reports
+of addressees for that checksum.
+That total is independent of the other checksums, and so counts
+all reports for all messages with that source IP address.
+A source of legitimate mail that has sent a message that was reported
+as spam by one of its recipients will often have the totals
+for the checksums of its IP address, From header, and
+other values be <I>MANY</I>.
+This is why it usually does not make sense to reject mail based on what the
+DCC server reports for the IP address, From header, and other values that
+are not unique to the message.
+Only the last Received header line, the Message-ID line, and body checksums
+can be expected to be unique and sometimes not the Message-ID
+and Received header lines.
+
+<P><DT><A NAME="qmail2">
+Why is legitimate mail from someone using <I>qmail</I>
+marked as spam?</A>
+<DD>
+A common cause for that and similar complaints involves
+null or missing Message-ID header lines.
+Spam often lacks Message-ID lines or has a null or "&lt&gt" ID,
+so rejecting mail with null or missing Message-IDs can be an
+effective filter.
+DCC clients treat missing Message-ID lines as if they were present but null.
+The sample <A HREF="homedir/whiteclnt">@prefix@/whiteclnt</A>
+<A HREF="dcc.html#White-and-Blacklists">whitelist</A> file in the DCC source
+includes the line:
+<PRE>
+        many    message-id &lt;&gt;
+</PRE>
+Some Mail Transfer Agents violate section 3.6.4 of RFC 2822 and
+do not include Message-ID header lines in mail they send,
+including some combinations of qmail and
+"<B>sendmail&nbsp;-bs</B>" acting as the originating MTA,
+and qmail by itself when it is generates a non-delivery message or "bounce."
+Solutions to this problem include removing that line from your
+<A HREF="dcc.html#White-and-Blacklists">whitelists</A>
+or adding lines specifying the From or envelope
+from values of senders of legitimate mail lacking Message-ID header lines.
+
+
+<P><DT><A NAME="whitelist5"></A>
+Are <A HREF="dcc.html#White-and-Blacklists">IP address blocks</A>
+in <A HREF="homedir/whiteclnt">whitelists</A> used by
+<A HREF="dccproc.html">dccproc</A>?
+<DD>
+Yes, <A HREF="dccproc.html">dccproc</A> can whitelist mail
+by the IP address of the immediately
+preceding SMTP client,
+but only if it knows that IP address.
+Unless the <A HREF="dccproc.html#OPTION-a">dccproc&nbsp;-a</A>
+or <A HREF="dccproc.html#OPTION-R">dccproc&nbsp;-R</A>
+options are used, dccproc does not know the IP address.
+
+
+<P><DT><A NAME="whitelist6">
+Why is</A> <A HREF="dccproc.html">dccproc</A> is ignoring
+<A HREF="dcc.html#White-and-Blacklists"><I>env_from</I> whitelist</A>
+entries?
+<DD>
+DCC checksums are of the entire header line or envelope value.
+An entry in the whitelist file for <I>jsmith@example.com</I>
+will have no effect on mail with an envelope value of
+<I>"J.Smith"&nbsp;jsmith@example.com</I>.
+The file must contain <I>"J.Smith"&nbsp;jsmith@example.com</I>.
+<P>
+Another common cause for this problem is implied by the fact that
+for an <I>env_from</I> whitelist entry
+to have any effect, dccproc must be able to find the envelope value
+in the message in a <I>Return-Path</I> header,
+an old UNIX-style <I>From_</I> header, or an <B>-f</B> argument.
+If your mail delivery agent does not add a <I>Return-Path</I> header
+and you do not use
+<A HREF="dccproc.html#OPTION-f">dccproc&nbsp;-f</A>,
+then dccproc cannot know about
+white or blacklist entries for envelope return addresses.
+<P>
+Note also that dccproc has no whitelist by default and
+that <A HREF="dccproc.html#OPTION-w">dccproc&nbsp;-w</A>
+must be used.
+
+
+<P><DT><A NAME="delck">
+What if I make a mistake with</A>
+<A HREF="dccproc.html#OPTION-t">dccproc&nbsp;-t&nbsp;many</A>
+and report legitimate mail as spam?
+<DD>
+It is possible to delete checksums from the distributed DCC
+database with the <A HREF="cdcc.html#OPERATION-delck-type-hex1-hex2-hex3-hex4">
+cdcc&nbsp;delck</A>
+operation.
+However, it is not worth the trouble.
+Unless the same (as far as the fuzzy checksums are concerned) message
+is sent again, no one is likely to notice the mistake before the
+report of the message's checksums expire from the DCC servers'
+databases for lack of repetition.
+
+
+<P><DT><A NAME="whitelist8">
+Can the sendmail "spamfriend" mechanism tell</A>
+<A HREF="dccm.html">dccm</A> to not check mail sent to some addresses?
+<DD>
+Sendmail decisions to accept, reject, or discard mail are largely
+independent of the decisions made by dccm.
+The DCC equivalent is to add
+<A HREF="dcc.html#White-and-Blacklists">env_to</A> entries to the
+<A HREF="dccm.html#FILE-whiteclnt">dccm whitelist</A>.
+See the sample <A HREF="homedir/whiteclnt">@prefix@/whiteclnt</A> file in the
+DCC source
+<P>
+However, if your sendmail.cf file sets the
+<I>dcc_notspam</I> macro while processing the
+envelope, then the message will by whitelisted.
+This is related to the <I>dcc_isspam</I> macro
+used by sendmail.cf modified by <A HREF="misc/hackmc">misc/hackmc&nbsp;-R</A>
+to tell dccm to report blacklisted messages as spam to the DCC server.
+
+
+<P><DT><A NAME="whitelist12">
+How do I tell</A> <A HREF="dccm.html">dccm</A>
+to not check mail for an entire domain?
+<DD>
+To whitelist all mail addressed to mailboxes in a domain,
+add the following line to the sendmail access_DB file and rebuild
+the database with the sendmail tool, <I>makemap</I>:
+<PRE>
+        To:domain.com       DCC:OK
+</PRE>
+<P>
+You can apply finer control by adding
+a third argument to the FEATURE(dcc) macro in your sendmail.mc file
+as described in
+<A HREF="misc/dcc.m4.in">misc/dcc.m4</A>.
+All mail for the domain can use a single "per-user"
+<A HREF="homedir/whiteclnt">whiteclnt</A> file,
+often in the @prefix@/userdirs/esmtp/example.com, where @prefix@/userdirs
+is the default value for <EM>DCCM_USERDIRS</EM>in the DCC configuration file
+<A HREF="homedir/dcc_conf.in">@prefix@/dcc_conf</A>.
+Making @prefix@/userdirs/esmtp a symbolic link to @prefix@/userdir/local
+can be handy.
+
+
+<P><DT><A NAME="false-positives">
+How can I avoid polluting databases of DCC servers with
+checksums of my mail that is not spam?</A>
+<DD>
+Reports of checksums with
+<A HREF="dcc.html#White-and-Blacklists">whitelist</A>
+entries in your server's database are not flooded to its peers.
+The checksums of messages whitelisted with entries in local
+<A HREF="dccm.html">dccm</A> or <A HREF="dccproc.html">dccproc</A>
+whitelists are not reported to DCC servers.
+It is good to add entries to DCC server and client
+<A HREF="dcc.html#White-and-Blacklists">whitelists</A>
+for localhost, your IP address blocks, and your domains if
+you know that none of your users will ever send spam.
+<P>
+However, in the common mode in which the DCC is used, no
+checksums of mail are pollution.
+Checksums of genuinely private mail will have target counts of
+1 or a small number, and so will not be flooded by your server to
+other servers.
+Strangers will not see your private mail and so will not be able
+to ask any DCC server about the checksums of your private mail.
+On the other hand, the DCC functions best by collecting reports
+of the receipt of bulk mail as soon as possible.
+That implies that it is generally desirable
+to send reports of all mail to a DCC server.
+The DCC flooding protocol does not send checksums with counts
+below 10 <!--fix if BULK_THRESHOLD changes-->
+to other servers.
+
+
+<P><DT><A NAME="spamtrap">
+Can DCC be fed with <Q lang="en-us">spam traps</Q>?</A>
+<DD>
+A spam trap is a mail address that should practically
+never receive legitimate mail,
+and that treats any mail that it does receive as spam.
+A spam trap might a common name such as
+<Q lang="en-us">user1</Q> that has never been valid
+and is discovered by unsolicited bulk email
+advertisers by <Q lang="en-us">dictionary attacks</Q> or guessing.
+It might instead be an address hidden in a web page
+or a mailbox of an account that has been disabled for many months.
+<P>
+Any spam trap might receive legitimate mail.
+For example, a spam trap that differs from an ordinary mailbox by a
+single character might receive mail intended for the ordinary mailbox.
+It might be best for a system to reject mail sent to such a trap so
+that legitimate mail senders know that their messages have gone astray.
+A mailbox that is a long string of arbitrary letters and digits is much
+less likely to receive legitimate messages and so might best accept
+all messages without complaint.
+<P>
+There are several ways to connect
+<Q lang="en-us">spam trap</Q> mailboxes to DCC:
+<DL>
+<DT><A HREF="dccproc.html">dccproc</A>
+<DD>
+For example,
+<PRE>dccproc -R -tMANY -cCMN,MANY -o/dev/null</PRE>
+will accept a message on STDIN,
+look for the IP address of the sender among
+<Q lang="en-us">Received:</Q> SMTP fields,
+reports the message to the DCC server as spam and the IP address as the sender,
+and exit with the default value of
+<A HREF="dccproc.html#OPTION-x">dccproc&nbsp;-x</A>.
+<P>
+<DT>dccif-test
+<DD>
+dccif-test was written to test the interface to the DCC interface daemon,
+<A HREF="dccifd.html">dccifd</A>.
+When wired to a spam trap, it is more efficient than dccproc.
+For example,
+<PRE>dccif-test -cclnt-IP-addr -oSPAM -O/dev/null</PRE>
+will do much the same as the dccproc example above.
+<P>
+<DT><A HREF="dcc.html#White-and-Blacklists">whiteclnt file</A> option line
+<DD>
+The best way to build a spam trap is with a
+per-user <A HREF="dccm.html#OPTION-w">whiteclnt file</A>
+with an
+<EM>option spam-trap-accept</EM> or <EM>option spam-trap-reject</EM>
+line.
+<P>
+With sendmail, virtual user mapping can be used to send mail to invalid
+mailboxes to a single mailbox whose corresponding DCC per-user
+whiteclnt file contains an
+<EM>option spam-trap-accept</EM> or <EM>option spam-trap-reject</EM>
+line.
+</DL>
+
+
+<P><DT><A NAME="flood3">
+How many flooding peers does my DCC server need?</A>
+<DD>
+A single flooding peer delivers all reports of checksums of bulk
+mail seen by any DCC server.  Additional peers provided reports
+sooner and so help the clients of a peer detect spews of spam sooner.
+However, more peers will cause more reports to be duplicates.
+<P>
+A DCC server in a network of many servers should have at least three
+flooding peers to ensure that the failure of a single server or network
+link cannot partition the network.
+Limiting the number the number of peers of any server to four or perhaps
+a few more ensures that no single server is critical to the network.
+To minimize the distances in the network, four peers
+per server seem necessary.
+<P>
+An organization with more than one server can be viewed as a single
+server by other organizations, with its servers flooding each other
+and external peers spread among its servers.
+This protects the network should the organization suffer large scale problems
+while protecting the organization from single points of failure.
+
+
+<P><DT><A NAME="flood1">
+Do I need to tell the operators of other DCC servers
+the password for controlling my server to turn on flooding?</A>
+<DD>
+No, you do not need to and generally should not tell other DCC server
+operators the passwords for controlling your server with
+the <A HREF="cdcc.html">cdcc</A> command.
+Every Inter-server flood of checksums is authorized by lines in
+each server's <A HREF="dccd.html#FILE-flod">@prefix@/flod</A> file
+and authenticated by the password associated with the
+<A HREF="dccd.html#FILE-flod">passwd-ID</A> in those lines.
+The passwd-ID is a <A HREF="dcc.html#Client-and-Server-IDs">server-ID</A>
+defined in the <A HREF="dccd.html#FILE-ids">@prefix@/ids</A> file
+that should generally be used only to authenticate floods of checksums.
+
+
+<P><DT><A NAME="flood2">
+How can I figure out why flooding is not working?</A>
+<DD>
+Many DCC server problems can be diagnosed by turning
+on one or more of the tracing modes in the server with the
+<A HREF="cdcc.html#OPERATION-trace">cdcc&nbsp;trace</A> operation
+or by restarting the server with
+<A HREF="dccd.html#OPTION-T">dccd&nbsp;-T</A>.
+<P>
+The <A HREF="cdcc.html#OPERATION-flood-list">cdcc flood list</A>
+operation displays the current flooding peers of a DCC server.
+Counts of checksum reports sent and received to and from
+a single peer can be displayed with
+<A HREF="cdcc.html#OPERATION-flood-stats">cdcc&nbsp;"flood stats ID"</A>
+<P>
+The positions in the local database of outgoing streams of checksums
+are displayed by the start of <A HREF="dblist.html">dblist&nbsp;-Hv</A>.
+
+
+<P><DT><A NAME="rtt">
+Why didn't the RTT reported by the</A>
+<A HREF="cdcc.html#OPERATION-info">cdcc&nbsp;info</A> operation
+change when my network topology changed?
+<DD>
+The RTT or round trip time is an average value.
+Changes in network topology, server load, and so forth are not
+immediately reflected in the RTT to avoid switching DCC servers
+too frequently.
+
+
+<P><DT><A NAME="socks1">
+When my clients are configured to use SOCKS, they do not
+realize immediately when a server is down.</A>
+<DD>
+When configured to use SOCKS, DCC clients cannot "connect"
+to a server and so do not receive ICMP errors and must wait for
+timeouts to know the server is not answering.
+
+
+</DL>
+
+<P>
+<HR>
+<P class=small>
+This document describes DCC version 1.3.103.
+<P>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG SRC="http://logos.dcc-servers.net/border.png"
+            class=logo ALT="DCC logo">
+    </A>
+<A HREF="http://validator.w3.org/check?uri=referer">
+    <IMG class=logo ALT="Valid HTML 4.01 Strict"
+        SRC="http://www.w3.org/Icons/valid-html401">
+    </A>
+</BODY>
+</HTML>
+<!--  LocalWords:  dccproc libmilter pthreads procmail dccm dccd DCC libmilter
+ -->
+<!--  LocalWords:  homedir dbclean setenv nbsp Solaris crontab Linux ICMP flod
+ -->
+<!--  LocalWords:  gmake FreeBSD NetBSD CFLAGS PTHREAD LDFLAGS LIBS HPUX IDs DT
+ -->
+<!--  LocalWords:  cdcc DL DD ids var RTT TD TR whiteclnt dccifd whitelist MTA
+ -->
+<!--  LocalWords:  hackmc busters whitelisted dblist SpamAssassin
+ -->
+<!--  LocalWords:  ARGS
+ -->
diff -r 000000000000 -r c7f6b056b673 INSTALL.html.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/INSTALL.html.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,980 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<HTML>
+<HEAD>
+    <TITLE>DCC Installation</TITLE>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <META HTTP-EQUIV="Content-Style-Type" content="text/css">
+    <STYLE type="text/css">
+        <!--
+        BODY {background-color:white; color:black}
+        TABLE.centered {margin-left: auto; margin-right: auto; }
+        TD {font-size:80%}
+            TD.env {font-family:monospace}
+            TD.conf {font-family:monospace}
+        DL.compat {margin-left:5%; margin-right:10%}
+        .small {font-size:smaller}
+        SUP {font-size:smallest}
+        IMG.logo {width:6em; vertical-align:middle}
+        -->
+    </STYLE>
+</HEAD>
+
+<BODY>
+<H1>Distributed Checksum Clearinghouse (DCC) Installation</H1>
+
+<P>
+<OL>
+<LI><H3>Fetch the Source and Read the License</H3>
+The DCC source is available at
+<A HREF="http://www.dcc-servers.net/dcc/">dcc-servers.net</A>
+and
+<A HREF="http://www.rhyolite.com/dcc/">Rhyolite Software</A>.
+<P>
+Please <EM>do not</EM> try to use the more than 3 year old modified
+versions of DCC software distributed by some Linux packagers.
+Those versions do not detect bulk mail as well as more recent versions.
+Installations using those old versions also have problems using the
+public DCC servers that often make it necessary to add their IP addresses
+to the blacklist that protects the public DCC servers.
+Even worse, all known Linux redistributions of DCC software have been
+changed in ways that break things, including the
+<A HREF="misc/updatedcc.in">libexec/updatedcc</A> shell script that could
+otherwise be used to fetch, configure, compile, install, and restart
+a current version.
+<P>
+The license on the free source is in the source as well as
+<A HREF="http://www.dcc-servers.net/dcc/dcc-tree/LICENSE">dcc-servers.net</A>
+and
+<A HREF="http://www.rhyolite.com/dcc/dcc-tree/LICENSE">Rhyolite
+Software</A>.
+The free license is intended to cover individuals and organizations
+including Internet service providers using DCC to filter their own mail.
+Organizations selling anti-spam appliances or managed mail services are
+not eligible for the free license.
+
+
+<P><LI><H3>Read the Documentation</H3>
+<P>
+The <A HREF="dcc.html#Installation-Considerations">DCC</A>
+and other man pages describe the features, operating modes,
+required data files, and other characteristics of the DCC.
+Also see the DCC <A HREF=FAQ.html>FAQ</A>
+or list of frequently answered questions.
+
+
+<P><LI><H3><A NAME="step-sendmail">Build Sendmail</A></H3>
+If the DCC-sendmail interface, <A HREF="dccm.html">dccm</A>, is not used,
+then <B>skip</B> to the <A HREF="#step-compile">next step</A>.
+<P>
+Sendmail must have the
+Mail Filter API or Milter enabled.
+Some systems such a FreeBSD 4.6 and newer are shipped with
+Milter enabled and the library installed by default.
+If your system comes with the Milter interface turn on,
+then <B>skip</B> to the <A HREF="#step-compile">next step</A>.
+Otherwise, the Milter interface must be explicitly enabled
+by adding lines like those in
+<A HREF="misc/site.config.m4">misc/site.config.m4</A>
+to your sendmail/devtools/Site/site.config.m4 file or equivalent.
+Then build sendmail as described in the INSTALL file distributed with sendmail.
+You must build <CODE>libmilter</CODE> separately by something like
+<PRE>
+            cd libmilter
+            sh ./Build
+</PRE>
+<P>
+After sendmail has been rebuilt if necessary it will need to be restarted.
+That should be done after <A HREF="#step-compile">the next step</A>
+after <A HREF="misc/dcc.m4">misc/dcc.m4</A> has been created by the
+<EM>./configure</EM> script.
+
+
+<P><LI><H3><A NAME="step-compile">Configure, Build, and
+Install the DCC Programs</A></H3>
+<P>
+See the installation considerations in the
+<A HREF="dcc.html#Installation-Considerations">DCC man page</A>.
+<P>
+Most DCC files are in a "home directory" such as @prefix@.
+DCC programs such as cdcc and dccproc are run by end
+users and should be installed in a directory such as /usr/local/bin.
+They must also be set-UID to the UID that can change the DCC
+data files.
+DCC programs that do not need to be run by end users
+are installed by default in the libexec subdirectory of the DCC home directory.
+See the <A HREF="#envtbl">table</A> of
+<EM>./configure</EM> script and makefile parameters.
+If necessary, set CFLAGS, LDFLAGS, LIBS or other environment variables
+listed in the <A HREF="#envtbl">table</A>.
+Omit any parameters you don't really need to change and usually use only:
+<PRE>
+        ./configure
+        make install
+</PRE>
+<P>
+End users installing only <A HREF="dccproc.html">dccproc</A>
+can install it in their private
+<Q>~/bin</Q> directories and use private directories for their DCC
+home directories.
+In this case, the DCC programs that would otherwise need to be set-UID
+need not be.
+<P>
+To build <A HREF="dccproc.html">dccproc</A>
+<A NAME="individual-user">for an individual user</A>,
+use something like
+<PRE>
+        ./configure <A HREF="#envtbl--disable-sys-inst">--disable-sys-inst</A> <A HREF="#envtbl--disable-dccm">--disable-dccm</A> <A HREF="#envtbl--homedir">--homedir=$HOME/dccdir</A>  <A HREF="#envtbl--bindir">--bindir=$HOME/bin</A>
+        make install
+</PRE>
+<P>
+The sendmail interface, <A HREF="dccm.html">dccm</A>,
+must be built with the sendmail source and object tree.
+By default, the makefiles look for a
+native sendmail libraries (e.g. on FreeBSD 4.6), an installed "package"
+(e.g. on FreeBSD), or a directory named sendmail parallel to the DCC
+source and object tree.
+Those who regularly build new versions of sendmail may find it convenient
+to make a symbolic link there to their current sendmail.
+Otherwise configure the dccm makefile with
+<PRE>
+        ./configure <A HREF="#envtbl--with-sendmail">--with-sendmail</A>=/some/where/sendmail
+        make install
+</PRE>
+If dccm does not build because it cannot find libmilter,
+check that libmilter was compiled with sendmail
+in the <A HREF="#step-sendmail">previous step</A>.
+<P>
+To connect the sendmail Milter interface to <A HREF="dccm.html">dccm</A>,
+copy or "sym-link" <A HREF="misc/dcc.m4">misc/dcc.m4</A> to
+your sendmail/cf/feature directory and
+add <CODE>FEATURE(dcc)</CODE> lines to your sendmail.mc configuration file.
+Then rebuild and reinstall your sendmail.cf file, and restart sendmail.
+
+
+<P><LI><H3>Create Client Configuration Files</H3>
+All DCC configuration files are in the DCC  home directory, usually @prefix@.
+See the
+<A HREF="dcc.html#Client-Installation">dcc</A>,
+<A HREF="dccm.html#FILES">dccm</A>,
+<A HREF="dccifd.html#FILES">dccifd</A>,
+and <A HREF="dccproc.html#FILES">dccproc</A>
+man pages
+for the files each needs.
+Example files are in the <A HREF="homedir/">homedir</A> directory in the source.
+
+<UL>
+<LI>Unless run anonymously, DCC clients need client-ID numbers and passwords
+assigned by the operators of the chosen DCC servers in the @prefix@/map file.
+<LI><P>Even if run anonymously, the @prefix@/map file must contain the IP addresses
+of DCC servers.
+If your mail system handles fewer than 100,000 mail messages per day,
+the installation process generates a serviceable @prefix@/map file
+from the included <A HREF="homedir/map.txt">homedir/map.txt</A>.
+That file points to the public DCC servers.
+
+<LI><P>If using remote DCC servers such as the public DCC servers,
+ensure that your firewalls allow outgoing packets to UDP port 6277
+on distant systems and incoming responses from UDP port 6277.
+There is a
+<A HREF="http://www.dcc-servers.net/dcc/firewall.html">description</A>
+one firewall's configuration.
+
+<LI><P>Your MX servers and mail submission clients should be listed in the main
+<A NAME=whitelist HREF="homedir/whiteclnt">whiteclnt</A> file with lines like:
+<PRE>
+    mx          ip  10.2.3.4
+    mx          ip  10.5.6.0/28
+    mxdcc       ip  10.5.6.0/28
+    ok          ip  10.7.8.9
+    submit      ip  192.168.1.0/24
+</PRE>
+If those other systems also run DCC clients, use <EM>MXDCC</EM> instead
+of <EM>MX</EM> so that messages will not be reported twice to the DCC network
+and so have higher target counts,
+and appear to be unsolicited bulk mail.
+<P>
+Use <EM>OK</EM> for mail systems that you trust to never send or
+forward unsolicited bulk mail.
+<P>
+Untrusted SMTP clients such as end users with browsers used as
+MUAs (mail user agents) should be listed in the
+<A HREF="homedir/whiteclnt">whiteclnt</A> file with <EM>submit</EM>.
+
+<LI><P>Sources of legitimate bulk mail must be recorded in whitelists.
+Example <A HREF="homedir/whiteclnt">whiteclnt</A>,
+<A HREF="homedir/whitelist">whitelist</A>, and
+<A HREF="homedir/whitecommon">common</A> whitelists are among
+the <A HREF="homedir/">sample configuration files</A> in the homedir directory.
+The format of DCC whitelists is described in the
+<A HREF="dcc.html#White-and-Blacklists">DCC</A> man page.
+
+<LI><P>Put suitable values in the DCC configuration file,
+<A HREF="homedir/dcc_conf.in">@prefix@/dcc_conf</A> for dccm or dccifd.
+The default client values are usually good for a start and often only
+DCCM_REJECT_AT needs to be changed when it is time to reject spam.
+
+<LI><P>Optionally configure DNS blacklist (DNSBL) checks in
+<A HREF="dccm.html#OPTION-B">dccm</A>
+or
+<A HREF="dccifd.html#OPTION-B">dccifd</A>
+by setting DNSBL_ARGS in in the configuration file,
+<A HREF="homedir/dcc_conf.in">dcc_conf</A>, in the home directory.
+
+
+<LI><P>Optionally create per-user directories for logs and whitelists.
+See also the
+<A HREF="cgi-bin/">CGI scripts</A> that allow users to
+maintain their private whitelists
+and monitor their individual logs of rejected mail.
+
+<LI><P>Install a daily or more frequent cron job like
+<A HREF="misc/crontab.in">misc/crontab</A>
+and
+<A HREF="misc/cron-dccd.in">@libexecdir@/cron-dccd</A>
+to prune <A HREF="dccm.html#FILE-logdir">dccm</A> or
+<A HREF="dccifd.html#FILE-logdir">dccifd</A>
+log files and the prune dccd database with
+<A HREF="dbclean.html">dbclean</A>.
+</UL>
+
+<P><LI><H3>Create Server Files and Start the Server</H3>
+<B><A HREF="#step-greylist">Skip</A></B> this and the next step
+if only remote DCC servers will be used.
+You should use your own, local DCC servers if your mail system handles
+more than 100,000 mail messages per day.
+<P>
+It is best to use remote servers until the DCC client,
+dccm, dccifd, or dccproc, is stable.
+Then
+<UL>
+<LI>Put suitable values for dccd
+ in the configuration file,
+<A HREF="homedir/dcc_conf.in">dcc_conf</A>.
+Every DCC server requires a unique
+<A HREF="dcc.html#Client-and-Server-IDs">server-ID</A>.
+Obtain a server-ID by contacting Vernon Schryver
+<A HREF="mailto:vjs@rhyolite.com">vjs@rhyolite.com</A> by email
+or via a
+<A HREF="http://www.rhyolite.com/cgi-bin/ct.cgi?sb=DCC+server-ID">web
+form</A>.
+
+<LI><P>Choose a secret password for your server-ID in your
+<A HREF="homedir/ids">@prefix@/ids file</A>.
+This password can be used to control your server remotely.
+
+<LI><P>Start the server with the system by installing
+<A HREF="misc/rcDCC.in">@libexecdir@/rcDCC</A> or an equivalent.
+If it is used unchanged, rcDCC is best installed with a symbolic link
+to automate installing updates.
+The server can be started manually with
+<PRE>
+        rcDCC start
+</PRE>
+
+<LI><P>The script
+<A NAME=cleaning HREF="misc/cron-dccd.in">@libexecdir@/cron-dccd</A>
+<EM>must</EM> be used to run
+<A HREF="dbclean.html">dbclean</A> about once a day.
+An entry like <A HREF="misc/crontab.in">misc/crontab</A> can be put into
+the crontab file for the user that runs dccd.
+If you have more than one DCC server,
+stagger the times at which the cron job is run so
+that not all of your servers are simultaneously busy cleaning databases.
+
+<LI><P>Install the shutdown script
+<A HREF="misc/rcDCC.in">@libexecdir@/rcDCC</A>
+to shut down the DCC server as the operating system stops.
+If the DCC server fails to close the database cleanly,
+the database must be cleaned by the server with it starts.
+That takes time.
+
+</UL>
+
+
+
+<P><LI><H3><A NAME="step-flooding">Configure Flooding</A></H3>
+<A HREF="#step-greylist">Skip to the next step</A>
+if only remote DCC servers will be used.
+<P>
+Flooding requires that every server participating in a network of DCC
+servers have a unique server-ID.
+Server-IDs can be obtained by contacting Vernon Schryver
+<A HREF="mailto:vjs@rhyolite.com">vjs@rhyolite.com</A> by email
+or via a
+<A HREF="http://www.rhyolite.com/cgi-bin/ct.cgi?sb=DCC+server-ID">web
+form</A>.
+<P>
+After you have an official server-ID,
+<UL>
+<LI>Obtain the <A HREF="dccd.html#FILE-flod">passwd-ID</A> and its password
+and add them to your <A HREF="homedir/ids">@prefix@/ids file</A>.
+<LI>If necessary adjust your firewalls to allow both incoming TCP connections
+to port 6277 on your DCC server and outgoing TCP connections to port 6277
+on your flooding peer.
+<LI>Add a line for each flooding peer to
+the <A HREF="dccd.html#FILE-flod">@prefix@/flod</A> file.
+<LI>Wait a few minutes for dccd to notice the change to the file
+and start flooding.
+The <A HREF="cdcc.html#OPERATION-stats">cdcc&nbsp;stats</A>,
+<A HREF="cdcc.html#OPERATION-flood-list">cdcc&nbsp;"id&nbsp;X;&nbsp;flood list"</A>
+and
+<A HREF="dblist.html#OPTION-H">@libexecdir@/dblist&nbsp;-Hv</A>
+commands can be used to monitor the floods of reports of checksums
+of bulk mail.
+</UL>
+<P>
+Flooded reports of bulk email contain timestamps that are used for several
+things including expiring old reports.
+To accurately detect stale incoming reports,
+a DCC server needs a clock that is not too inaccurate.
+For that reason it is good to run an NTP daemon on systems running DCC servers.
+
+<P><LI><H3><A NAME="step-greylist">Configure Greylisting</A></H3>
+<B>Skip</B> to the <A HREF="#step-start-dccm">next step</A>
+if greylisting will not be used.
+Greylist is very effective.
+See this
+<A HREF="http://www.dcc-servers.net/dcc/greylist.html">description</A>.
+<P>
+Larger sites can use more than one greylist server,
+with the greylist servers flooding data just like DCC servers.
+<P>
+To configure greylisting:
+<OL>
+<LI><H4>Assign greylist client- and server-IDs</H4>
+<P>Client-IDs and matching passwords must be used by clients of
+greylist servers such as dccm and dccifd.
+The client-IDs must be in the @prefix@/map file on the client system.
+Greylist client- and server-IDs must be in the
+<A HREF="homedir/ids">@prefix@/ids</A> file on the
+greylist server.
+When a system hosts both DCC and greylist servers, it is convenient
+for clients to use the same client-ID and password for both.
+It is also convenient for a greylist server and a DCC server on a system
+to share a common server-ID and password.
+<P>
+The vast majority of installations, which do not have local DCC servers,
+can use the greylist server-ID generated by the makefiles in the
+<A HREF="homedir/ids">@prefix@/ids</A> file.
+
+<P><LI><H4>Add the greylist server to @prefix@/map</H4>
+<P>If the
+cdcc&nbsp;"<A HREF="cdcc.html#OPERATION-info">info</A>"
+command does not show the correct greylist server,
+add it with something like
+<PRE>
+        cdcc "<A HREF="cdcc.html#OPERATION-add">add localhost greylist 32768 secret"</A>
+</PRE>
+The DCC makefile files add a greylist server at localhost or 127.0.0.1
+to <A HREF="cdcc.html#FILES">@prefix@/map</A> file created for a new
+DCC installation.
+
+<P><LI><H4>Set @prefix@/dcc_conf</H4>
+In most installations, enable a local greylist server by
+installing the script <A HREF="misc/rcDCC.in">@libexecdir@/rcDCC</A>
+with a symbolic link,
+setting  <EM>GREY_ENABLE=on</EM> in @prefix@/dcc_conf
+and then running
+<PRE>
+        <A HREF="misc/rcDCC.in">@libexecdir@/rcDCC</A> start
+</PRE>
+
+<P>If absolutely necessary, override the greylist
+<A HREF="dccd.html#OPTION-G">embargo, wait, and white</A> values
+in GREY_DCCD_ARGS in @prefix@/dcc_conf.
+Usually simply set GREY_CLIENT_ARGS=on
+
+<P><LI><H4>Set @prefix@/grey_flod</H4>
+<P>
+Sites with more than one greylist server should arrange to flood
+data among them by adding lines to
+<A HREF="dccd.html#FILE-grey_flod">@prefix@/grey_flod</A> files
+in the same format as
+<A HREF="dccd.html#FILE-flod">@prefix@/flod</A> files.
+Flooding among greylist servers uses port 6276 by default, and so that
+port may need to be opened in firewalls.
+
+<P><LI><H4>Set cron job</H4>
+<P>
+Install a daily cron job like
+<A HREF="misc/crontab.in">misc/crontab</A>
+and
+<A HREF="misc/cron-dccd.in">@libexecdir@/cron-dccd</A>
+to clean the database.
+<P>
+
+<P><LI><H4>Whitelist Mail Submission Clients</H4>
+<P>
+Greylisting of local mail systems must be turned off because common
+mail user agents (MUAs) cannot handle temporary rejections.
+One way to turn off greylisting of local client is with <EM>submit</EM> lines
+in the main <A HREF="homedir/whiteclnt">whiteclnt</A> file
+as described <A HREF="#whitelist">above</A>.
+<P>
+An alternative to whitelisting mail submission clients is available with
+<A HREF="dccm.html">dccm</A> and sendmail by using the
+<A HREF="misc/hackmc">misc/hackmc&nbsp;-T</A>
+script to modify sendmail.cf to trust SMTP clients authenticated with
+SMTP-TLS or SMTP-AUTH.
+
+</OL>
+
+
+
+<P><LI><H3><A NAME="step-start-dccm">Start dccm</A></H3>
+If the DCC-sendmail interface, dccm, is not used,
+<B>skip</B> to the <A HREF="#step-start-dccifd">next step</A>.
+<P>
+The DCC sendmail milter interface <A HREF="dccm.html">dccm</A>
+should be started before sendmail.
+That often requires changing an /etc/rc script or configuration file.
+The script <A HREF="misc/rcDCC.in">@libexecdir@/rcDCC</A>
+should be installed, best with a symbolic link.
+The milter daemon can be started manually with
+<PRE>
+        rcDCC start
+</PRE>
+
+
+
+<P><LI><H3><A NAME="step-start-dccifd">Start dccifd</A></H3>
+If the general MTA interface, dccifd, is not used,
+<B>skip</B> to the <A HREF="#configure-dccproc">next step</A>.
+If you are using SpamAssassin, then you almost certainly
+should be using dccifd.
+<P>
+The general MTA interface <A HREF="dccifd.html">dccifd</A>
+should usually be started before the mail transfer agent or MTA.
+It should be enabled by setting <EM>DCCIFD_ENABLE=on</EM>
+in <A HREF="homedir/dcc_conf.in">dcc_conf</A>.
+It is also usually necessary to change an /etc/rc script or configuration file
+to start and stop the daemon with the system.
+The script <A HREF="misc/rcDCC.in">@libexecdir@/rcDCC</A>
+should be installed, best with a symbolic link.
+The daemon can be started manually with
+<PRE>
+        rcDCC start
+</PRE>
+<P>
+Dccifd can be used as a
+<A HREF="http://www.postfix.org/SMTPD_PROXY_README.html">Postfix Before-Queue
+Content filter</A>
+as described the
+<A HREF="dccifd.html#EXAMPLES">dccifd documentation</A>.
+
+
+<P><LI><H3><A NAME="configure-dccproc">Configure Uses of dccproc</A></H3>
+If dccproc is used with procmail,
+add rules to procmailrc files as described in the
+<A HREF="dccproc.html#EXAMPLES">dccproc man page</A>.
+
+<P><LI><H3>Adjust Rejection Thresholds</H3>
+<P>It is best to only mark mail with X-DCC SMTP headers
+before changing procmail or dccm to reject mail.
+Configure dccm with DCCM_LOG_AT in <A HREF="homedir/dcc_conf.in">dcc_conf</A>
+to log bulk mail with somewhat lower counts.
+
+
+<P><LI><H3>Additional Considerations</H3>
+<P>Some additional mechanisms are available in the DCC client programs.
+They are often unnecessary when
+<A HREF="#step-greylist">greylisting</A> is used.
+<UL>
+<LI><A HREF="dccm.html#OPTION-B">DNS blacklists (DNSBL)</A>
+can reject messages containing "spamvertised" URLs.
+<LI><A HREF="dccm.html#OPTION-t">DCC reputations</A>
+are available in the commercial version of the DCC source.
+</UL>
+
+<P>
+When possible, it is almost always better to use dccifd than dccproc.
+This is certainly true with SpamAssassin.
+
+
+
+<P><LI><H3>Update As Needed</H3>
+<P>New versions released at the
+<A HREF="http://www.dcc-servers.net/dcc/">usual place</A>
+can be installed by running the
+<A HREF="misc/updatedcc.in">@libexecdir@/updatedcc</A> script.
+That script is (re)built by the
+<EM>./configure</EM> script
+and runs <EM>./configure</EM> with parameters and
+environment variables from the previous installation.
+
+
+
+<P><LI><H3>Remove or Uninstall</H3>
+<P>Most of the DCC can be removed by running
+<A HREF="misc/uninstalldcc.in">@libexecdir@/uninstalldcc</A> script.
+Some logs and configuration files with locally chosen parameters in the home
+directory are not deleted.
+Manual changes such as links to
+<A HREF="misc/rcDCC.in">@libexecdir@/rcDCC</A>
+or the installation of the cron job,
+<A HREF="misc/cron-dccd.in">@libexecdir@/cron-dccd</A>,
+are not reversed.
+
+
+</OL>
+
+
+
+<H2>Installation Parameters</H2>
+<P>There are several installation configuration parameters that can
+set to suit individual preferences and systems.
+
+<P>
+<TABLE class=centered border="1" cellpadding="2%"  frame=void rules=rows
+    summary="table of makefile and configure script controls">
+<CAPTION><A NAME="envtbl">
+    <B>Makefile and <EM>./configure</EM> Script Controls
+    <BR>Do NOT set these parameters unless absolutely necessary.</B></A>
+</CAPTION>
+<TR><TH><EM>./configure</EM> option
+    <TH>env name or <br>make variable
+    <TH>used by
+    <TH>default value
+    <TH>use
+<TR><TD class=env><A NAME="envtbl--homedir">--homedir=HOMEDIR</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>@prefix@/
+    <TD>DCC home directory with most DCC files
+<TR><TD class=env><A NAME="envtbl--libexecdir">--libexecdir=DIR</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD><A HREF="#envtbl--homedir">--homedir</A>/libexec
+    <TD>directory containing most DCC programs
+<TR><TD class=env><A NAME="envtbl--bindir">--bindir</A>=DIR
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>/usr/local/bin
+    <TD>installation directory for DCC user commands including cdcc and
+        dccproc<SUP>3</SUP>
+<TR><TD class=env>--mandir=DIR
+    <TD>&nbsp
+    <TD><EM>./configure</EM>
+    <TD>/usr/local/man
+    <TD>installation directory for man pages<SUP>3</SUP>
+<TR><TD class=env>&nbsp;
+    <TD class=conf>NOMAN<SUP>1</SUP>
+    <TD>make
+    <TD>(unset)
+    <TD>do not install man pages when set<SUP>3</SUP>
+<TR><TD class=env><A NAME="envtbl--installroot">--with-installroot=DIR</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>(unset)
+    <TD>prefix all installation directory paths to build a binary tarball
+<TR><TD class=env><A NAME="envtbl--configsuffix">--with-configsuffix=str</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>(unset)
+    <TD>append <EM>str</EM> to generated configuration file names
+<TR><TD class=env><A NAME="envtbl--with-uid">--with-uid=UID</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>root
+    <TD>user name and set-UID for DCC programs and data
+<TR><TD class=env>&nbsp;
+    <TD class=conf><A NAME="envtbl-DCC_OWN">DCC_OWN</A><SUP>1</SUP>
+    <TD>make
+    <TD>bin, daemon on OS X, or current
+    <TD>owner or UID of most installed files<SUP>3</SUP>
+<TR><TD class=env>&nbsp;
+    <TD class=conf><A NAME="envtbl-DCC_GRP">DCC_GRP</A><SUP>1</SUP>
+    <TD>make
+    <TD>bin, daemon on OS X, or current
+    <TD>group of most installed files<SUP>3</SUP>
+<TR><TD class=env>&nbsp;
+    <TD class=conf>DCC_MODE<SUP>1</SUP>
+    <TD>make
+    <TD>555
+    <TD>mode of most installed programs
+<TR><TD class=env>&nbsp;
+    <TD class=conf>MANOWN<SUP>1</SUP>
+    <TD>make
+    <TD><A HREF="#envtbl-DCC_OWN">DCC_OWN</A>
+        or current
+    <TD>owner or UID of installed man pages<SUP>3</SUP>
+<TR><TD class=env>&nbsp;
+    <TD class=conf>MANGRP<SUP>1</SUP>
+    <TD>make
+    <TD><A HREF="#envtbl-DCC_GRP">DCC_GRP</A>
+        or current
+    <TD>group of installed man pages<SUP>3</SUP>
+<TR><TD class=env><A NAME="envtbl--disable-sys-inst">--disable-sys-inst</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>enabled
+    <TD>disable system installation or chmod, chgrp, and set-UID<SUP>3</SUP>
+<TR><TD class=env><A NAME="envtbl--disable-server">--disable-server</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>build but do not start
+    <TD>do not build server including dbclean and dccd
+<TR><TD class=env><A NAME="envtbl--disable-dccifd">--disable-dccifd</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>build but do not start
+    <TD>do not build program interface
+<TR><TD class=env><A NAME="envtbl--disable-dccm">--disable-dccm</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>build but do not start
+    <TD>do not build sendmail interface
+<TR><TD class=env><A NAME="envtbl--with-sendmail">--with-sendmail=DIR</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>../sendmail or /usr/ports/mail/...
+    <TD>directory containing sendmail milter header files
+<TR><TD class=env><A NAME="envtbl--cgibin">--with-cgibin</A>=DIR
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD><A HREF="#envtbl--homedir">--homedir</A>/cgi-bin
+    <TD>directory for DCC whitelist <A HREF="cgi-bin/">CGI scripts</A>
+<TR><TD class=env>--with-rundir=DIR
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>@dcc_rundir@
+    <TD>"run" directory for PIDs and sockets
+<TR><TD class=env>&nbsp;
+    <TD class=conf>CFLAGS<SUP>1</SUP>
+    <TD>make & <EM>./configure</EM>
+    <TD>&nbsp;
+    <TD>global compiler options such as -g or -O2
+<TR><TD class=env>&nbsp;
+    <TD class=conf>DCC_CFLAGS<SUP>2</SUP>
+    <TD><EM>./configure</EM>
+    <TD>depends on target
+    <TD>global compiler options
+<TR><TD class=env>&nbsp;
+    <TD class=conf>PTHREAD_CFLAGS<SUP>2</SUP>
+    <TD><EM>./configure</EM>
+    <TD>depends on target
+    <TD>compiler options for compiling dccm and dccifd with pthreads
+<TR><TD class=env>&nbsp;
+    <TD class=conf>LDFLAGS<SUP>1</SUP>
+    <TD>make & <EM>./configure</EM>
+    <TD>&nbsp;
+    <TD>global linker options
+<TR><TD class=env>&nbsp;
+    <TD class=conf><A NAME="envtbl-DCC_LDFLAGS">DCC_LDFLAGS</A><SUP>2</SUP>
+    <TD><EM>./configure</EM>
+    <TD>depends on target
+    <TD>global linker options
+<TR><TD class=env>&nbsp;
+    <TD class=conf>PTHREAD_LDFLAGS<SUP>2</SUP>
+    <TD><EM>./configure</EM>
+    <TD>depends on target
+    <TD>linker options for dccm and dccifd
+<TR><TD class=env>&nbsp;
+    <TD class=conf><A NAME="envtbl-LIBS">LIBS</A><SUP>2</SUP>
+    <TD><EM>./configure</EM>
+    <TD>&nbsp;
+    <TD>additional libraries linked with all programs
+<TR><TD class=env>&nbsp;
+    <TD class=conf>PTHREAD_LIBS<SUP>2</SUP>
+    <TD><EM>./configure</EM>
+    <TD>depends on target
+    <TD>libraries for dccm and dccifd
+<TR><TD class=env>&nbsp;
+    <TD class=conf>CC
+    <TD>make & <EM>./configure</EM>
+    <TD>cc
+    <TD>C compiler such as "gcc" or "/opt/SUNWspro/SC6.1/bin/cc"
+<TR><TD class=env>&nbsp;
+    <TD class=conf>INSTALL<SUP>1</SUP>
+    <TD>make
+    <TD><A HREF="autoconf/install-sh">./autoconf/install-sh</A>
+    <TD>installation script
+<TR><TD class=env>&nbsp;
+    <TD class=conf>DCCD_MAX_FLOODS<SUP>1</SUP>
+    <TD>make
+    <TD>32
+    <TD>maximum DCC server flooding peers
+<TR><TD class=env><A NAME="envtbl--with-db-memory">--with-db-memory=MB</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>64
+    <TD>minimum server database buffer size between 32 MBytes and 49152 MBytes
+        <!-- DB_MIN_MBYTE -->
+<TR><TD class=env><A NAME="envtbl--with-max-db-mem">--with-max-db-mem=MB</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>1920 <!--DB_MAX_2G_MBYTE--> on 32-bit systems
+            <BR>49152 <!--MAX_MAX_DB_MBYTE--> on 64-bit systems
+    <TD>maximum server database buffer size
+        <!-- DB_MAX_MBYTE -->
+<TR><TD class=env><A NAME="envtbl--with-max-log-size">--with-max-log-size=KB</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>32
+    <TD>maximum dccifd and dccm log file size in KBytes; 0=no limit
+<TR><TD class=env><A NAME="envtbl--disable-IPv6">--without-IPv6</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>IPV6 on if supported
+    <TD>turn off IPv6 support
+<TR><TD class=env><A NAME="envtbl--with-socks">--with-socks[=lib]</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>none
+    <TD>location of <A HREF="#SOCKS">SOCKS</A> client library
+<TR><TD class=env><A NAME="envtbl--64bits">--enable-64-bits</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>correct value on most systems
+            <BR>32 bits Solaris and Linux PowerPC
+    <TD>use MD5 code in DCC source instead of any local library
+<TR><TD class=env><A NAME="envtbl--with-DCC-MD5">--with-DCC-MD5</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>local library if available;
+    <TD>use MD5 code in DCC source instead of any local library
+<TR><TD class=env><A NAME="envtbl--with-kludge">--with-kludge=FILE</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>none
+    <TD>include header FILE, best with an absolute path
+<TR><TD class=env><A NAME="envtbl--fetch-cmd">--with-fetch-cmd=pgm</A>
+    <TD>&nbsp;
+    <TD><EM>./configure</EM>
+    <TD>wget, fetch, curl, or ftp
+    <TD>program used by
+        <A HREF="misc/updatedcc.in">@libexecdir@/updatedcc</A>,
+        and other utilities to fetch files
+<TR><TD>&nbsp;
+</TABLE>
+
+<DL class=small>
+<DT>Note<SUP>1</SUP>
+<DD>These values are not built into the Makefiles by the
+<EM>./configure</EM> script but their current values in the environment
+are used by the script and the Makefiles.
+
+<DT>Note<SUP>2</SUP>
+<DD>These values are copied by the <EM>./configure</EM> script from the
+environment into the generated Makefiles.
+
+<DT>Note<SUP>3</SUP>
+<DD>When <A HREF="#envtbl--disable-sys-inst">--disable-sys-inst</A>
+is specified, the current UID and GID become the defaults,
+and the man pages are not installed.
+If the <EM>./configure</EM> script is not run as root,
+<A HREF="dccproc.html">dccproc</A>, <A HREF="cdcc.html">cdcc</A>,
+and <A HREF="dccsight.html">dccsight</A> are not installed set-UID.
+It is usually also necessary to set
+<A HREF="#envtbl--bindir">--bindir</A> to a private directory such
+as $HOME/bin.
+</DL>
+
+<H2><A NAME="Compatibility">Compatibility</A></H2>
+<P>DCC is thought to work on several systems including:
+<DL class=compat>
+<DT>BSDI BSD/OS
+<DD>DCC works starting with version 3.0 of BSD/OS.
+
+<DT>FreeBSD
+<DD>The works starting with at least version 4.0 of FreeBSD.
+
+<DT>NetBSD
+<DD>The DCC should work starting with at least 1.4.2 without threads
+and so with dccd, dccproc, and all of DCC except the part that uses
+threads, dccm.
+Dccm is available if you point PTHREAD_LIBS, PTHREAD_CFLAGS, and
+PTHREAD_LDFLAGS to the optional threads package.
+
+<DT>OpenBSD
+<DD>DCC works starting with at least 2.9 despite lame
+the lame mmap() implementation.
+
+<DT>Linux
+<DD>DCC works starting with at least RedHat 5.2.
+<P>
+On 64-bit PowerPC systems with more than 4 GBytes,
+use <EM>./configure --with-64-bits</EM> to build a DCC server that can
+benefit from a full sized database.
+A 64-bit sendmail milter library will be needed if
+<A HREF="dccm.html">Dccm</A> is used
+
+<DT>AIX
+<DD>DCC on 4.1.PPC has been tried but not well tested.
+Rumor has it that the 4.1.PPC pthreads code does not work
+with the sendmail milter library and dccm, but the rest of
+DCC does work.
+
+<DT>Solaris
+<DD>DCC compiles on several versions of Solaris with gcc or
+native C compiler by setting the environment variable CC appropriately.
+<EM>You must install gmake</EM>.
+<EM>Do not</EM> use "CFLAGS=-fast" with the native compiler.
+<P>
+While building the sendmail milter library, consider using
+<EM>_FFR_USE_POLL</EM> to avoid problems with large file descriptors
+and select().
+<P>
+On 64-bit systems with more than 4 GBytes,
+use <EM>./configure --with-64-bits</EM> to build a DCC server that can
+benefit from a full sized database.
+A 64-bit sendmail milter library will be needed if
+<A HREF="dccm.html">Dccm</A> is used
+
+<DT>HP-UX
+<DD>DCC compiles on versions of HP-UX starting with 11.00.
+It requires gmake.  Dccproc and dccm work.
+Dccifd does not work with UNIX domain sockets because select() and
+poll() do not notice the results of shutdown().
+Dccifd does work with TCP/IP connections to MTAs or spam filters.
+<BR>
+Dccproc should work on version 10.20, since it does not use pthreads.
+
+<DT>IRIX
+<DD>DCC compiles on IRIX 6.5.
+It requires gmake.
+
+<DT>OSF1
+<DD>DCC compiles on OSF1 V5.0 with gmake.
+
+<DT>OpenUNIX
+<DD>DCC compiles on OpenUNIX 8.0.1.
+
+<DT>Mac OS/X
+<DD>DCC compiles on at least some versions of Apple's OS/X.
+
+<DT>Windows
+<DD>The DCC client dccproc compiles and works on at least some versions of
+Windows 98 and Windows XP with Borland's free SDK
+and with Microsoft's SDK.
+See the <A HREF="win32.mak">main Makefile</A> for Windows.
+
+
+</DL>
+<P>
+<SMALL>Those system names include trademarks.  Please don't abuse them.</SMALL>
+
+<H2><A NAME="Troubleshooting">Troubleshooting</A></H2>
+<P>
+Much of the DCC <A HREF=FAQ.html>list of frequently asked questions</A>
+concerns troubleshooting DCC installations.
+Many of the messages in the archive of the
+<A HREF="http://www.rhyolite.com/pipermail/dcc/">DCC mailing list</A>
+are also troubleshooting questions and answers.
+
+<H2><A NAME="spam-traps"></A><A NAME="spamtraps">Spam Traps</A></H2>
+<P><A HREF="dccm.html">Dccm</A> and sendmail can be configured to
+report the checksums of unsolicited bulk mail so that other DCC clients
+can reject later copies of the same unsolicited bulk mail
+sent from other sources.
+Such mechanisms are commonly called <EM>spam traps</EM>.
+
+<P>Entries in a sendmail access_db can also be rejected or discarded
+while they are reported to the DCC server by dccm.
+The script
+<A HREF="misc/hackmc">misc/hackmc</A> modifies the output of
+sendmail .mc files to tell dccm about some undesirable mail.
+The script accepts one or more .mc files and generates the corresponding
+slightly modified .cf files.
+If the access_db entry starts with the string "DCC:",
+the message is reported by dccm to the DCC server as extremely bulky.
+Otherwise the message is rejected as usual.
+The remainder of the the access_db entry after "DCC:" consists of
+the optional string "DISCARD" followed by an optional SMTP status message.
+If the string "DISCARD" is present, the message is discarded instead of
+rejected.  This is important to keep senders of unsolicited bulk mail from
+discovering and removing "spam trap" addresses from their target lists.
+
+<P>For example, a line like the following in an access_db can
+discard all mail from example.com while reporting it to the DCC server
+as extremely bulky.
+ Note the quotes (").
+<PRE>
+    example.com     DCC: "DISCARD spam"
+</PRE>
+
+<P>It is also possible to route mail from a spam trap address to
+dccproc as described in the
+<A HREF="dccproc.html#EXAMPLES">dccproc man page</A>
+
+
+<H2><A NAME="SOCKS">SOCKS</A></H2>
+<P>The DCC client and server programs can be built to use the SOCKS
+protocol.
+The
+<A HREF="#envtbl--with-socks">--with-socks</A>
+<EM>./configure</EM> parameter
+configures the DCC client library and the DCC server
+to use common SOCKS network library functions.
+If the SOCKS library is in a standard place,
+something like <A HREF="#envtbl--with-socks">--with-socks=socks</A>
+should be sufficient.
+Setting the environment variable
+<A HREF="#envtbl-DCC_LDFLAGS">DCC_LDFLAGS</A> to something
+like <Em>-L/usr/local/lib</Em> is sometimes helpful.
+Otherwise, using <A HREF="#envtbl--with-socks">--with-socks</A>
+without specifying the library name and setting <A HREF="#envtbl-LIBS">LIBS</A>
+to the full pathname of the library
+might work.
+<P>
+DCC client programs
+including dccproc and dccm that use the DCC client library
+must be told to use the SOCKS5 protocol with the
+<A HREF="cdcc.html#OPERATION-SOCKS">SOCKS&nbsp;on</A>
+operation of
+<A HREF="cdcc.html">cdcc</A>.
+SOCKS5 is required instead of SOCKS4
+because DCC clients communicate with DCC servers using UDP.
+
+<P>
+DCC servers can use SOCKS4 or SOCKS5 when exchanging
+floods of reports of checksums.
+Links between individual pairs of peers are configured with the
+<Em>passive</Em> and <Em>SOCKS</Em> flags in the flod file described
+in the <A HREF="dccd.html#FILE-flod">dccd</A> man page.
+In both cases, the SOCKS library code must be configured, often
+in the files /etc/socks.conf and /etc/socksd.conf.
+
+<P>
+When the DCC software is built with SOCKS,
+IPv6 name resolution is turned off.
+
+<P>
+The DCC server and client programs have been tested with the
+<A HREF="http://www.inet.no/dante/">DANTE</A> library and server.
+The DANTE SOCKS implementation is also one of the FreeBSD "ports"
+or packages.
+<P>
+Note that if a connection fails repeatedly, Dante will disable the rule
+that failed and will eventually try the underlying connect()
+call.
+This fails in almost every SOCKS environment because there is
+no available route for an ordinary connect().
+Dante by default won't re-enable the failing rule.
+To fix this, change BADROUTE_EXPIRE from the default of <EM>0*60</EM>
+to <EM>5</EM> in include/config.h in the Dante source and recompile.
+
+
+<P class=small>
+This document describes DCC version 1.3.103.
+<P>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG SRC="http://logos.dcc-servers.net/border.png"
+            class=logo ALT="DCC logo">
+    </A>
+<A HREF="http://validator.w3.org/check?uri=referer">
+    <IMG class=logo ALT="Valid HTML 4.01 Strict"
+        SRC="http://www.w3.org/Icons/valid-html401">
+    </A>
+</BODY>
+</HTML>
+<!--  LocalWords:  dccproc libmilter pthreads procmail dccm dccd dcc libmilter
+ -->
+<!--  LocalWords:  homedir dbclean whitelist setenv nbsp Solaris crontab Linux
+ -->
+<!--  LocalWords:  gmake FreeBSD NetBSD CFLAGS PTHREAD LDFLAGS LIBS HPUX IDs DT
+ -->
+<!--  LocalWords:  cdcc DL DD ids var RTT TD TR dccifd greylist MTA
+ -->
+<!--  LocalWords:  whitelisting
+ -->
diff -r 000000000000 -r c7f6b056b673 LICENSE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,47 @@
+ * Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+---
+
+
+ * Some parts including dcclib/getopt.c and include/sendmail-sysexits.h
+ * Copyright (c) 1987, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+
+ * Some other parts including dcclib/inet_ntop.c
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+
+ * Some other parts including autoconf/install-sh
+ *	Copyright 1991 by the Massachusetts Institute of Technology
diff -r 000000000000 -r c7f6b056b673 Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,73 @@
+# make all of the Distributed Checksum Clearinghouse
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.50 $Revision$
+# @configure_input@
+
+DEPTH	=.
+SUBDIR_PROGS=cdcc @SUBDIR_DCCD@ @SUBDIR_DCCM@ @SUBDIR_DCCIFD@	\
+	dccproc dccsight homedir misc cgi-bin
+SUBDIR_MAN  =cdcc dbclean dccd dblist dccm dccifd dccproc dccsight
+SUBDIR	    =dcclib @SUBDIR_SRVRLIB@ @SUBDIR_THRLIB@ $(SUBDIR_PROGS)
+SUBDIR_ALL  =$(SUBDIR) @SUBDIR_DISABLED@ include autoconf rrd-combine
+
+SUBDIR_HTML =$(SUBDIR_MAN:=.html)
+SUBDIR_MAN0 =$(SUBDIR_MAN:=.0)
+SUBDIR_MAN8 =$(SUBDIR_MAN:=.8)
+MAN8	    =@MAN8@
+TEXT	= $(MAN8) dcc.0 $(SUBDIR_MAN0) dcc.html $(SUBDIR_HTML)
+
+@MAKE_INC@
+@MAKE_SUBDIR@
+
+all:	$(TEXT)
+
+@MAKE_INC2@
+
+# delete all known files in build tree
+purge:cleandir
+	find $(SUBDIR_ALL) -depth -name .manifest -print | while read NM; do\
+	    (cd `expr $$NM : "\(.*\)/[^/]*"`; rm -f `cat .manifest`)\
+	    done
+	-rm -rf `cat .config .manifest`
+	-find $(SUBDIR_ALL) -depth -type d -print | @DCC_XARGS@ rmdir
+
+# uninstall things we won't install
+install:noinstall
+noinstall:
+	for DIR in @SUBDIR_DISABLED@ .; do\
+	    if test "$$DIR" != .; then\
+		(cd $$DIR; $(MAKE) deinstall); fi; done
diff -r 000000000000 -r c7f6b056b673 Makefile.inc.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile.inc.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,143 @@
+# common Makefile lines for the Distributed Checksum Clearinghouse
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.61 $Revision$
+# @configure_input@
+
+# These lines must not include any rules, or the shared BSD mk files
+# will be confused and not do anything.
+
+# BSD/OS bsd.prog.mk includes ../Makefile.inc twice
+@MAKE_DOT@ifndef DCC_MAKEFILE_INC
+DCC_MAKEFILE_INC=x
+
+# common to all DCC clients
+CLNTLIBS    =$(DEPTH)/dcclib/libdcc.a
+
+# common to threaded DCC clients
+THRINC	    =-I$(DEPTH)/thrlib @PTHREAD_CFLAGS@
+THR_LDADD    =$(DEPTH)/thrlib/libthr.a @PTHREAD_LIBS@
+THR_DPADD    =$(DEPTH)/thrlib/libthr.a
+
+SRVRINC	    =-I$(DEPTH)/srvrlib
+SRVRLIBS    =$(DEPTH)/srvrlib/libsrvr.a
+
+@DCC_CC@
+RANLIB	    =@RANLIB@
+
+NOPROFILE   =		# stop FreeBSD profile library
+NO_PROFILE  =
+NOGCCERROR  =		#turn off NetBSD's default nonsense in bsd.sys.mk
+NOLINT	    =		#why does NetBSD care about lint today?
+NO_LINT	    =		#   suppress FreeBSD noise about NOLINT
+STRIP	    =		#keep FreeBSD from stripping the a.outs
+
+LDADD	    +=$(CLNTLIBS) @LIBS@
+DPADD	    +=$(CLNTLIBS)
+
+#DBGFLAGS   +=-g
+#DBGFLAGS   +=-DDCC_DEBUG_HEAP -DDCC_DEBUG_CKSUM -DDCC_DEBUG_CLNT_LOCK
+# --with-c-warnings   gcc warnings
+@CWRN1@	    +=-W -Wall -Wunused -Winline -Wbad-function-cast
+@CWRN1@	    +=-Wnested-externs -Wpointer-arith -Wshadow -Wwrite-strings
+# --with-c-warnings=2   often not supported
+@CWRN2@	    +=-Wdeclaration-after-statement -Wmissing-noreturn -Wswitch-enum
+#  --with-c-warnings=3   often wrong
+@CWRN3@	    +=-Wformat-nonliteral -Wunreachable-code -Wcast-align
+CFLAGS	    +=@DCC_CFLAGS@ $(DBGFLAGS) $(CWARN) -I$(DEPTH)/include $(PROFILE)
+LDFLAGS	    +=@DCC_LDFLAGS@ $(DBGFLAGS) $(PROFILE)
+
+# turn off man pages if not installing on the system
+@NO_SYS_INSTALL@
+@NO_SUID@
+
+DCC_HOMEDIR =@prefix@
+@MAKE_DOT@ifdef DCC_BINDIR
+BINDIR	    =$(DCC_BINDIR)
+@MAKE_DOT@else
+BINDIR	    =@installroot@@bindir@
+@MAKE_DOT@endif
+
+@MAKE_DOT@ifndef DCC_OWN
+@MAKE_DOT@ifdef NO_SUID
+DCC_OWN	    =@DCC_OWN@
+@MAKE_DOT@else
+DCC_OWN	    =@DCCSUID@
+@MAKE_DOT@endif
+@MAKE_DOT@endif
+BINOWN	    =$(DCC_OWN)
+
+@MAKE_DOT@ifndef DCC_GRP
+DCC_GRP	    =@DCC_GRP@
+@MAKE_DOT@endif
+BINGRP	    =$(DCC_GRP)
+
+@MAKE_DOT@ifndef DCC_MODE
+DCC_MODE    =555
+@MAKE_DOT@endif
+BINMODE	    =$(DCC_MODE)
+
+MANDIR	    =@installroot@@mandir@
+@MAKE_DOT@ifndef MANGRP
+MANGRP	    =$(DCC_GRP)
+@MAKE_DOT@endif
+@MAKE_DOT@ifndef MANOWN
+MANOWN	    =$(DCC_OWN)
+@MAKE_DOT@endif
+MANMODE	    =444
+
+@MAKE_DOT@ifdef NO_SYS_INSTALL
+NOMAN	=no
+SET_BINOWN=
+SET_MANOWN=
+SET_DCCOWN=
+@MAKE_DOT@else
+SET_BINOWN=-o $(BINOWN) -g $(BINGRP)
+SET_MANOWN=-o $(MANOWN) -g $(MANGRP)
+SET_DCCOWN=-o @DCCSUID@ -g $(BINGRP)
+@MAKE_DOT@endif
+
+@MAKE_DOT@ifdef PROG
+MAN8	=$(PROG).0
+# turn off man pages in the command directories and build them in the top level
+NOMAN	=no
+@MAKE_DOT@endif
+@MAKE_DOT@endif
+
+# deal with silly FreeBSD renaming frenzy
+@MAKE_DOT@ifdef NOMAN
+NO_MAN	=no
+@MAKE_DOT@endif
diff -r 000000000000 -r c7f6b056b673 Makefile.inc2.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile.inc2.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,101 @@
+# common Makefile lines for the Distributed Checksum Clearinghouse programs
+
+# These rules must be included after the share BSD mk files so that
+# the shared files can be happy about defining the main targets
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.41 $Revision$
+# @configure_input@
+
+# some versions of gmake do not understand ?=
+@MAKE_DOT@ifndef INSTALL
+INSTALL	=@DCCINSTALL@
+@MAKE_DOT@endif
+
+BININSTALL=$(INSTALL) -c $(SET_BINOWN) -m $(BINMODE)
+
+# create the DCC home directory before trying to install files
+beforeinstall:$(BINDIR) $(MANDIR)8 depend all
+$(BINDIR):
+	$(INSTALL) -d $(SET_BINOWN) -m 755 $@
+
+@USE_DCCMANINSTALL@
+dccmaninstall:
+@MAKE_DOT@ifndef NOMAN
+@MAKE_DOT@ifdef MAN8
+	eval 'for NM in $(MAN8); do\
+	  $(INSTALL) -c $(SET_MANOWN) -m $(MANMODE) $$NM $(MANDIR)8@MAN8INST@;\
+	  done'
+@MAKE_DOT@endif
+@MAKE_DOT@endif
+
+$(MANDIR)8:
+@MAKE_DOT@ifndef NOMAN
+@MAKE_DOT@ifdef MAN8
+	$(INSTALL) -d $(SET_MANOWN) -m 755 $(MANDIR)8
+@MAKE_DOT@endif
+@MAKE_DOT@endif
+
+
+uninstall delete:deinstall
+deinstall:beforedelete
+
+progdelete:
+@MAKE_DOT@ifdef PROG
+	rm -f $(BINDIR)/$(PROG)
+@MAKE_DOT@endif
+
+beforedelete:progdelete
+@MAKE_DOT@ifdef SUBDIR
+	for DIR in $(SUBDIR); do\
+	    (cd $$DIR; $(MAKE) $(GMAKE_QUIET) deinstall); done
+@MAKE_DOT@endif
+@MAKE_DOT@ifndef NOMAN
+@MAKE_DOT@ifdef MAN8
+	eval 'for NM in $(MAN8); do rm -f $(MANDIR)8@MAN8INST@/$$NM; done'
+@MAKE_DOT@endif
+@MAKE_DOT@endif
+
+
+# ensure that even systems with neither the BSD nor the gmake dependency
+#   mechanisms rebuild things when the main parameter file changes.
+@MAKE_DOT@ifdef SRCS
+$(SRCS:.c=.o):$(DEPTH)/include/dcc_config.h
+# ensure that adding a kludge.h file or otherwise changing included files
+#   rebuilds dependencies in systems with .depend files
+.depend:$(DEPTH)/include/dcc_config.h Makefile $(DEPTH)/Makefile.inc	\
+	    $(DEPTH)/Makefile.inc2
+@MAKE_DOT@endif
diff -r 000000000000 -r c7f6b056b673 RESTRICTIONS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RESTRICTIONS	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,11 @@
+The free DCC source is available only for non-commercial and some other
+uses.  It is not available to organizations that sell anti-spam appliances
+or provide managed email services as opposed to common Internet service.
+It has always been wrong to take and sell the CPU cycles, bandwidth, and
+human system administration labor of the public DCC servers.
+
+Please read the LICENSE file in the DCC source or at
+http://www.dcc-servers.net/dcc/dcc-tree/LICENSE
+
+
+Rhyolite Software DCC 1.3.103-1.2 $Revision$
diff -r 000000000000 -r c7f6b056b673 autoconf/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/autoconf/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,2 @@
+install-sh
+.manifest
diff -r 000000000000 -r c7f6b056b673 autoconf/install-sh
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/autoconf/install-sh	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+	-c) instcmd="$cpprog"
+	    shift
+	    continue;;
+
+	-d) dir_arg=true
+	    shift
+	    continue;;
+
+	-m) chmodcmd="$chmodprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-o) chowncmd="$chownprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-g) chgrpcmd="$chgrpprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-s) stripcmd="$stripprog"
+	    shift
+	    continue;;
+
+	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
+	    shift
+	    continue;;
+
+	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+	    shift
+	    continue;;
+
+	*)  if [ x"$src" = x ]
+	    then
+		src=$1
+	    else
+		# this colon is to work around a 386BSD /bin/sh bug
+		:
+		dst=$1
+	    fi
+	    shift
+	    continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+	echo "install:	no input file specified"
+	exit 1
+else
+	true
+fi
+
+if [ x"$dir_arg" != x ]; then
+	dst=$src
+	src=""
+	
+	if [ -d $dst ]; then
+		instcmd=:
+		chmodcmd=""
+	else
+		instcmd=mkdir
+	fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+	if [ -f $src -o -d $src ]
+	then
+		true
+	else
+		echo "install:  $src does not exist"
+		exit 1
+	fi
+	
+	if [ x"$dst" = x ]
+	then
+		echo "install:	no destination specified"
+		exit 1
+	else
+		true
+	fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+	if [ -d $dst ]
+	then
+		dst="$dst"/`basename $src`
+	else
+		true
+	fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='	
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+	pathcomp="${pathcomp}${1}"
+	shift
+
+	if [ ! -d "${pathcomp}" ] ;
+        then
+		$mkdirprog "${pathcomp}"
+	else
+		true
+	fi
+
+	pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+	$doit $instcmd $dst &&
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+	if [ x"$transformarg" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		dstfile=`basename $dst $transformbasename | 
+			sed $transformarg`$transformbasename
+	fi
+
+# don't allow the sed command to completely eliminate the filename
+
+	if [ x"$dstfile" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		true
+	fi
+
+# Make a temp file name in the proper directory.
+
+	dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+	$doit $instcmd $src $dsttmp &&
+
+	trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+	$doit $rmcmd -f $dstdir/$dstfile &&
+	$doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff -r 000000000000 -r c7f6b056b673 cdcc.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cdcc.0	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,391 @@
+cdcc(8)               Distributed Checksum Clearinghouse               cdcc(8)
+
+NNAAMMEE
+     ccddcccc -- Control Distributed Checksum Clearinghouse
+
+SSYYNNOOPPSSIISS
+     ccddcccc [--VVddqq] [--hh _h_o_m_e_d_i_r] [--cc _i_d_s] [_o_p_1 _o_p_2 _._._. [_-]]
+
+DDEESSCCRRIIPPTTIIOONN
+     CCddcccc is used to clear, control, and query the control file used by Dis-
+     tributed Checksum Clearinghouse clients such as dccm(8).  The host names,
+     UDP port numbers, IDs, and passwords local clients use to talk to servers
+     as well as IP addresses, round trip times, and other information are con-
+     tained in the _m_a_p file.  While ccddcccc is set-UID, it uses the real UID only
+     when accessing the _m_a_p file.  It refuses to display sensitive information
+     such as passwords unless the real UID is the same as the effective UID.
+     Note that ccddcccc needs to be set to a UID that can read and write the _m_a_p
+     file, but that UID need not be 0.
+
+     CCddcccc is also used to send commands to DCC servers to tell them to stop,
+     reload their lists of DCC IDs, turn on tracing, and so forth.
+
+     Many commands sent to DCC servers require a numeric DCC ID and a password
+     recognized by the server.  A DCC password is a 1-32 character string that
+     does not contain blank, tab, newline or carriage return characters.  The
+     ID is specified with the iidd operation.  If ccddcccc is run with a real UID
+     that can read the _i_d_s file and a password is not specified (see the
+     ppaasssswwoorrdd operation), then the current password for the specified ID in
+     the _i_d_s file will be used.  If no _i_d_s file is available and a password
+     and DCC ID are not specified, ccddcccc uses the anonymous DCC client-ID.  DCC
+     servers do not expect a password from clients using the anonymous client-
+     ID, but they also won't honor control requests.
+
+     Operations that modify the _m_a_p file can only be performed when the real
+     UID is sufficient to modify the file directly.  Trying to perform an
+     operation that requires a password without specifying a server-ID or
+     without using a UID that can access the _i_d_s file produces an error mes-
+     sage complaining about a "privileged operation."
+
+     Commands and operations are read from the command line or from stdin.  A
+     series of _o_p_1 _o_p_2 _._._. operations followed a _- (a dash) causes operations
+     to be read from stdin after the command line operations are processed.
+     Semi-colons or newlines separate commands in UNIX command-line "words,"
+     as well as when commands are read from stdin.  Since each command line
+     operation must be a shell "word," quotes are often required as in
+
+           % cdcc "load map.txt"
+     or
+
+           % cdcc "host localhost;info" stats
+
+   OOPPTTIIOONNSS
+     The following options are available:
+
+     --VV   displays the version of the DCC controller.
+
+     --dd   enables debugging output from the DCC client software.  Additional
+          --dd options increase the number of messages.  See the ddeebbuugg command.
+
+     --qq   quiets initial complaints about the map file and some messages about
+          successful commands.  See the qquuiieett command.
+
+     --hh _h_o_m_e_d_i_r
+          overrides the default DCC home directory, _/_v_a_r_/_d_c_c.  See the hhoommeeddiirr
+          operation.
+
+     --cc _i_d_s
+          specifies file containing DCC IDs and passwords known by the local
+          DCC server.  An _i_d_s file that can be read by others cannot be used.
+          The format of the _i_d_s file is described in dccd(8).
+
+     _o_p_1 _o_p_2 _._._.
+          are operations or commands such as "id 100; stop".  Commands or
+          operations specified on the command line are performed before the
+          first interactive request.  The last command can be _- to specify
+          that additional commands should be read from stdin.
+
+   OOPPEERRAATTIIOONNSS
+     Local operations include the following:
+
+     hheellpp [_c_o_m_m_a_n_d]
+           lists information about one or all available commands and opera-
+           tions.
+
+     eexxiitt  stops ccddcccc
+
+     ggrreeyy [_o_n | _o_f_f]
+           switches between DCC and greylist servers.
+
+     hhoommeeddiirr [_p_a_t_h]
+           displays or specifies the DCC home directory.
+
+     ffiillee [_m_a_p]
+           displays or specifies the name or path of the map file.  The string
+           "-" specifies the default file _m_a_p in the DCC home directory.
+
+     nneeww mmaapp [_m_a_p]
+           creates a new, empty file for DCC server host names, port numbers,
+           passwords, and so forth.  There must not already be a file of the
+           same name.  The default is _m_a_p in the DCC home directory.
+
+     ddeelleettee _h_o_s_t[,_p_o_r_t]
+           deletes the entry in the _m_a_p file for _h_o_s_t and UDP _p_o_r_t_. If
+           greylist mode has been set with the ggrreeyy oonn command, the entry for
+           the grelist server at _h_o_s_t is deleted.
+
+     aadddd _h_o_s_t[,_p_o_r_t] [_R_T_T_+_a_d_j|_R_T_T_-_a_d_j] [_G_r_e_y_l_i_s_t] [_c_l_i_e_n_t_-_I_D [password]]
+           adds an entry to the _m_a_p file.  The _p_o_r_t can be "-" to specify the
+           default DCC server port number.
+
+           An adjustment to the round trip time is a multiple of 10 millisec-
+           onds between -4000 and +4000 following the string _R_T_T.  The adjust-
+           ment is added to the average measured round trip time when the DCC
+           client software picks the "nearest" DCC server, or the server with
+           the smallest RTT.  If an IP address is mentioned more than once in
+           the list of servers, for example because it is among the addresses
+           for more than one server name, conflicts among RTT adjustments are
+           resolved by picking the adjustment with the largest absolute value.
+
+           _G_r_e_y_l_i_s_t marks an entry for a greylist servers.  _G_r_e_y_l_i_s_t is
+           assumed if greylist mode has been set with the ggrreeyy oonn command, See
+           dccd(8).
+
+           If both the client-ID and the password are absent, the anonymous
+           client-ID, 1, is used.  The string _a_n_o_n is equivalent to the anony-
+           mous client-ID.  A null password string is assumed if the password
+           is missing and the client-ID is 1 or also missing.
+
+     llooaadd _i_n_f_o_-_f_i_l_e
+           loads the current parameter file with the host names, port numbers,
+           IDs, and passwords in _i_n_f_o_-_f_i_l_e.  Standard input is understood if
+           _i_n_f_o_-_f_i_l_e is "-".
+
+           A suitable file can be created with the iinnffoo operation.  It con-
+           sists of ignored blank or comment lines starting with '#' and other
+           lines in the same format as the arguments to the aadddd operation.
+           Note that output of the iinnffoo command will lack passwords unless it
+           is run by a privileged user.
+
+     hhoosstt [_h_o_s_t_n_a_m_e]
+           specifies the host name of the DCC server to which commands should
+           be sent.  If _h_o_s_t_n_a_m_e is "-", the current default DCC server is
+           chosen.
+
+     ppoorrtt [_p_o_r_t]
+           specifies the UDP port number of the DCC server to which commands
+           should be sent.  The default is 6277 or 6276 depending on the set-
+           ting of the greylist mode controlled with the ggrreeyy command.
+
+     ppaasssswwoorrdd _s_e_c_r_e_t
+           specifies the password with which to sign commands sent to the DCC
+           server specified with the sseerrvveerr and ppoorrtt operations.
+
+     iidd [_I_D]
+           specifies or displays the numeric DCC ID for commands sent to the
+           DCC server specified with the sseerrvveerr and ppoorrtt operations.  If no
+           password is specified with the ppaasssswwoorrdd command, the password is
+           sought in the local _i_d_s.
+
+     iinnffoo [--NN]
+           displays information about the connections to DCC servers.  It
+           starts with the current date and name of the current _m_a_p file or
+           says that ccddcccc is using the implicit file created with the sseerrvveerr
+           and ppoorrtt operations.  It then says when host names will next be
+           resolved into IP addresses, the smallest round trip time to the IP
+           addresses of known DCC servers.  The host name, UDP port number (or
+           dash if it is the default), DCC client-ID, and password (if ccddcccc is
+           used by a privileged user) are shown in one line per configured DCC
+           server.
+
+           The currently preferred IP address is indicated by an asterisk.
+           The "brand" of the server, its DCC ID, and its IP address are dis-
+           played in one line per IP address.  The performance of the server
+           at each IP address in the most recent 32 operations is displayed in
+           a second line.  The second line ends with the measured delay
+           imposed by the server on requests with this client's ID.
+
+           --NN displays the reverse DNS name of each server.
+
+     RRTTTT [--NN]
+           measures the round trip time to the DCC servers.  It does this by
+           discarding accumulated information and forcing a probe of all
+           listed server IP addresses.
+
+           _B_e_w_a_r_e that when run with sufficient privilege, the RRTTTT operation
+           is like the iinnffoo and llooaadd operations and displays cleartext pass-
+           words.
+
+           --NN displays the reverse DNS name of each server.
+
+     ddeebbuugg Op Ar on | off | TTL=x
+           increases or decreases debugging information from the DCC client
+           software or sets the IP TTL on queries to the server.  See --dd.
+
+           Some operating systems do not include the functions required to
+           change the IP TTL.  Others include the required functions but have
+           no apparent effect.
+
+     qquuiieett [_o_n | _o_f_f]
+           makes commands more quiet or more verbose.
+
+     IIPPvv66 [_o_n | _o_f_f]
+           sets a switch to cause clients using the map file to try to use
+           IPv6.
+
+     SSOOCCKKSS [_o_n _o_f_f]
+           sets a switch to cause DCC clients using the map to use the SOCKS5
+           protocol, if they have been built with a SOCKS library.  The socks
+           library linked with the DCC client must be configured appropri-
+           ately, often including knowing which DCC servers must be connected
+           via the SOCKS proxy and which can be reached directly.  DCC clients
+           use SOCKS functions such as Rsendto() with all or no servers
+           depending on the setting of this switch.
+
+     ssrrcc [_- | _I_P_a_d_d_r_e_s_s]
+           displays or configures the source address of DCC client requests.
+           _- removes the explicit configuration of the source, while _I_P_a_d_d_r_e_s_s
+           sets it.  This makes sense only on multi-homed hosts.  It can be
+           useful for passing firewalls.
+
+   DDCCCC SSEERRVVEERR CCOOMMMMAANNDDSS
+     Commands that can be sent to a DCC server include the following.  Most of
+     the commands must be used with the server's _I_D specified with the iidd com-
+     mand.  The specified ID is included in the commands sent to the server
+     The command itself is digitally signed with the first password associated
+     with the ID in the _i_d_s file.  The server requires that the signature
+     match one of the passwords associated with the ID in its _i_d_s file.
+
+     ddeellcckk ttyyppee hheexx11 hheexx22 hheexx33 hheexx44
+          asks the server to delete the _t_y_p_e checksum with value _h_e_x_1 _h_e_x_2
+          _h_e_x_3 _h_e_x_4.  The type and checksum values can be found in dccproc(8)
+          and dccm(8) log files or computed with _d_c_c_p_r_o_c --QQCC.
+
+          There are very few situations where it makes sense to bother to
+          delete checksums.  For example, mail that was accidentally reported
+          with a target count of "MANY" is either private and so will not be
+          seen by other people and so will not be affected, or it is bulk and
+          its source so must have already been whitelisted by recipients.
+
+     ssttaattss [_a_l_l | _c_l_e_a_r]
+          displays current status and statistics from the current DCC server
+          or for _a_l_l known DCC servers.  The server's counters will be cleared
+          after they are displayed when the server's ID has been specified
+          with the iidd _I_D operation.
+
+     cclliieennttss [--nnssiiaaVVAAKK] [_m_a_x [_t_h_o_l_d]] [_a_d_d_r[_/_p_r_e_f_i_x]]
+          displays some of the clients recently seen by the server.
+          --nn   displays only the IP addresses and not the names of clients.
+          --ss   sorts the clients by the number of requests they have made.
+          --ii   counts clients with the same client-ID as single entities.
+          --aa   produces 24 hour average numbers of requests.
+          --AA   displays only anonymous clients.
+          --KK   displays only clients using client-IDs.
+          --VV   includes the DCC protocol versions used by clients.
+          _m_a_x  displays only the _m_a_x most recent clients.
+          _m_a_x _t_h_o_l_d displays the most recent _m_a_x clients that have made at
+               least _t_h_o_l_d requests.
+          _a_d_d_r[_/_p_r_e_f_i_x] restricts the results to the DCC client with that IP
+               address or clients with addresses in that CIDR block.
+
+          The mechanism that implements this command involves asking the DCC
+          server for the first approximately 100 clients, then the second
+          about 100, and so on, If entries change position in the complete
+          list maintained by the server between requests, the displayed list
+          will have duplicate or missing entries.  Only clients heard from
+          since ssttaattss cclleeaarr was last used are displayed.
+
+     ssttoopp
+          tells the DCC server to exit.
+
+     ssyysstteemm ssttoopp
+          tells the DCC server to exit so that the operating system can be
+          shut down.  This tells the DCC server on some systems to delete the
+          dcc_db.hash file to speed system shut down.  The file will be
+          rebuilt automatically by ddbbcclleeaann when the DCC server is restarted.
+
+     cclleeaann ssttoopp
+          tells the DCC server to exit after applying fsync() to the database.
+
+     rreellooaadd IIDDss
+          tells the local DCC server to reload its DCC _i_d_s file immediately.
+          This command is not strictly needed.  Every several minutes, the DCC
+          server notices if the file has been changed and automatically reads
+          it.
+
+     fflloooodd cchheecckk
+          tells the DCC server to check for changes in the _f_l_o_d file and try
+          to restart any of the streams to peers that are broken.
+
+     fflloooodd sshhuuttddoowwnn
+          tells the DCC server to cleanly stop flooding checksums to and from
+          peers.  The server will wait for sending and receiving peers to
+          agree to stop.  Each fflloooodd sshhuuttddoowwnn or fflloooodd hhaalltt request increases
+          a count of reasons why the server should not flood checksums.
+
+     fflloooodd hhaalltt
+          tells the DCC server to abruptly stop flooding checksums to and from
+          peers.
+
+     fflloooodd rreewwiinndd _s_e_r_v_e_r_-_I_D
+          tells the DCC server to ask its peer with _s_e_r_v_e_r_-_I_D to rewind and
+          resend its stream of checksums.
+
+     fflloooodd ffffwwdd iinn _s_e_r_v_e_r_-_I_D
+          tells the DCC server to ask its peer to "fast forward" or skip to
+          the end of the incoming flood.
+
+     fflloooodd ffffwwdd oouutt _s_e_r_v_e_r_-_I_D
+          tells the DCC server to "fast forward" or skip to the current end of
+          the flood to its peer.
+
+     fflloooodd rreessuummee
+          tells the DCC server to reduce the number of reasons to not flood
+          checksums increased by fflloooodd sshhuuttddoowwnn and fflloooodd hhaalltt.. When the num-
+          ber of reasons reaches zero, the server tries to resume flooding.
+
+     fflloooodd lliisstt
+          displays the list of current incoming and outgoing floods.  Each
+          line contains the server-ID of the peer, the IP address and port
+          used for the outgoing flood, the address for the incoming flood if
+          different, and the host name.  Only the server-IDs of flooding peers
+          are disclosed with the server's ID.
+
+     fflloooodd ssttaattss [cclleeaarr] { _s_e_r_v_e_r_-_I_D | _a_l_l }
+          displays counts of checksum reports sent and received by the current
+          flooding connections to and from _s_e_r_v_e_r_-_I_D or _a_l_l flooding connec-
+          tions and then optionally clears the counts.
+
+     DDBB cclleeaann
+          is used by ddbbcclleeaann to tell the server that the database expiration
+          has begun.
+
+     DDBB nneeww
+          is used by ddbbcclleeaann to tell the server that the database cleaning is
+          complete.
+
+     fflluusshh ccaacchhee
+          tells the server to flush its cache and to keep it clean.
+
+     ccaacchhee ookk
+          tells the server to resume normal operations after fflluusshh ccaacchhee.
+
+     cclloocckk cchheecckk
+          asks the DCC server to say how much its clock differs from the local
+          clock.
+
+     cclloocckk kklluuddggee ++//--sseeccoonnddss
+          adjusts the timestamps in server commands to make it possible to
+          control servers with inaccurate clocks.
+
+     ttrraaccee _d_e_f_a_u_l_t
+          turns on _A_N_O_N and _C_L_N_T tracing and turns off all others.
+
+     ttrraaccee _m_o_d_e _{_o_n_|_o_f_f_}
+          turns the server's tracing _m_o_d_e on or off.  _M_o_d_e must be one of:
+            _A_D_M_N    administrative requests from ccddcccc
+            _A_N_O_N    errors by anonymous clients
+            _C_L_N_T    errors by authenticated clients
+            _R_L_I_M    rate-limited messages
+            _Q_U_E_R_Y   all queries and reports
+            _R_I_D_C    messages concerning the report-ID cache that is used to
+                    detect duplicate reports from clients
+            _F_L_O_O_D   messages about inter-server flooding connections
+            _F_L_O_O_D_2  messages about flooded reports
+            _I_D_S     unknown server-IDs in flooded reports
+            _B_L      blacklisted clients
+            _D_B      odd database events
+            _W_L_I_S_T   reports of whitelisted checksums from authenticated, not
+                    anonymous DCC clients
+
+     ccddcccc exits with 0 on success, and >0 if an error occurs in operations
+     specified on the command line.
+
+FFIILLEESS
+     /var/dcc  DCC home directory
+     map       memory mapped file in the home DCC home directory of server
+               host names, port numbers, passwords, measured round trip times
+               (RTT), and so forth.
+     ids       list of IDs and passwords, as described in dccd(8).  It is only
+               required by systems running the DCC server, but is used by ccddcccc
+               if available.
+
+SSEEEE AALLSSOO
+     dbclean(8), dcc(8), dccd(8), dblist(8), dccifd(8), dccm(8), dccproc(8),
+     dccsight(8).
+
+HHIISSTTOORRYY
+     Implementation of ccddcccc was started at Rhyolite Software in 2000.  This
+     document describes version 1.3.103.
+
+                               February 26, 2009
diff -r 000000000000 -r c7f6b056b673 cdcc.8.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cdcc.8.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,641 @@
+.\" Copyright (c) 2008 by Rhyolite Software, LLC
+.\"
+.\" This agreement is not applicable to any entity which sells anti-spam
+.\" solutions to others or provides an anti-spam solution as part of a
+.\" security solution sold to other entities, or to a private network
+.\" which employs the DCC or uses data provided by operation of the DCC
+.\" but does not provide corresponding data to other users.
+.\"
+.\" Permission to use, copy, modify, and distribute this software without
+.\" changes for any purpose with or without fee is hereby granted, provided
+.\" that the above copyright notice and this permission notice appear in all
+.\" copies and any distributed versions or copies are either unchanged
+.\" or not called anything similar to "DCC" or "Distributed Checksum
+.\" Clearinghouse".
+.\"
+.\" Parties not eligible to receive a license under this agreement can
+.\" obtain a commercial license to use DCC by contacting Rhyolite Software
+.\" at sales@rhyolite.com.
+.\"
+.\" A commercial license would be for Distributed Checksum and Reputation
+.\" Clearinghouse software.  That software includes additional features.  This
+.\" free license for Distributed ChecksumClearinghouse Software does not in any
+.\" way grant permision to use Distributed Checksum and Reputation Clearinghouse
+.\" software
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+.\" 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.
+.\"
+.\" Rhyolite Software DCC 1.3.103-1.95 $Revision$
+.\"
+.Dd February 26, 2009
+.ds volume-ds-DCC Distributed Checksum Clearinghouse
+.Dt cdcc 8 DCC
+.Os " "
+.Sh NAME
+.Nm cdcc
+.Nd Control Distributed Checksum Clearinghouse
+.Sh SYNOPSIS
+.Nm cdcc
+.Op Fl Vdq
+.Op Fl h Ar homedir
+.Op Fl c Ar ids
+.Op Ar op1 op2 ... Op Ar -
+.Sh DESCRIPTION
+.Nm Cdcc
+is used to clear, control, and query the control file used
+by Distributed Checksum Clearinghouse
+clients such as
+.Xr dccm 8 .
+The host names, UDP port numbers, IDs, and passwords local clients use
+to talk to servers as well as IP addresses, round trip times, and other
+information are contained in the
+.Pa map
+file.
+While
+.Nm
+is set-UID, it uses the real UID only when accessing the
+.Pa map
+file.
+It refuses to display sensitive information such as passwords
+unless the real UID is the same as the effective UID.
+Note that
+.Nm
+needs to be set to a UID that can read and write the
+.Pa map
+file, but that UID need not be 0.
+.Pp
+.Nm Cdcc
+is also used to send commands to DCC servers to tell them
+to stop, reload their lists of DCC IDs, turn on tracing, and so forth.
+.Pp
+Many commands sent to DCC servers require a numeric DCC ID
+and a password recognized by the server.
+A DCC password is a 1-32 character string that does not contain
+blank, tab, newline or carriage return characters.
+The ID is specified with the
+.Ic id
+operation.
+If
+.Nm cdcc
+is run with a real UID that can read the
+.Pa ids
+file and a password is not specified
+(see the
+.Ic password
+operation),
+then the current password for the specified ID in the
+.Pa ids
+file will be used.
+If no
+.Pa ids
+file is available and a password and DCC ID are not specified,
+.Nm
+uses the anonymous DCC client-ID.
+DCC servers do not expect a password from clients using the
+anonymous client-ID,
+but they also won't honor control requests.
+.Pp
+Operations that modify the
+.Pa map
+file can only be performed when
+the real UID is sufficient to modify the file directly.
+Trying to perform an operation that requires a password without
+specifying a server-ID or without using a UID that can access the
+.Pa ids
+file produces an error message complaining
+about a "privileged operation."
+.Pp
+Commands and operations are read from the command line or from stdin.
+A series of
+.Ar op1 op2 ...
+operations followed a
+.Ar -
+(a dash) causes operations to be read from stdin after the command line
+operations are processed.
+Semi-colons or newlines separate commands in UNIX command-line "words,"
+as well as when commands are read from stdin.
+Since each command line operation must be a shell "word," quotes are
+often required as in
+.Bd -ragged -offset indent
+% cdcc
+.Qq load map.txt
+.Ed
+or
+.Bd -ragged -offset indent
+% cdcc
+.Qq host localhost;info
+stats
+.Ed
+.Ss OPTIONS
+The following options are available:
+.Bl -tag -width 3n
+.It Fl V
+displays the version of the DCC controller.
+.It Fl d
+enables debugging output from the DCC client software.
+Additional
+.Fl d
+options increase the number of messages.
+See the
+.Ic debug
+command.
+.It Fl q
+quiets initial complaints about the map file
+and some messages about successful commands.
+See the
+.Ic quiet
+command.
+.It Fl h Ar homedir
+overrides the default DCC home directory,
+.Pa @prefix@ .
+See the
+.Ic homedir
+operation.
+.It Fl c Ar ids
+specifies file containing DCC IDs and passwords known by the local DCC server.
+An
+.Pa ids
+file that can be read by others cannot be used.
+The format of the
+.Pa ids
+file is described in
+.Xr dccd 8 .
+.It Ar op1 op2 ...
+are operations or commands such as "id\ 100;\ stop".
+Commands or operations specified on the command line are performed
+before the first interactive request.
+The last command can be
+.Ar "-"
+to specify that additional commands should be read from stdin.
+.El
+.Ss OPERATIONS
+Local operations include the following:
+.Bl -tag -width info
+.It Ic help Op Ar command
+lists information about one or all available commands and operations.
+.It Ic exit
+stops
+.Nm
+.It Ic grey Op Ar on | off
+switches between DCC and greylist servers.
+.It Ic homedir Op Ar path
+displays or specifies the DCC home directory.
+.It Ic file Op Ar map
+displays or specifies the name or path of the map file.
+The string "-" specifies the default file
+.Pa map
+in the DCC home directory.
+.It Ic new map Op Ar map
+creates a new, empty file for DCC server host names,
+port numbers, passwords, and so forth.
+There must not already be a file of the same name.
+The default is
+.Pa map
+in the DCC home directory.
+.It Ic delete Ar host Ns Xo
+.Ns Op , Ns Ar port
+.Xc
+deletes the entry in the
+.Pa map
+file for
+.Ar host
+and UDP
+.Ar port.
+If greylist mode has been set with the
+.Ic grey\ on
+command,
+the entry for the grelist server at
+.Ar host
+is deleted.
+.It Ic add Ar host Ns Xo
+.Ns Op , Ns Ar port
+.Op Ar RTT+adj Ns | Ns Ar RTT-adj
+.Op Ar Greylist
+.Op Ar client-ID Op password
+.Xc
+adds an entry to the
+.Pa map
+file.
+The
+.Ar port
+can be "-" to specify the default DCC server port number.
+.Pp
+An adjustment to the round trip time is a multiple of 10 milliseconds
+between -4000 and +4000 following the string
+.Ar RTT .
+The adjustment is added to the average measured round trip time when
+the DCC client software picks the "nearest" DCC server, or the server
+with the smallest RTT.
+If an IP address is mentioned more than once in the list of servers,
+for example because it is among the addresses for more than one server name,
+conflicts among RTT adjustments are resolved by picking
+the adjustment with the largest absolute value.
+.Pp
+.Ar Greylist
+marks an entry for a greylist servers.
+.Ar Greylist
+is assumed if greylist mode has been set with
+the
+.Ic grey\ on
+command,
+See
+.Xr dccd 8 .
+.Pp
+If both the client-ID and the password are absent,
+the anonymous client-ID, 1, is used.
+The string
+.Ar anon
+is equivalent to the anonymous client-ID.
+A null password string is assumed if the password is missing
+and the client-ID is 1 or also missing.
+.It Ic load Ar info-file
+loads the current parameter file with the host names, port numbers, IDs, and
+passwords in
+.Ar info-file .
+Standard input is understood if
+.Ar info-file
+is "-".
+.Pp
+A suitable file can be created with the
+.Ic info
+operation.
+It consists of ignored blank or comment lines starting with '#' and
+other lines in the same format as the arguments to the
+.Ic add
+operation.
+Note that output of the
+.Ic info
+command will lack passwords unless it is run by a privileged user.
+.It Ic host Op Ar hostname
+specifies the host name of the DCC server to which commands should be sent.
+If
+.Ar hostname
+is "-", the current default DCC server is chosen.
+.It Ic port Op Ar port
+specifies the UDP port number of the DCC server to which commands should
+be sent.
+The default is 6277 or 6276 depending on the setting of the greylist
+mode controlled with the
+.Ic grey
+command.
+.It Ic password Ar secret
+specifies the password with which to sign commands sent to the DCC
+server specified with the
+.Ic server
+and
+.Ic port
+operations.
+.It Ic id Op Ar ID
+specifies or displays the numeric DCC ID for commands sent to the DCC
+server specified with the
+.Ic server
+and
+.Ic port
+operations.
+If no password is specified with the
+.Ic password
+command,
+the password is sought in the local
+.Pa ids .
+.It Ic info Op Fl N
+displays information about the connections to DCC servers.
+It starts with the current date and name of the current
+.Ar map
+file or
+says that
+.Nm
+is using the implicit file created with the
+.Ic server
+and
+.Ic port
+operations.
+It then says when host names will next be resolved into IP addresses,
+the smallest round trip time to the IP addresses of known DCC servers.
+The host name, UDP port number (or dash if it is the default),
+DCC client-ID, and password (if
+.Nm
+is used by a privileged user)
+are shown in one line per configured DCC server.
+.Pp
+The currently preferred IP address is indicated by an asterisk.
+The "brand" of the server, its DCC ID, and its IP address
+are displayed in one line per IP address.
+The performance of the server at each IP address in the most recent
+32 operations is displayed in a second line.
+The second line ends with the measured delay imposed by the server on requests
+with this client's ID.
+.Pp
+.Fl N
+displays the reverse DNS name of each server.
+.It Ic RTT Op Fl N
+measures the round trip time to the DCC servers.
+It does this by discarding accumulated information and forcing
+a probe of all listed server IP addresses.
+.Pp
+.Em Beware
+that when run with sufficient privilege, the
+.Ic RTT
+operation is like the
+.Ic info
+and
+.Ic load
+operations and displays cleartext passwords.
+.Pp
+.Fl N
+displays the reverse DNS name of each server.
+.It Ic debug Xo
+Op Ar on | off | TTL=x
+.Xc
+increases or decreases debugging information from the DCC client software
+or sets the IP TTL on queries to the server.
+See
+.Fl d .
+.Pp
+Some operating systems do not include the functions required to change the
+IP TTL.
+Others include the required functions
+but have no apparent effect.
+.It Ic quiet Op Ar on | off
+makes commands more quiet or more verbose.
+.It Ic IPv6 Op Ar on | off
+sets a switch to cause clients using the map file to try to use IPv6.
+.It Ic SOCKS Op Ar on off
+sets a switch to cause DCC clients using the map to use the SOCKS5
+protocol, if they have been built with a SOCKS library.
+The socks library linked with the DCC client must be configured appropriately,
+often including knowing which DCC servers must be connected via the
+SOCKS proxy and which can be reached directly.
+DCC clients use SOCKS functions such as Rsendto() with all or no servers
+depending on the setting of this switch.
+.It Ic src Op Ar - | IPaddress
+displays or configures the source address of DCC client requests.
+.Ar -
+removes the explicit configuration of the source, while
+.Ar IPaddress
+sets it.
+This makes sense only on multi-homed hosts.
+It can be useful for passing firewalls.
+.El
+.Pp
+.Ss DCC SERVER COMMANDS
+Commands that can be sent to a DCC server include the following.
+Most of the commands must be used with the server's 
+.Ar ID
+specified with the
+.Ic id
+command.
+The specified ID is included in the commands sent to the server
+The command itself is digitally signed with the first password associated
+with the ID in the
+.Pa ids
+file.
+The server requires that the signature match one of the passwords associated
+with the ID in its
+.Pa ids
+file.
+.Bl -tag -width xxx
+.It Ic delck type hex1 hex2 hex3 hex4
+asks the server to delete the
+.Ar type
+checksum with value
+.Ar hex1 hex2 hex3 hex4 .
+The type and checksum values can be found in
+.Xr dccproc 8
+and
+.Xr dccm 8
+log files
+or computed with
+.Em dccproc Fl QC .
+.Pp
+There are very few situations where it makes sense to bother to delete
+checksums.
+For example, mail that was accidentally reported with a target
+count of "MANY" is either private and so will not be seen by other
+people and so will not be affected, or it is bulk and its source
+so must have already been whitelisted by recipients.
+.It Ic stats Op Ar all | clear
+displays current status and statistics from the current DCC server
+or for
+.Ar all
+known DCC servers.
+The server's counters will be cleared after they are displayed
+when the server's ID has been specified with the
+.Ic id Ar ID
+operation.
+.It Ic clients Xo
+.Op Fl nsiaVAK
+.Op Ar max Op Ar thold
+.Op Ar addr Ns Op Ar /prefix
+.Xc
+displays some of the clients recently seen by the server.
+.Bl -hang -compact -width xxx
+.It Fl n
+displays only the IP addresses and not the names of clients.
+.It Fl s
+sorts the clients by the number of requests they have made.
+.It Fl i
+counts clients with the same client-ID as single entities.
+.It Fl a
+produces 24 hour average numbers of requests.
+.It Fl A
+displays only anonymous clients.
+.It Fl K
+displays only clients using client-IDs.
+.It Fl V
+includes the DCC protocol versions used by clients.
+.It Ar max
+displays only the
+.Ar max
+most recent clients.
+.It Ar max Ar thold
+displays the most recent
+.Ar max
+clients that have made at least
+.Ar thold
+requests.
+.It Ar addr Ns Op Ar /prefix
+restricts the results to the DCC client with that IP address or
+clients with addresses in that CIDR block.
+.El
+.Pp
+The mechanism that implements this command involves
+asking the DCC server for the first approximately 100 clients, then
+the second about 100, and so on,
+If entries change position in the complete list maintained by the server
+between requests,
+the displayed list will have duplicate or missing entries.
+Only clients heard from since
+.Ic stats clear
+was last used are displayed.
+.It Ic stop
+tells the DCC server to exit.
+.It Ic system stop
+tells the DCC server to exit so that the operating system can be shut down.
+This tells the DCC server on some systems to delete the dcc_db.hash file
+to speed system shut down.
+The file will be rebuilt automatically by
+.Nm dbclean
+when the DCC server is restarted.
+.It Ic clean stop
+tells the DCC server to exit after applying fsync() to the database.
+.It Ic reload IDs
+tells the local DCC server to reload its DCC
+.Pa ids
+file immediately.
+This command is not strictly needed.
+Every several minutes, the DCC server notices if the file has been changed
+and automatically reads it.
+.It Ic flood check
+tells the DCC server to check for changes in the
+.Pa flod
+file and try to restart any of the streams to peers that are broken.
+.It Ic flood shutdown
+tells the DCC server to cleanly stop flooding checksums to and from peers.
+The server will wait for sending and receiving peers to agree to stop.
+Each
+.Ic flood shutdown
+or
+.Ic flood halt
+request increases a count of reasons why the server should not
+flood checksums.
+.It Ic flood halt
+tells the DCC server to abruptly stop flooding checksums to and from peers.
+.It Ic flood rewind Ar server-ID
+tells the DCC server to ask its peer with
+.Ar server-ID
+to rewind and resend its stream of checksums.
+.It Ic flood ffwd in Ar server-ID
+tells the DCC server to ask its peer to "fast forward" or skip to
+the end of the incoming flood.
+.It Ic flood ffwd out Ar server-ID
+tells the DCC server to "fast forward" or skip to the current end
+of the flood to its peer.
+.It Ic flood resume
+tells the DCC server to reduce the number of reasons to
+not flood checksums increased by
+.Ic flood shutdown
+and
+.Ic flood halt.
+When the number of reasons reaches zero,
+the server tries to resume flooding.
+.It Ic flood list
+displays the list of current incoming and outgoing floods.
+Each line contains the server-ID of the peer,
+the IP address and port used for the outgoing flood,
+the address for the incoming flood if different,
+and the host name.
+Only the server-IDs of flooding peers are disclosed with the server's ID.
+.It Ic flood stats Xo
+.Op Ic clear
+.No {
+.Ar server-ID | all
+.No }
+.Xc
+displays counts of checksum reports sent and received by the current
+flooding connections to and from
+.Ar server-ID
+or
+.Ar all
+flooding connections
+and then optionally clears the counts.
+.It Ic DB clean
+is used by
+.Nm dbclean
+to tell the server that the database expiration has begun.
+.It Ic DB new
+is used by
+.Nm dbclean
+to tell the server that the database cleaning is complete.
+.It Ic flush cache
+tells the server to flush its cache and to keep it clean.
+.It Ic cache ok
+tells the server to resume normal operations after
+.Ic flush cache .
+.It Ic clock check
+asks the DCC server to say how much its clock differs from the local clock.
+.It Ic clock kludge +/-seconds
+adjusts the timestamps in server commands to make it possible to
+control servers with inaccurate clocks.
+.It Ic trace Ar default
+turns on
+.Ar ANON
+and
+.Ar CLNT
+tracing
+and turns off all others.
+.It Ic trace Ar mode {on|off}
+turns the server's tracing 
+.Ar mode
+on or off.
+.Ar Mode
+must be one of:
+.Bl -tag -width FLOOD2 -offset 2n -compact
+.It Ar ADMN
+administrative requests from
+.Nm
+.It Ar ANON
+errors by anonymous clients
+.It Ar CLNT
+errors by authenticated clients
+.It Ar RLIM
+rate-limited messages
+.It Ar QUERY
+all queries and reports
+.It Ar RIDC
+messages concerning the report-ID cache that is used
+to detect duplicate reports from clients
+.It Ar FLOOD
+messages about inter-server flooding connections
+.It Ar FLOOD2
+messages about flooded reports
+.It Ar IDS
+unknown server-IDs in flooded reports
+.It Ar BL
+blacklisted clients
+.It Ar DB
+odd database events
+.It Ar WLIST
+reports of whitelisted checksums from authenticated, not anonymous DCC clients
+.El
+.El
+.Pp
+.Nm
+exits with 0 on success,
+and >0 if an error occurs in operations specified on the command line.
+.Sh FILES
+.Bl -tag -width @prefix@ -compact
+.It Pa @prefix@
+DCC home directory
+.It Pa map
+memory mapped file in the home DCC home directory of server host names,
+port numbers,
+passwords, measured round trip times (RTT), and so forth.
+.It Pa ids
+list of IDs and passwords, as described in
+.Xr dccd 8 .
+It is only required by systems running the DCC server,
+but is used by
+.Nm
+if available.
+.El
+.Sh SEE ALSO
+.Xr dbclean 8 ,
+.Xr dcc 8 ,
+.Xr dccd 8 ,
+.Xr dblist 8 ,
+.Xr dccifd 8 ,
+.Xr dccm 8 ,
+.Xr dccproc 8 ,
+.Xr dccsight 8 .
+.Sh HISTORY
+Implementation of
+.Nm
+was started at Rhyolite Software in 2000.
+This document describes version 1.3.103.
diff -r 000000000000 -r c7f6b056b673 cdcc.html.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cdcc.html.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,436 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<HTML>
+<HEAD>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <TITLE>cdcc.0.8</TITLE>
+    <META http-equiv="Content-Style-Type" content="text/css">
+    <STYLE type="text/css">
+	BODY {background-color:white; color:black}
+	ADDRESS {font-size:smaller}
+        IMG.logo {width:6em; vertical-align:middle}
+    </STYLE>
+</HEAD>
+<BODY>
+<PRE>
+<!-- Manpage converted by man2html 3.0.1 -->
+<B><A HREF="cdcc.html">cdcc(8)</A></B>               Distributed Checksum Clearinghouse               <B><A HREF="cdcc.html">cdcc(8)</A></B>
+
+
+</PRE>
+<H2><A NAME="NAME">NAME</A></H2><PRE>
+     <B>cdcc</B> -- Control Distributed Checksum Clearinghouse
+
+
+</PRE>
+<H2><A NAME="SYNOPSIS">SYNOPSIS</A></H2><PRE>
+     <B>cdcc</B> [<B>-Vdq</B>] [<B>-h</B> <I>homedir</I>] [<B>-c</B> <I>ids</I>] [<I>op1</I> <I>op2</I> <I>...</I> [<I>-</I>]]
+
+
+</PRE>
+<H2><A NAME="DESCRIPTION">DESCRIPTION</A></H2><PRE>
+     <B>Cdcc</B> is used to clear, control, and query the control file used by Dis-
+     tributed Checksum Clearinghouse clients such as <B><A HREF="dccm.html">dccm(8)</A></B>.  The host names,
+     UDP port numbers, IDs, and passwords local clients use to talk to servers
+     as well as IP addresses, round trip times, and other information are con-
+     tained in the <I>map</I> file.  While <B>cdcc</B> is set-UID, it uses the real UID only
+     when accessing the <I>map</I> file.  It refuses to display sensitive information
+     such as passwords unless the real UID is the same as the effective UID.
+     Note that <B>cdcc</B> needs to be set to a UID that can read and write the <I>map</I>
+     file, but that UID need not be 0.
+
+     <B>Cdcc</B> is also used to send commands to DCC servers to tell them to stop,
+     reload their lists of DCC IDs, turn on tracing, and so forth.
+
+     Many commands sent to DCC servers require a numeric DCC ID and a password
+     recognized by the server.  A DCC password is a 1-32 character string that
+     does not contain blank, tab, newline or carriage return characters.  The
+     ID is specified with the <B>id</B> operation.  If <B>cdcc</B> is run with a real UID
+     that can read the <I>ids</I> file and a password is not specified (see the
+     <B>password</B> operation), then the current password for the specified ID in
+     the <I>ids</I> file will be used.  If no <I>ids</I> file is available and a password
+     and DCC ID are not specified, <B>cdcc</B> uses the anonymous DCC client-ID.  DCC
+     servers do not expect a password from clients using the anonymous client-
+     ID, but they also won't honor control requests.
+
+     Operations that modify the <I>map</I> file can only be performed when the real
+     UID is sufficient to modify the file directly.  Trying to perform an
+     operation that requires a password without specifying a server-ID or
+     without using a UID that can access the <I>ids</I> file produces an error mes-
+     sage complaining about a "privileged operation."
+
+     Commands and operations are read from the command line or from stdin.  A
+     series of <I>op1</I> <I>op2</I> <I>...</I> operations followed a <I>-</I> (a dash) causes operations
+     to be read from stdin after the command line operations are processed.
+     Semi-colons or newlines separate commands in UNIX command-line "words,"
+     as well as when commands are read from stdin.  Since each command line
+     operation must be a shell "word," quotes are often required as in
+
+           % cdcc "load map.txt"
+     or
+
+           % cdcc "host localhost;info" stats
+
+   <A NAME="OPTIONS"><B>OPTIONS</B></A>
+     The following options are available:
+
+     <A NAME="OPTION-V"><B>-V</B></A>   displays the version of the DCC controller.
+
+     <A NAME="OPTION-d"><B>-d</B></A>   enables debugging output from the DCC client software.  Additional
+          <B>-d</B> options increase the number of messages.  See the <B>debug</B> command.
+
+     <A NAME="OPTION-q"><B>-q</B></A>   quiets initial complaints about the map file and some messages about
+          successful commands.  See the <B>quiet</B> command.
+
+     <A NAME="OPTION-h"><B>-h</B></A> <I>homedir</I>
+          overrides the default DCC home directory, <I>@prefix@</I>.  See the <B>homedir</B>
+          operation.
+
+     <A NAME="OPTION-c"><B>-c</B></A> <I>ids</I>
+          specifies file containing DCC IDs and passwords known by the local
+          DCC server.  An <I>ids</I> file that can be read by others cannot be used.
+          The format of the <I>ids</I> file is described in <B><A HREF="dccd.html">dccd(8)</A></B>.
+
+     <I>op1</I> <I>op2</I> <I>...</I>
+          are operations or commands such as "id 100; stop".  Commands or
+          operations specified on the command line are performed before the
+          first interactive request.  The last command can be <I>-</I> to specify
+          that additional commands should be read from stdin.
+
+   <A NAME="OPERATIONS"><B>OPERATIONS</B></A>
+     Local operations include the following:
+
+     <A NAME="OPERATION-help"><B>help</B></A> [<I>command</I>]
+           lists information about one or all available commands and opera-
+           tions.
+
+     <A NAME="OPERATION-exit"><B>exit</B></A>  stops <B>cdcc</B>
+
+     <A NAME="OPERATION-grey"><B>grey</B></A> [<I>on</I> | <I>off</I>]
+           switches between DCC and greylist servers.
+
+     <A NAME="OPERATION-homedir"><B>homedir</B></A> [<I>path</I>]
+           displays or specifies the DCC home directory.
+
+     <A NAME="OPERATION-file"><B>file</B></A> [<I>map</I>]
+           displays or specifies the name or path of the map file.  The string
+           "-" specifies the default file <I>map</I> in the DCC home directory.
+
+     <A NAME="OPERATION-new-map"><B>new map</B></A> [<I>map</I>]
+           creates a new, empty file for DCC server host names, port numbers,
+           passwords, and so forth.  There must not already be a file of the
+           same name.  The default is <I>map</I> in the DCC home directory.
+
+     <A NAME="OPERATION-delete"><B>delete</B></A> <I>host</I>[,<I>port</I>]
+           deletes the entry in the <I>map</I> file for <I>host</I> and UDP <I>port.</I> If
+           greylist mode has been set with the <B>grey on</B> command, the entry for
+           the grelist server at <I>host</I> is deleted.
+
+     <A NAME="OPERATION-add"><B>add</B></A> <I>host</I>[,<I>port</I>] [<I>RTT+adj</I>|<I>RTT-adj</I>] [<I>Greylist</I>] [<I>client-ID</I> [password]]
+           adds an entry to the <I>map</I> file.  The <I>port</I> can be "-" to specify the
+           default DCC server port number.
+
+           An adjustment to the round trip time is a multiple of 10 millisec-
+           onds between -4000 and +4000 following the string <I>RTT</I>.  The adjust-
+           ment is added to the average measured round trip time when the DCC
+           client software picks the "nearest" DCC server, or the server with
+           the smallest RTT.  If an IP address is mentioned more than once in
+           the list of servers, for example because it is among the addresses
+           for more than one server name, conflicts among RTT adjustments are
+           resolved by picking the adjustment with the largest absolute value.
+
+           <I>Greylist</I> marks an entry for a greylist servers.  <I>Greylist</I> is
+           assumed if greylist mode has been set with the <B>grey on</B> command, See
+           <B><A HREF="dccd.html">dccd(8)</A></B>.
+
+           If both the client-ID and the password are absent, the anonymous
+           client-ID, 1, is used.  The string <I>anon</I> is equivalent to the anony-
+           mous client-ID.  A null password string is assumed if the password
+           is missing and the client-ID is 1 or also missing.
+
+     <A NAME="OPERATION-load"><B>load</B></A> <I>info-file</I>
+           loads the current parameter file with the host names, port numbers,
+           IDs, and passwords in <I>info-file</I>.  Standard input is understood if
+           <I>info-file</I> is "-".
+
+           A suitable file can be created with the <B>info</B> operation.  It con-
+           sists of ignored blank or comment lines starting with '#' and other
+           lines in the same format as the arguments to the <B>add</B> operation.
+           Note that output of the <B>info</B> command will lack passwords unless it
+           is run by a privileged user.
+
+     <A NAME="OPERATION-host"><B>host</B></A> [<I>hostname</I>]
+           specifies the host name of the DCC server to which commands should
+           be sent.  If <I>hostname</I> is "-", the current default DCC server is
+           chosen.
+
+     <A NAME="OPERATION-port"><B>port</B></A> [<I>port</I>]
+           specifies the UDP port number of the DCC server to which commands
+           should be sent.  The default is 6277 or 6276 depending on the set-
+           ting of the greylist mode controlled with the <B>grey</B> command.
+
+     <A NAME="OPERATION-password"><B>password</B></A> <I>secret</I>
+           specifies the password with which to sign commands sent to the DCC
+           server specified with the <B>server</B> and <B>port</B> operations.
+
+     <A NAME="OPERATION-id"><B>id</B></A> [<I>ID</I>]
+           specifies or displays the numeric DCC ID for commands sent to the
+           DCC server specified with the <B>server</B> and <B>port</B> operations.  If no
+           password is specified with the <B>password</B> command, the password is
+           sought in the local <I>ids</I>.
+
+     <A NAME="OPERATION-info"><B>info</B></A> [<B>-N</B>]
+           displays information about the connections to DCC servers.  It
+           starts with the current date and name of the current <I>map</I> file or
+           says that <B>cdcc</B> is using the implicit file created with the <B>server</B>
+           and <B>port</B> operations.  It then says when host names will next be
+           resolved into IP addresses, the smallest round trip time to the IP
+           addresses of known DCC servers.  The host name, UDP port number (or
+           dash if it is the default), DCC client-ID, and password (if <B>cdcc</B> is
+           used by a privileged user) are shown in one line per configured DCC
+           server.
+
+           The currently preferred IP address is indicated by an asterisk.
+           The "brand" of the server, its DCC ID, and its IP address are dis-
+           played in one line per IP address.  The performance of the server
+           at each IP address in the most recent 32 operations is displayed in
+           a second line.  The second line ends with the measured delay
+           imposed by the server on requests with this client's ID.
+
+           <B>-N</B> displays the reverse DNS name of each server.
+
+     <A NAME="OPERATION-RTT"><B>RTT</B></A> [<B>-N</B>]
+           measures the round trip time to the DCC servers.  It does this by
+           discarding accumulated information and forcing a probe of all
+           listed server IP addresses.
+
+           <I>Beware</I> that when run with sufficient privilege, the <B>RTT</B> operation
+           is like the <B>info</B> and <B>load</B> operations and displays cleartext pass-
+           words.
+
+           <B>-N</B> displays the reverse DNS name of each server.
+
+     <A NAME="OPERATION-debug"><B>debug</B></A> Op Ar on | off | TTL=x
+           increases or decreases debugging information from the DCC client
+           software or sets the IP TTL on queries to the server.  See <B>-d</B>.
+
+           Some operating systems do not include the functions required to
+           change the IP TTL.  Others include the required functions but have
+           no apparent effect.
+
+     <A NAME="OPERATION-quiet"><B>quiet</B></A> [<I>on</I> | <I>off</I>]
+           makes commands more quiet or more verbose.
+
+     <A NAME="OPERATION-IPv6"><B>IPv6</B></A> [<I>on</I> | <I>off</I>]
+           sets a switch to cause clients using the map file to try to use
+           IPv6.
+
+     <A NAME="OPERATION-SOCKS"><B>SOCKS</B></A> [<I>on</I> <I>off</I>]
+           sets a switch to cause DCC clients using the map to use the SOCKS5
+           protocol, if they have been built with a SOCKS library.  The socks
+           library linked with the DCC client must be configured appropri-
+           ately, often including knowing which DCC servers must be connected
+           via the SOCKS proxy and which can be reached directly.  DCC clients
+           use SOCKS functions such as Rsendto() with all or no servers
+           depending on the setting of this switch.
+
+     <A NAME="OPERATION-src"><B>src</B></A> [<I>-</I> | <I>IPaddress</I>]
+           displays or configures the source address of DCC client requests.
+           <I>-</I> removes the explicit configuration of the source, while <I>IPaddress</I>
+           sets it.  This makes sense only on multi-homed hosts.  It can be
+           useful for passing firewalls.
+
+   <A NAME="DCC-SERVER-COMMANDS"><B>DCC SERVER COMMANDS</B></A>
+     Commands that can be sent to a DCC server include the following.  Most of
+     the commands must be used with the server's <I>ID</I> specified with the <B>id</B> com-
+     mand.  The specified ID is included in the commands sent to the server
+     The command itself is digitally signed with the first password associated
+     with the ID in the <I>ids</I> file.  The server requires that the signature
+     match one of the passwords associated with the ID in its <I>ids</I> file.
+
+     <A NAME="OPERATION-delck-type-hex1-hex2-hex3-hex4"><B>delck type hex1 hex2 hex3 hex4</B></A>
+          asks the server to delete the <I>type</I> checksum with value <I>hex1</I> <I>hex2</I>
+          <I>hex3</I> <I>hex4</I>.  The type and checksum values can be found in <B><A HREF="dccproc.html">dccproc(8)</A></B>
+          and <B><A HREF="dccm.html">dccm(8)</A></B> log files or computed with <I>dccproc</I> <B>-QC</B>.
+
+          There are very few situations where it makes sense to bother to
+          delete checksums.  For example, mail that was accidentally reported
+          with a target count of "MANY" is either private and so will not be
+          seen by other people and so will not be affected, or it is bulk and
+          its source so must have already been whitelisted by recipients.
+
+     <A NAME="OPERATION-stats"><B>stats</B></A> [<I>all</I> | <I>clear</I>]
+          displays current status and statistics from the current DCC server
+          or for <I>all</I> known DCC servers.  The server's counters will be cleared
+          after they are displayed when the server's ID has been specified
+          with the <B>id</B> <I>ID</I> operation.
+
+     <A NAME="OPERATION-clients"><B>clients</B></A> [<B>-nsiaVAK</B>] [<I>max</I> [<I>thold</I>]] [<I>addr</I>[<I>/prefix</I>]]
+          displays some of the clients recently seen by the server.
+          <B>-n</B>   displays only the IP addresses and not the names of clients.
+          <B>-s</B>   sorts the clients by the number of requests they have made.
+          <B>-i</B>   counts clients with the same client-ID as single entities.
+          <B>-a</B>   produces 24 hour average numbers of requests.
+          <B>-A</B>   displays only anonymous clients.
+          <B>-K</B>   displays only clients using client-IDs.
+          <B>-V</B>   includes the DCC protocol versions used by clients.
+          <I>max</I>  displays only the <I>max</I> most recent clients.
+          <I>max</I> <I>thold</I> displays the most recent <I>max</I> clients that have made at
+               least <I>thold</I> requests.
+          <I>addr</I>[<I>/prefix</I>] restricts the results to the DCC client with that IP
+               address or clients with addresses in that CIDR block.
+
+          The mechanism that implements this command involves asking the DCC
+          server for the first approximately 100 clients, then the second
+          about 100, and so on, If entries change position in the complete
+          list maintained by the server between requests, the displayed list
+          will have duplicate or missing entries.  Only clients heard from
+          since <B>stats clear</B> was last used are displayed.
+
+     <A NAME="OPERATION-stop"><B>stop</B></A>
+          tells the DCC server to exit.
+
+     <A NAME="OPERATION-system-stop"><B>system stop</B></A>
+          tells the DCC server to exit so that the operating system can be
+          shut down.  This tells the DCC server on some systems to delete the
+          dcc_db.hash file to speed system shut down.  The file will be
+          rebuilt automatically by <B>dbclean</B> when the DCC server is restarted.
+
+     <A NAME="OPERATION-clean-stop"><B>clean stop</B></A>
+          tells the DCC server to exit after applying fsync() to the database.
+
+     <A NAME="OPERATION-reload-IDs"><B>reload IDs</B></A>
+          tells the local DCC server to reload its DCC <I>ids</I> file immediately.
+          This command is not strictly needed.  Every several minutes, the DCC
+          server notices if the file has been changed and automatically reads
+          it.
+
+     <A NAME="OPERATION-flood-check"><B>flood check</B></A>
+          tells the DCC server to check for changes in the <I>flod</I> file and try
+          to restart any of the streams to peers that are broken.
+
+     <A NAME="OPERATION-flood-shutdown"><B>flood shutdown</B></A>
+          tells the DCC server to cleanly stop flooding checksums to and from
+          peers.  The server will wait for sending and receiving peers to
+          agree to stop.  Each <B>flood shutdown</B> or <B>flood halt</B> request increases
+          a count of reasons why the server should not flood checksums.
+
+     <A NAME="OPERATION-flood-halt"><B>flood halt</B></A>
+          tells the DCC server to abruptly stop flooding checksums to and from
+          peers.
+
+     <A NAME="OPERATION-flood-rewind"><B>flood rewind</B></A> <I>server-ID</I>
+          tells the DCC server to ask its peer with <I>server-ID</I> to rewind and
+          resend its stream of checksums.
+
+     <A NAME="OPERATION-flood-ffwd-in"><B>flood ffwd in</B></A> <I>server-ID</I>
+          tells the DCC server to ask its peer to "fast forward" or skip to
+          the end of the incoming flood.
+
+     <A NAME="OPERATION-flood-ffwd-out"><B>flood ffwd out</B></A> <I>server-ID</I>
+          tells the DCC server to "fast forward" or skip to the current end of
+          the flood to its peer.
+
+     <A NAME="OPERATION-flood-resume"><B>flood resume</B></A>
+          tells the DCC server to reduce the number of reasons to not flood
+          checksums increased by <B>flood shutdown</B> and <B>flood halt.</B> When the num-
+          ber of reasons reaches zero, the server tries to resume flooding.
+
+     <A NAME="OPERATION-flood-list"><B>flood list</B></A>
+          displays the list of current incoming and outgoing floods.  Each
+          line contains the server-ID of the peer, the IP address and port
+          used for the outgoing flood, the address for the incoming flood if
+          different, and the host name.  Only the server-IDs of flooding peers
+          are disclosed with the server's ID.
+
+     <A NAME="OPERATION-flood-stats"><B>flood stats</B></A> [<B>clear</B>] { <I>server-ID</I> | <I>all</I> }
+          displays counts of checksum reports sent and received by the current
+          flooding connections to and from <I>server-ID</I> or <I>all</I> flooding connec-
+          tions and then optionally clears the counts.
+
+     <A NAME="OPERATION-DB-clean"><B>DB clean</B></A>
+          is used by <B>dbclean</B> to tell the server that the database expiration
+          has begun.
+
+     <A NAME="OPERATION-DB-new"><B>DB new</B></A>
+          is used by <B>dbclean</B> to tell the server that the database cleaning is
+          complete.
+
+     <A NAME="OPERATION-flush-cache"><B>flush cache</B></A>
+          tells the server to flush its cache and to keep it clean.
+
+     <A NAME="OPERATION-cache-ok"><B>cache ok</B></A>
+          tells the server to resume normal operations after <B>flush cache</B>.
+
+     <A NAME="OPERATION-clock-check"><B>clock check</B></A>
+          asks the DCC server to say how much its clock differs from the local
+          clock.
+
+     <B>clock kludge +/-seconds</B>
+          adjusts the timestamps in server commands to make it possible to
+          control servers with inaccurate clocks.
+
+     <A NAME="OPERATION-trace"><B>trace</B></A> <I>default</I>
+          turns on <I>ANON</I> and <I>CLNT</I> tracing and turns off all others.
+
+     <A NAME="OPERATION-trace"><B>trace</B></A> <I>mode</I> <I>{on|off}</I>
+          turns the server's tracing <I>mode</I> on or off.  <I>Mode</I> must be one of:
+            <I>ADMN</I>    administrative requests from <B>cdcc</B>
+            <I>ANON</I>    errors by anonymous clients
+            <I>CLNT</I>    errors by authenticated clients
+            <I>RLIM</I>    rate-limited messages
+            <I>QUERY</I>   all queries and reports
+            <I>RIDC</I>    messages concerning the report-ID cache that is used to
+                    detect duplicate reports from clients
+            <I>FLOOD</I>   messages about inter-server flooding connections
+            <I>FLOOD2</I>  messages about flooded reports
+            <I>IDS</I>     unknown server-IDs in flooded reports
+            <I>BL</I>      blacklisted clients
+            <I>DB</I>      odd database events
+            <I>WLIST</I>   reports of whitelisted checksums from authenticated, not
+                    anonymous DCC clients
+
+     <A NAME="OPERATION-cdcc"><B>cdcc</B></A> exits with 0 on success, and &gt;0 if an error occurs in operations
+     specified on the command line.
+
+
+</PRE>
+<H2><A NAME="FILES">FILES</A></H2><PRE>
+     <A NAME="FILE-@prefix@">@prefix@</A>  DCC home directory
+     <A NAME="FILE-map">map</A>       memory mapped file in the home DCC home directory of server
+               host names, port numbers, passwords, measured round trip times
+               (RTT), and so forth.
+     <A NAME="FILE-ids">ids</A>       list of IDs and passwords, as described in <B><A HREF="dccd.html">dccd(8)</A></B>.  It is only
+               required by systems running the DCC server, but is used by <B>cdcc</B>
+               if available.
+
+
+</PRE>
+<H2><A NAME="SEE-ALSO">SEE ALSO</A></H2><PRE>
+     <B><A HREF="dbclean.html">dbclean(8)</A></B>, <B><A HREF="dcc.html">dcc(8)</A></B>, <B><A HREF="dccd.html">dccd(8)</A></B>, <B><A HREF="dblist.html">dblist(8)</A></B>, <B><A HREF="dccifd.html">dccifd(8)</A></B>, <B><A HREF="dccm.html">dccm(8)</A></B>, <B><A HREF="dccproc.html">dccproc(8)</A></B>,
+     <B><A HREF="dccsight.html">dccsight(8)</A></B>.
+
+
+</PRE>
+<H2><A NAME="HISTORY">HISTORY</A></H2><PRE>
+     Implementation of <B>cdcc</B> was started at Rhyolite Software in 2000.  This
+     document describes version 1.3.103.
+
+                               February 26, 2009
+</PRE>
+<HR>
+<ADDRESS>
+Man(1) output converted with
+<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
+modified for the DCC $Date 2001/04/29 03:22:18 $
+<BR>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG SRC="http://logos.dcc-servers.net/border.png"
+            class=logo ALT="DCC logo">
+    </A>
+<A HREF="http://validator.w3.org/check?uri=referer">
+    <IMG class=logo ALT="Valid HTML 4.01 Strict"
+        SRC="http://www.w3.org/Icons/valid-html401">
+    </A>
+</ADDRESS>
+</BODY>
+</HTML>
diff -r 000000000000 -r c7f6b056b673 cdcc/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cdcc/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,4 @@
+Makefile.in
+cdcc.c
+win32.mak
+.manifest
diff -r 000000000000 -r c7f6b056b673 cdcc/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cdcc/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,51 @@
+# make the Distributed Checksum Clearinghouse server controller
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.12 $Revision$
+# @configure_input@
+
+DEPTH	=..
+PROG	=cdcc
+SRCS	=$(PROG).c
+
+@MAKE_PROG@
+
+@MAKE_DOT@ifndef NO_SUID
+# cdcc needs to be SUID to read the server passwords
+BINMODE	=4$(DCC_MODE)
+BINOWN	=@DCCSUID@
+@MAKE_DOT@endif
+@MAKE_INC2@
diff -r 000000000000 -r c7f6b056b673 cdcc/cdcc.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cdcc/cdcc.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,2752 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * control dcc server
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.227 $Revision$
+ */
+
+#include "dcc_ck.h"
+#include "dcc_xhdr.h"
+#include "dcc_heap_debug.h"
+#include "dcc_ids.h"
+#ifndef DCC_WIN32
+#include <arpa/inet.h>
+#endif
+
+static DCC_EMSG dcc_emsg;
+static DCC_FNM_LNO_BUF fnm_buf;
+
+static DCC_CLNT_CTXT *ctxt;
+
+static DCC_PATH info_map_nm = DCC_MAP_NM_DEF;
+static const char *ids_nm;
+static time_t clock_kludge;
+static const char *homedir;
+static DCC_PASSWD passwd;
+static u_char passwd_set;
+static DCC_IP src;
+static DCC_SRVR_NM srvr = DCC_SRVR_NM_DEF;
+static u_char port_set;
+static DCC_CLNT_ID srvr_clnt_id = DCC_ID_ANON;
+static enum WHICH_MAP {MAP_TMP, MAP_INFO} which_map = MAP_INFO;
+static u_char map_changed = 1;
+
+static u_char info_flags;
+
+static u_char grey_set;
+
+static u_char quiet;
+
+
+static u_char do_cmds(char *);
+static void set_which_map(enum WHICH_MAP);
+static u_char init_map(u_char, u_char);
+
+struct cmd_tbl_entry;
+/* -1=display help message, 0=command failed, 1=success */
+typedef int CMD (const char *, const struct cmd_tbl_entry *);
+typedef struct cmd_tbl_entry {
+    const char	*cmd;
+    CMD		(*fnc);
+    u_char	args;			/* 0=optional, 1=required, 2=none */
+    u_char	privileged;		/* 1=must have server's password */
+    u_char	write_map;		/* 1=write map, 2=write /var/dcc/map */
+    const char	*help_str;
+} CMD_TBL_ENTRY;
+
+static CMD help_cmd;
+static CMD exit_cmd;
+static CMD grey_cmd;
+static CMD file_cmd;
+static CMD new_map_cmd;
+static CMD delete_cmd;
+static CMD add_cmd;
+static CMD load_cmd;
+static CMD host_cmd;
+static CMD port_cmd;
+static CMD passwd_cmd;
+static CMD id_cmd;
+static CMD homedir_cmd;
+static CMD debug_cmd;
+static CMD quiet_cmd;
+static CMD no_fail_cmd;
+static CMD ckludge_cmd;
+static CMD ipv6_cmd;
+static CMD src_cmd;
+static CMD socks_cmd;
+static CMD info_cmd;
+static CMD rtt_cmd;
+static CMD delck_cmd;
+static CMD sleep_cmd;
+static CMD clients_cmd;
+static CMD anon_cmd;
+static CMD flod_rewind;
+static CMD ffwd_in;
+static CMD ffwd_out;
+static CMD flod_stats;
+static CMD stats_cmd;
+static const char *stats_help;
+static CMD clock_ck_cmd;
+static CMD trace_def;
+
+static const CMD_TBL_ENTRY cmds_tbl[] = {
+    {"help",	    help_cmd,     0, 0, 0, "help [cmd]"},
+    {"?",	    help_cmd,     0, 0, 0, 0},
+    {"exit",	    exit_cmd,     2, 0, 0, "exit"},
+    {"quit",	    exit_cmd,     2, 0, 0, 0},
+    {"grey",	    grey_cmd,     0, 0, 0, "grey [on|off]"},
+    {"homedir",	    homedir_cmd,  0, 0, 0, "homedir [path]"},
+    {"file",	    file_cmd,     0, 0, 0, "file [map]"},
+    {"map",	    file_cmd,     0, 0, 0, 0},
+    {"new map",	    new_map_cmd,  0, 0, 0, "new map [map]"},
+    {"delete",	    delete_cmd,   1, 0, 2, "delete host[,port]"},
+    {"add",	    add_cmd,      1, 0, 2,
+	    "add host,[port|-] [RTT+/-#] [ID [passwd]]"},
+    {"load",	    load_cmd,     1, 0, 2, "load {info-file | -}"},
+    {"host",	    host_cmd,     0, 0, 0, "host [hostname]"},
+    {"server",	    host_cmd,	  0, 0, 0, 0},
+    {"port",	    port_cmd,     0, 0, 0, "port #"},
+    {"password",    passwd_cmd,   0, 0, 0, "password secret"},
+    {"passwd",	    passwd_cmd,   0, 0, 0, 0},
+    {"id",	    id_cmd,	  0, 0, 0, "id [ID]"},
+    {"debug",	    debug_cmd,    0, 0, 0, "debug [on|off|TTL=x]"},
+    {"quiet",	    quiet_cmd,	  0, 0, 0, "-quiet [on|off]"},
+    {"no fail",	    no_fail_cmd,  2, 0, 2, "-no fail"},
+    {"clock kludge",ckludge_cmd,  0, 0, 0, "clock kludge +/-secs"},
+    {"IPv6",	    ipv6_cmd,	  0, 0, 0, "IPv6 [on|off]"},
+    {"src",	    src_cmd,	  0, 0, 0, "src [-|IPaddress]"},
+    {"SOCKS",	    socks_cmd,	  0, 0, 0, "SOCKS [on|off]"},
+    {"info",	    info_cmd,     0, 0, 0, "info [-N]"},
+    {"RTT",	    rtt_cmd,      0, 0, 0, "RTT [-N]"},
+    {"delck",	    delck_cmd,    1, 1, 0, "delck type hex1..4"},
+    {"sleep",	    sleep_cmd,	  1, 0, 0, "sleep sec.onds"},
+    {"clients",	    clients_cmd,  0, 0, 0,
+	    "clients [-nsiaVAK] [max [thold [addr/prefix]]]"},
+    {"anon delay",  anon_cmd,	  0, 0, 0, "\nanon delay [delay[,inflate]]"},
+    {"flood rewind",flod_rewind,  1, 1, 0, "flood rewind ID"},
+    {"flod rewind", flod_rewind,  1, 1, 0, 0},
+    {"flood FFWD in",ffwd_in,	  1, 1, 0, "flood FFWD in ID"},
+    {"flod FFWD in",ffwd_in,	  1, 1, 0, 0},
+    {"flood FFWD out",ffwd_out,	  1, 1, 0, "flood FFWD out ID"},
+    {"flod FFWD out",ffwd_out,	  1, 1, 0, 0},
+    {"flood stats", flod_stats,	  1, 1, 0, "flood stats [clear] {ID|all}"},
+    {"flod stats",  flod_stats,	  1, 1, 0, 0},
+    {"stats",	    stats_cmd,	  0, 0, 0, "stats [clear|all]"},
+    {"status",	    stats_cmd,	  0, 0, 0, 0},
+    {"clock check", clock_ck_cmd, 0, 0, 0, "clock check"},
+    {"trace default",trace_def,	  2, 1, 0, "trace default"},
+};
+
+
+#define PRV_MSG ";\n"		\
+"   use the \"id server-ID\" command\n"	\
+"   and either \"password secret\" command or `su` to read passwords from %s"
+
+
+static DCC_OP_RESP aop_resp;
+static struct timeval op_start, op_end;
+static DCC_SOCKU op_result_su;
+
+static struct {
+    const char	*op;
+    const char	*help_str;
+    DCC_AOPS	aop;
+    u_char	privileged;
+    u_int32_t	val;
+} aops_tbl[] = {
+#define TMAC(s,b) \
+    {"trace "#s" on",	"trace "#s" {on|off}",			\
+			    DCC_AOP_TRACE_ON, 1, DCC_TRACE_##b},\
+    {"trace "#s" off",	0, DCC_AOP_TRACE_OFF,	1, DCC_TRACE_##b}
+    TMAC(admn,ADMN_BIT),
+    TMAC(anon,ANON_BIT),
+    TMAC(clnt,CLNT_BIT),
+    TMAC(rlim,RLIM_BIT),
+    TMAC(query,QUERY_BIT),
+    TMAC(ridc,RIDC_BIT),
+    TMAC(flood,FLOD_BIT),
+    TMAC(flood2,FLOD2_BIT),
+    TMAC(ids,IDS_BIT),
+    TMAC(bl,BL_BIT),
+    TMAC(db,DB_BIT),
+    TMAC(wlist,WLIST_BIT),
+#undef TMAC
+
+    {"stop",		"",	DCC_AOP_STOP,	    1, 0},
+    {"system stop",	"",	DCC_AOP_STOP,	    1, 1},
+    {"clean stop",	"",	DCC_AOP_STOP,	    1, 2},
+    {"flood check",	"",	DCC_AOP_FLOD,	    1, DCC_AOP_FLOD_CHECK},
+    {"flod check",	0,	DCC_AOP_FLOD,	    1, DCC_AOP_FLOD_CHECK},
+    {"flood shutdown",	"",	DCC_AOP_FLOD,	    1, DCC_AOP_FLOD_SHUTDOWN},
+    {"flood shutdown",	0,	DCC_AOP_FLOD,	    1, DCC_AOP_FLOD_SHUTDOWN},
+    {"flood halt",	"",	DCC_AOP_FLOD,	    1, DCC_AOP_FLOD_HALT},
+    {"flood halt",	0,	DCC_AOP_FLOD,	    1, DCC_AOP_FLOD_HALT},
+    {"flood resume",	"",	DCC_AOP_FLOD,	    1, DCC_AOP_FLOD_RESUME},
+    {"flood resume",	0,	DCC_AOP_FLOD,	    1, DCC_AOP_FLOD_RESUME},
+    {"flood list",	"",	DCC_AOP_FLOD,	    0, DCC_AOP_FLOD_LIST},
+    {"flood list",	0,	DCC_AOP_FLOD,	    0, DCC_AOP_FLOD_LIST},
+    {"DB clean",	"",	DCC_AOP_DB_CLEAN,   1, 0},
+    {"DB new",		"",	DCC_AOP_DB_NEW,	    1, 0},
+    {"DB flush cache",	"",	DCC_AOP_DB_UNLOAD,  1, 0},
+    {"DB cache ok",	"",	DCC_AOP_DB_UNLOAD,  1, 1},
+};
+
+
+static void NRATTRIB
+usage(void)
+{
+	dcc_logbad(EX_USAGE,
+		   "usage: [-Vdq] [-h homedir] [-c ids] [op1 [op2] ... ]\n");
+}
+
+
+
+int NRATTRIB
+main(int argc, char **argv)
+{
+	char cmd_buf[500];
+	int i;
+
+	srvr.port = htons(DCC_SRVR_PORT);
+
+	dcc_init_priv();
+	dcc_syslog_init(0, argv[0], 0);
+
+	while ((i = getopt(argc, argv, "Vdqh:c:")) != -1) {
+		switch (i) {
+		case 'V':
+			fprintf(stderr, DCC_VERSION"\n");
+			break;
+
+		case 'd':
+			++dcc_clnt_debug;
+			break;
+
+		case 'q':
+			++quiet;
+			break;
+
+		case 'h':
+			homedir = optarg;
+			break;
+
+		case 'c':
+			ids_nm = optarg;
+			break;
+
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	dcc_clnt_unthread_init();
+	dcc_cdhome(0, homedir, 1);
+	set_ids_path(0, ids_nm);
+	dcc_wf_init(&cmn_wf, 0);
+
+	dcc_all_srvrs = 1;
+
+	if (!init_map(!quiet, 0))
+		set_which_map(MAP_TMP);
+
+	/* with a list of commands, act as a batch utility */
+	if (argc != 0) {
+		for (;;) {
+			/* a final arg of "-" says switch to interactive mode */
+			if (argc == 1 && !strcmp(*argv, "-"))
+				break;
+
+			if (!do_cmds(*argv)) {
+				fputs(" ?\n", stderr);
+				exit(EX_UNAVAILABLE);
+			}
+			assert_ctxts_unlocked();
+			assert_info_unlocked();
+
+			++argv;
+			if (!--argc) {
+				exit(EX_OK);
+			}
+		}
+	}
+
+	/* Without an arg list of commands, look for commands from STDIN.
+	 * Commands end with a semicolon or newline. */
+	for (;;) {
+		assert_ctxts_unlocked();
+		assert_info_unlocked();
+		printf("cdcc %s> ",
+		       which_map == MAP_INFO ? info_map_nm : "-");
+		fflush(stderr);
+		fflush(stdout);
+		if (!fgets(cmd_buf, sizeof(cmd_buf), stdin)) {
+			fputc('\n', stdout);
+			exit(EX_OK);
+		}
+		if (!do_cmds(cmd_buf))
+			fputs(" ?\n", stderr);
+	}
+}
+
+
+
+/* see if don't need a server-ID password, have one or if we can get it */
+static u_char				/* 0=failed, 1=ok */
+get_passwd(u_char privileged)		/* 0=no privileges need, 1=need power */
+{
+	const ID_TBL *srvr_clnt_tbl;
+
+	srvr.clnt_id = srvr_clnt_id;
+	if (passwd_set) {
+		/* set to use the manual password */
+		memcpy(srvr.passwd, passwd, sizeof(srvr.passwd));
+		/* succeed if this is not a priviledge command
+		 * or if we won't be trying to get the server to do
+		 * something priviledge for the anonymous client-ID */
+		return (!privileged || srvr.clnt_id != DCC_ID_ANON);
+	}
+	memset(srvr.passwd, 0, sizeof(srvr.passwd));
+
+	/* fail if we would be trying to get the server to do something
+	 * powerful for the anonymous client-ID */
+	if (srvr.clnt_id == DCC_ID_ANON)
+		return !privileged;
+
+	/* Fetch the common server passwords only if we can read them without
+	 * set-UID.  This keeps random local users from attacking local
+	 * or remote servers with privileged commands, but does not slow
+	 * down privilege users who could use an editor to read and use
+	 * the cleartext passwords manually. */
+	dcc_rel_priv();
+	if (0 > access(ids_path, R_OK) && errno == EACCES) {
+		srvr.clnt_id = DCC_ID_ANON;
+		if (!privileged)
+			return 1;
+		dcc_error_msg("access(%s): %s",
+			      fnm2abs_err(0, ids_path), ERROR_STR());
+		return 0;
+	}
+
+	if (0 >= load_ids(dcc_emsg, srvr_clnt_id, &srvr_clnt_tbl, 1)) {
+		if (srvr_clnt_id != DCC_ID_ANON && privileged)
+			dcc_error_msg("%s", dcc_emsg);
+		srvr.clnt_id = DCC_ID_ANON;
+		return !privileged;
+	}
+	if (srvr_clnt_tbl)
+		memcpy(srvr.passwd, srvr_clnt_tbl->cur_passwd,
+		       sizeof(srvr.passwd));
+	return 1;
+}
+
+
+
+static void
+set_which_map(enum WHICH_MAP new)
+{
+	/* release things even if nothing seems to be changing
+	 * to ensure that we bind a new socket */
+	if (ctxt) {
+		dcc_ctxts_lock();
+		if (dcc_clnt_info)
+			dcc_unmap_close_info(0);
+		if (ctxt) {
+			dcc_rel_ctxt(ctxt);
+			ctxt = 0;
+		}
+		dcc_ctxts_unlock();
+	}
+
+	map_changed = 1;
+	which_map = new;
+	if (new == MAP_INFO) {
+		passwd_set = 0;
+		src.family = AF_UNSPEC;
+	}
+}
+
+
+
+static u_char
+cdcc_unlock(u_char complain)
+{
+	u_char result;
+
+	result = dcc_info_unlock(dcc_emsg);
+	dcc_ctxts_unlock();
+	if (!result && complain)
+		dcc_error_msg("%s", dcc_emsg);
+	return result;
+}
+
+
+
+/* start talking to the local map file */
+static u_char				/* 0=failed 1=mapped and locked */
+init_map(u_char complain,
+	 u_char lock)			/* 1=keep both locks on success */
+{
+	u_char result;
+
+	info_flags = 0;
+
+	dcc_emsg[0] = '\0';
+	dcc_ctxts_lock();
+	if (which_map == MAP_TMP) {
+		result = (dcc_map_tmp_info(dcc_emsg, &srvr, &src, info_flags)
+			  && dcc_info_lock(dcc_emsg));
+	} else {
+		result = dcc_map_lock_info(dcc_emsg, info_map_nm, -1);
+	}
+	if (result) {
+		info_flags = dcc_clnt_info->flags;
+		if (!lock)
+			result = cdcc_unlock(complain);
+	} else {
+		dcc_ctxts_unlock();
+		if (complain)
+			dcc_error_msg("%s", dcc_emsg);
+	}
+	return result;
+}
+
+
+
+/* get ready start talking to a DCC server */
+static u_char				/* 0=failed, 1=ok */
+rdy_ctxt(DCC_CLNT_FGS fgs)
+{
+	u_char rdy_done, need_unlock;
+
+	info_flags = 0;
+
+	if (grey_on)
+		fgs |= DCC_CLNT_FG_GREY;
+	else
+		fgs &= ~DCC_CLNT_FG_GREY;
+	fgs |= DCC_CLNT_FG_NO_FAIL;
+
+	if (!dcc_clnt_info && ctxt) {
+		dcc_rel_ctxt(ctxt);
+		ctxt = 0;
+	}
+
+	if (ctxt) {
+		rdy_done = 0;
+	} else {
+		if (which_map == MAP_TMP) {
+			/* create a new temporary map */
+			ctxt = dcc_tmp_clnt_init(dcc_emsg, ctxt, &srvr, &src,
+						 fgs, 0);
+		} else {
+			/* open official map file */
+			ctxt = dcc_clnt_init(dcc_emsg, ctxt, info_map_nm, fgs);
+		}
+		if (!ctxt) {
+			dcc_error_msg("%s", dcc_emsg);
+			return 0;
+		}
+		rdy_done = 1;
+	}
+
+	if (!grey_set && dcc_clnt_info
+	    && !grey_on
+	    && dcc_clnt_info->dcc.nms[0].hostname[0] == '\0'
+	    && dcc_clnt_info->grey.nms[0].hostname[0] != '\0') {
+		grey_on = 1;
+		fgs |= DCC_CLNT_FG_GREY;
+		rdy_done = 0;
+	}
+
+	dcc_ctxts_lock();
+	if (rdy_done) {
+		need_unlock = 0;
+	} else {
+		dcc_emsg[0] = '\0';
+		need_unlock = dcc_clnt_rdy(dcc_emsg, ctxt, fgs);
+		if (!dcc_clnt_info) {
+			dcc_rel_ctxt(ctxt);
+			ctxt = 0;
+			dcc_ctxts_unlock();
+			dcc_error_msg("%s", dcc_emsg);
+			return 0;
+		}
+	}
+	info_flags = dcc_clnt_info->flags;
+	if (!(fgs & DCC_CLNT_FG_NO_PICK_SRVR))
+		map_changed = 0;
+
+	if (need_unlock && !dcc_info_unlock(0)) {
+		dcc_rel_ctxt(ctxt);
+		ctxt = 0;
+		dcc_ctxts_unlock();
+		dcc_error_msg("%s", dcc_emsg);
+		return 0;
+	}
+
+	/* check the other (greylist or not) server */
+	if (which_map != MAP_TMP) {
+		dcc_emsg[0] = '\0';
+		if (!dcc_clnt_rdy(dcc_emsg, ctxt, fgs ^ DCC_CLNT_FG_GREY)) {
+			if (dcc_clnt_debug > 1)
+				dcc_error_msg("%s", dcc_emsg);
+		} else {
+			dcc_info_unlock(0);
+		}
+	}
+
+	dcc_ctxts_unlock();
+	return 1;
+}
+
+
+
+static void
+fix_info(DCC_SRVR_CLASS *class)
+{
+	map_changed = 1;
+
+	if (class) {
+		dcc_force_measure_rtt(class);
+	} else {
+		dcc_force_measure_rtt(&dcc_clnt_info->dcc);
+		dcc_force_measure_rtt(&dcc_clnt_info->grey);
+	}
+	cdcc_unlock(1);
+
+	/* repair addresses in the real map file now */
+	if (!quiet
+	    && which_map == MAP_INFO)
+		rdy_ctxt(DCC_CLNT_FG_BAD_SRVR_OK);
+}
+
+
+
+/* compare ignoring case */
+static const char *			/* 0 or mismatch in str */
+cmd_cmp(const char *str, const char *op)
+{
+	char op_c, str_c;
+	int len;
+
+	len = 0;
+	for (;;) {
+		op_c = *op;
+		/* avoid tolower() to avoid build hassles on odd systems */
+		if (op_c >= 'A' && op_c <= 'Z')
+			op_c += 'a'-'A';
+		str_c = *str;
+		if (str_c == '\t')
+			str_c = ' ';
+		else if (str_c >= 'A' && str_c <= 'Z')
+			str_c += 'a'-'A';
+		if (op_c != str_c) {
+			/* compress bursts of blanks */
+			if (str_c == ' ' && len != 0 && *(op-1) == ' ') {
+				++str;
+				continue;
+			}
+			return str;
+		}
+		if (op_c == '\0')
+			return 0;
+		++op;
+		++str;
+		++len;
+	}
+}
+
+
+
+/* Display our name for the server and its address,
+ * while suppressing some duplicates */
+static void
+print_aop(SRVR_INX srvr_inx)		/* -1 or server index */
+{
+	const DCC_SRVR_CLASS *class;
+	char date_buf[40];
+	char sustr[DCC_SU2STR_SIZE];
+	const char *srvr_nm;
+	NAM_INX nam_inx;
+
+	dcc_su2str2(sustr, sizeof(sustr), &op_result_su);
+	class = DCC_GREY2CLASS(grey_on);
+	/* Display the preferred server if srvr_inx is NO_SRVR */
+	if (!GOOD_SRVR(class, srvr_inx))
+		srvr_inx = class->srvr_inx;
+	if (GOOD_SRVR(class, srvr_inx)
+	    && (GOOD_NAM(nam_inx = class->addrs[srvr_inx].nam_inx))) {
+		srvr_nm = class->nms[nam_inx].hostname;
+		if (strcmp(srvr_nm, sustr)) {
+			fputs(srvr_nm, stdout);
+			putchar(' ');
+		}
+		printf("%s\n        server-ID %d",
+		       dcc_su2str_err(&op_result_su),
+		       class->addrs[srvr_inx].srvr_id);
+	} else {
+		printf("%s\n                    ",
+		       dcc_su2str_err(&op_result_su));
+	}
+	if (srvr.clnt_id != DCC_ID_ANON)
+		printf("  client-ID %d", srvr.clnt_id);
+	if (which_map == MAP_INFO)
+		printf("  %s", info_map_nm);
+	dcc_time2str(date_buf, sizeof(date_buf), "  %X",
+		     op_start.tv_sec);
+	fputs(date_buf, stdout);
+	putchar('\n');
+}
+
+
+
+static u_char				/* 0=some kind of problem, 1=done */
+start_aop(DCC_AOPS aop, u_int32_t val1, SRVR_INX srvr_inx)
+{
+	DCC_OPS result;
+
+	if (!rdy_ctxt(0))
+		return 0;
+
+	gettimeofday(&op_start, 0);
+	result = dcc_aop(dcc_emsg, ctxt, grey_on ? DCC_CLNT_FG_GREY : 0,
+			 srvr_inx, clock_kludge,
+			 aop, val1, 0, 0, 0, 0, 0, &aop_resp, &op_result_su);
+	gettimeofday(&op_end, 0);
+
+	if (result == DCC_OP_INVALID
+	    || result == DCC_OP_ERROR) {
+		dcc_error_msg("%s", dcc_emsg);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+static void
+fin_aop(SRVR_INX srvr_inx,		/* index of server */
+	u_char psrvr)			/* 1=print server name */
+{
+	if (quiet && !dcc_clnt_debug)
+		return;
+
+	if (psrvr)
+		print_aop(srvr_inx);
+
+	/* say what the server had to say */
+	if (aop_resp.resp.val.string[0] >= ' '
+	    && aop_resp.resp.val.string[0] < 0x7f) {
+		fputs(aop_resp.resp.val.string, stdout);
+		putchar('\n');
+	}
+
+	if (dcc_clnt_debug) {
+		printf("%.2f ms\n",
+		       ((op_end.tv_sec-op_start.tv_sec)*1000.0
+			+ (op_end.tv_usec-op_start.tv_usec)/1000.0));
+	}
+	putchar('\n');
+}
+
+
+
+static u_char				/* 0=some kind of problem, 1=done */
+do_aop(DCC_AOPS aop, u_int32_t val1, SRVR_INX srvr_inx, u_char psrvr)
+{
+	if (!start_aop(aop, val1, srvr_inx))
+		return 0;
+	fin_aop(srvr_inx, psrvr);
+	return 1;
+}
+
+
+
+static u_char				/* 0=not enough power */
+ck_cmd_priv(const CMD_TBL_ENTRY *ce,
+	    u_char privileged,		/* 1=need good server-ID & password */
+	    u_char write_map)		/* 1=write map, 2=write /var/dcc/map */
+{
+	/* always call get_passwd() so we have always fetched a password
+	 * fail if this command needs a good server-ID and password */
+	if (!get_passwd(privileged)) {
+		dcc_error_msg("\"%s\" is a privileged server command"PRV_MSG,
+			      ce->cmd, ids_path);
+		return 0;
+	}
+
+	if (!write_map)
+		return 1;
+
+	/* we can always write to our own throw-away map file */
+	if (write_map == 1 && which_map == MAP_TMP)
+		return 1;
+
+	if (0 > access(info_map_nm, R_OK)
+	    && errno != ENOENT && errno != ENOTDIR) {
+		dcc_error_msg("\"%s\" is a privileged command changing %s",
+			      ce->cmd, fnm2abs_err(0, info_map_nm));
+		return 0;
+	}
+	return 1;
+}
+
+
+
+static u_char				/* 1=ok 0=bad command */
+cmd(const char *op)
+{
+	const char *arg, *help_str;
+	int op_num, j;
+	const CMD_TBL_ENTRY *ce;
+
+	/* look for the string as a command and execute it if we find */
+	ce = &cmds_tbl[0];
+	for (op_num = 0; op_num < DIM(cmds_tbl); ++op_num) {
+		if (cmds_tbl[op_num].help_str)
+			ce = &cmds_tbl[op_num];
+		arg = cmd_cmp(op, cmds_tbl[op_num].cmd);
+		/* if the command table entry and the command completely
+		 * matched, then infer a null argument */
+		if (!arg) {
+			if (!ck_cmd_priv(ce, ce->privileged, ce->write_map))
+				return 0;
+			if (ce->args != 1) {
+				j = ce->fnc("", ce);
+				if (j >= 0)
+					return j;
+			}
+			help_cmd(op, 0);
+			return 0;
+		}
+		/* If the command table entry is an initial sustring of
+		 * the user's command, then the rest of the command must
+		 * start with white space or '='.  (Allow '=' to let
+		 * homedir/fix-map not need use `eval` to quote blanks
+		 * `eval` in bash loses exit status.
+		 * Trim and use the rest of the string as the argument */
+		j = strspn(arg, DCC_WHITESPACE"=");
+		if (j) {
+			if (ce->args == 2) {
+				help_cmd(op, 0);    /* arg not allowed */
+				return 0;
+			}
+			if (!ck_cmd_priv(ce, ce->privileged, ce->write_map))
+				return 0;
+			j = ce->fnc(arg+j, ce);
+			if (j >= 0)
+				return j;
+			help_cmd(op, 0);
+			return 0;
+		}
+	}
+
+
+	/* otherwise try to interpret it as a DCC administrative packet */
+	op_num = 0;
+	help_str = "";
+	for (;;) {
+		if (op_num >= DIM(aops_tbl)) {
+			dcc_error_msg("unrecognized command \"%s\"", op);
+			return 0;
+		}
+		/* do a command */
+		if (aops_tbl[op_num].help_str) {
+			help_str = aops_tbl[op_num].help_str;
+			if (*help_str == '\0')
+				help_str = aops_tbl[op_num].op;
+		}
+		if (!cmd_cmp(op, aops_tbl[op_num].op))
+			break;
+		++op_num;
+	}
+
+	/* send an administrative request to the server */
+	if (!get_passwd(aops_tbl[op_num].privileged)) {
+		dcc_error_msg("\"%s\" is a privileged operation"PRV_MSG,
+			      help_str, ids_path);
+		return 0;
+	}
+
+	/* try to send it */
+	return do_aop(aops_tbl[op_num].aop, aops_tbl[op_num].val,
+		      NO_SRVR, 1);
+}
+
+
+
+static u_char				/* 0=bad command, 1=ok */
+do_cmds(char *cmd_buf)
+{
+	char *next_cmd, *cur_cmd, *cmd_end;
+	char c;
+
+	next_cmd = cmd_buf;
+	for (;;) {
+		cur_cmd = next_cmd + strspn(next_cmd, DCC_WHITESPACE";");
+
+		if (*cur_cmd == '#' || *cur_cmd == '\0')
+			return 1;
+
+		next_cmd = cur_cmd + strcspn(cur_cmd, ";\n\r");
+		cmd_end = next_cmd;
+		next_cmd += strspn(next_cmd, ";\n\r");
+
+		/* null terminate and trim trailing white space from
+		 * command or arg */
+		do {
+			*cmd_end-- = '\0';
+			c = *cmd_end;
+		} while (cmd_end >= cur_cmd
+			 && strchr(DCC_WHITESPACE";", c));
+
+		if (*cur_cmd == '\0')	/* ignore blank commands */
+			continue;
+
+		if (!cmd(cur_cmd))
+			return 0;
+	}
+}
+
+
+
+static int
+help_cmd_print(int pos, const char *str)
+{
+#define HELP_COL 24
+	int col, nl;
+
+	if (str[0] == '\n') {
+		nl = 100;
+		++str;
+	} else {
+		nl = 0;
+	}
+	col = strlen(str)+1;
+	col += HELP_COL - (col % HELP_COL);
+	pos += col;
+	if (pos > 78) {
+		putchar('\n');
+		pos = col;
+	}
+	printf("%-*s", col, str);
+	pos += nl;
+
+	return pos;
+#undef HELP_COL
+}
+
+
+
+static int
+help_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	int i, pos;
+	const char *help_str;
+	const char *p;
+
+	/* say something about one command */
+	if (arg) {
+		help_str = "";
+		for (i = 0; i < DIM(cmds_tbl); ++i) {
+			if (cmds_tbl[i].help_str)
+				help_str = cmds_tbl[i].help_str;
+			p = cmd_cmp(arg, cmds_tbl[i].cmd);
+			if (!p || *p == ' ' || *p == '\t') {
+				while (*help_str == '\n' || *help_str == '-')
+					++help_str;
+				printf("usage: %s\n", help_str);
+				if (cmds_tbl[i].fnc == stats_cmd)
+					printf(stats_help);
+				return 1;
+			}
+		}
+		for (i = 0; i < DIM(aops_tbl); ++i) {
+			if (aops_tbl[i].help_str) {
+				help_str = aops_tbl[i].help_str;
+				if (*help_str == '\0')
+					help_str = aops_tbl[i].op;
+			}
+			p = cmd_cmp(arg, aops_tbl[i].op);
+			if (!p || *p == ' ' || *p == '\t') {
+				while (*help_str == '\n' || *help_str == '-')
+					++help_str;
+				printf("usage: %s\n", help_str);
+				return 1;
+			}
+		}
+	}
+
+	/* talk about all of the commands */
+	printf("   version "DCC_VERSION"\n");
+	pos = 0;
+	for (i = 0; i < DIM(cmds_tbl); ++i) {
+		if (cmds_tbl[i].help_str
+		    && cmds_tbl[i].help_str[0] != '-')
+			pos = help_cmd_print(pos, cmds_tbl[i].help_str);
+	}
+	for (i = 0; i < DIM(aops_tbl); ++i) {
+		help_str = aops_tbl[i].help_str;
+		if (!help_str)
+			continue;
+		if (*help_str == '\0')
+			help_str = aops_tbl[i].op;
+			pos = help_cmd_print(pos, help_str);
+	}
+	putchar('\n');
+
+	return 1;
+}
+
+
+
+static int NRATTRIB
+exit_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	exit(EX_OK);
+#ifndef HAVE_GCC_ATTRIBUTES
+	return -1;
+#endif
+}
+
+
+static int
+grey_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	if (arg[0] == '\0') {
+		printf("    Greylist mode %s%s\n",
+		       grey_on ? "on" : "off",
+		       grey_set ? "" : " by default");
+		return 1;
+	}
+	if (!strcmp(arg, "off")) {
+		grey_on = 0;
+		grey_set = 1;
+		set_which_map(which_map);
+	} else if (!strcmp(arg, "on")) {
+		grey_on = 1;
+		grey_set = 1;
+		set_which_map(which_map);
+	} else {
+		return -1;
+	}
+	if (!port_set)
+		srvr.port = DCC_GREY2PORT(grey_on);
+	return 1;
+}
+
+
+
+static int
+homedir_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	if (arg[0] != '\0') {
+		if (!dcc_cdhome(0, arg, 1))
+			return 0;
+		if (ids_nm && !set_ids_path(dcc_emsg, ids_nm))
+			dcc_error_msg("%s", dcc_emsg);
+		set_which_map(MAP_INFO);
+	}
+	printf("    homedir=%s\n", dcc_homedir);
+	return 1;
+}
+
+
+
+/* set name of map file */
+static int
+file_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	if (arg[0] == '\0') {
+		if (which_map == MAP_INFO)
+			printf("    using map file %s\n",
+			       fnm2abs_err(0, info_map_nm));
+		else
+			printf("    map file %s but using temporary file\n",
+			       fnm2abs_err(0, info_map_nm));
+		return 1;
+	}
+
+	BUFCPY(info_map_nm, arg);
+	set_which_map(MAP_INFO);
+	return 1;
+}
+
+
+
+/* create a new client map or parameter file */
+static int
+new_map_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	if (arg[0] == '\0')
+		arg = DCC_MAP_NM_DEF;
+
+	dcc_rel_priv();
+	if (!dcc_create_map(dcc_emsg, arg, 0, 0, 0, 0, 0, 0, info_flags)) {
+		dcc_error_msg("%s", dcc_emsg);
+		return 0;
+	}
+	BUFCPY(info_map_nm, arg);
+	set_which_map(MAP_INFO);
+	if (!quiet)
+		printf("    created %s\n", fnm2abs_err(0, info_map_nm));
+	return 1;
+}
+
+
+
+static int
+info_work(const char *arg, int fgs)
+{
+	DCC_CLNT_INFO info;
+	u_char dcc, srcbad, names;
+
+	if (*arg == '\0') {
+		names = 0;
+	} else if (!strcmp(arg, "-N")) {
+		names = 1;
+	} else {
+		return -1;
+	}
+
+	if (!rdy_ctxt(fgs))
+		return 0;
+
+	/* Snapshot the data and then release it while we print it. */
+	dcc_ctxts_lock();
+	if (!dcc_info_lock(0)) {
+		dcc_ctxts_lock();
+		return 0;
+	}
+	memcpy(&info, dcc_clnt_info, sizeof(info));
+	srcbad = ctxt && (ctxt->flags & DCC_CTXT_SRCBAD);
+	cdcc_unlock(1);
+
+	dcc_rel_priv();
+	if (which_map == MAP_INFO) {
+		if (info.dcc.nms[0].hostname[0] != '\0'
+		    || !grey_on) {
+			dcc_print_info(info_map_nm, &info,
+				       quiet, 0, srcbad, names,
+				       0 <= access(info_map_nm, R_OK));
+			dcc = 1;
+		} else {
+			dcc = 0;
+		}
+		if (info.grey.nms[0].hostname[0] != '\0'
+		    || grey_on) {
+			if (dcc && !quiet)
+				fputs("\n################\n", stdout);
+			dcc_print_info(info_map_nm, &info,
+				       quiet, 1, srcbad, names,
+				       0 <= access(info_map_nm, R_OK));
+		}
+	} else {
+		dcc_print_info(0, &info, quiet, grey_on, srcbad, names, 1);
+	}
+	if (!quiet)
+		putchar('\n');
+	return 1;
+}
+
+
+
+/* server hostname */
+static int
+host_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	DCC_SRVR_NM nm;
+	int error;
+
+	if (arg[0] == '\0') {
+		if (which_map == MAP_INFO)
+			return info_work(arg, DCC_CLNT_FG_BAD_SRVR_OK) ;
+		printf("    %s server hostname \"%s\"\n",
+		       grey_on ? "greylist" : "DCC", srvr.hostname);
+		return 1;
+	}
+	if (!strcmp(arg, "-")) {
+		set_which_map(MAP_INFO);
+		if (!init_map(1, 0)) {
+			set_which_map(MAP_TMP);
+			return 0;
+		}
+		return 1;
+	}
+
+	arg = dcc_parse_nm_port(0, arg, 0,
+				nm.hostname, sizeof(nm.hostname),
+				&nm.port, 0, 0,
+				0, 0);
+	if (!arg)
+		return 0;
+	arg += strspn(arg, DCC_WHITESPACE);
+	if (*arg != '\0')
+		return 0;
+
+	set_which_map(MAP_TMP);
+	memcpy(srvr.hostname, nm.hostname, sizeof(srvr.hostname));
+	if (nm.port != 0) {
+		srvr.port = nm.port;
+		port_set = 1;
+	}
+
+	/* go with the flow for IPv6 */
+	dcc_host_lock();
+	if (!dcc_get_host(nm.hostname,
+			  (info_flags & DCC_INFO_FG_IPV6) ? 2 : 3,
+			  &error)) {
+		dcc_host_unlock();
+		dcc_error_msg("%s: %s", nm.hostname, DCC_HSTRERROR(error));
+	} else {
+		if (dcc_hostaddrs[0].sa.sa_family == AF_INET)
+			info_flags &= ~DCC_INFO_FG_IPV6;
+		else
+			info_flags |= DCC_INFO_FG_IPV6;
+		dcc_host_unlock();
+	}
+
+	return 1;
+}
+
+
+
+/* server port # */
+static int
+port_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	u_int port;
+
+	if (arg[0] == '\0') {
+		if (which_map == MAP_INFO)
+			return info_work(arg, DCC_CLNT_FG_BAD_SRVR_OK) ;
+		printf("    port=%d\n", ntohs(srvr.port));
+		return 1;
+	}
+
+	port = dcc_get_port(0, arg, DCC_GREY2PORT(grey_on), 0, 0);
+	if (port == DCC_GET_PORT_INVALID)
+		return 0;
+
+	srvr.port = port;
+	port_set = 1;
+	set_which_map(MAP_TMP);
+	return 1;
+}
+
+
+
+static int
+ipv6_cmd(const char *arg, const CMD_TBL_ENTRY *ce)
+{
+	u_char new_use_ipv6;
+
+	if (arg[0] == '\0') {
+		if (!init_map(1, 0))
+			return 0;
+		printf("    IPv6 %s\n",
+		       (info_flags & DCC_INFO_FG_IPV6) ? "on" : "off");
+		return 1;
+	}
+
+	if (!strcasecmp(arg, "off")) {
+		new_use_ipv6 = 0;
+	} else if (!strcasecmp(arg, "on")) {
+		new_use_ipv6 = DCC_INFO_FG_IPV6;
+	} else {
+		return -1;
+	}
+
+	if (!ck_cmd_priv(ce, 0, 1))
+		return 0;
+
+	if (!init_map(1, 1))
+		return 0;
+	if ((dcc_clnt_info->flags & DCC_INFO_FG_IPV6) != new_use_ipv6) {
+		dcc_clnt_info->flags ^= DCC_INFO_FG_IPV6;
+		info_flags = dcc_clnt_info->flags;
+		fix_info(0);
+	} else if (!cdcc_unlock(1)) {
+		return 0;
+	}
+
+	if (rdy_ctxt(0)
+	    && (dcc_clnt_info->flags & DCC_INFO_FG_IPV6) != new_use_ipv6) {
+#ifdef NO_IPV6
+		dcc_error_msg("IPv6 switch not changed;"
+			      " No IPv6 support in this system?");
+#else
+		dcc_error_msg("IPv6 switch not changed.");
+#endif
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+static u_char
+ck_new_src(DCC_IP *new_ip, const char *arg, u_char use_ipv6)
+{
+	SOCKET soc;
+	DCC_SOCKU su;
+	int error;
+
+	memset(new_ip, 0, sizeof(*new_ip));
+	if (!strcmp(arg, "-"))
+		return 1;
+
+	dcc_host_lock();
+	if (!dcc_get_host(arg, use_ipv6 ? 1 : 0, &error)) {
+		dcc_host_unlock();
+		dcc_error_msg("%s: %s", arg, DCC_HSTRERROR(error));
+		return 0;
+	}
+	if (use_ipv6)
+		dcc_ipv4sutoipv6(&su, &dcc_hostaddrs[0]);
+	else
+		dcc_ipv6sutoipv4(&su, &dcc_hostaddrs[0]);
+	dcc_su2ip(new_ip, &su);
+	dcc_host_unlock();
+
+	soc = INVALID_SOCKET;
+	if (0 >= dcc_udp_bind(dcc_emsg, &soc, &su, 0)) {
+		dcc_error_msg("%s", dcc_emsg);
+		return 0;
+	}
+	closesocket(soc);
+
+	return 1;
+}
+
+
+
+static int
+src_cmd(const char *arg, const CMD_TBL_ENTRY *ce)
+{
+	DCC_IP new_ip;
+	char sustr[DCC_SU2STR_SIZE];
+
+	if (arg[0] == '\0') {
+		if (!init_map(1, 0))
+			return 0;
+
+		if (dcc_clnt_info->src.family == AF_UNSPEC) {
+			printf("    no source address specified\n");
+		} else {
+			/* display what the system actually uses */
+			printf("    source address=%s%s\n",
+			       dcc_su2str2(sustr, sizeof(sustr),
+					   &ctxt->bind_su),
+			       (ctxt->flags & DCC_CTXT_SRCBAD)
+			       ? " "DCC_INFO_USE_SRCBAD : "");
+		}
+		return 1;
+	}
+
+	if (!ck_new_src(&new_ip, arg, DCC_INFO_IPV6()))
+		return 0;
+
+	if (!ck_cmd_priv(ce, 0, 1))
+		return 0;
+
+	if (!init_map(1, 1))
+		return 0;
+	src = new_ip;
+	dcc_clnt_info->src = src;
+
+	fix_info(0);
+	return 1;
+}
+
+
+
+static int
+socks_cmd(const char *arg, const CMD_TBL_ENTRY *ce)
+{
+	u_char new_use_socks;
+
+	if (arg[0] == '\0') {
+		if (!init_map(1, 0))
+			return 0;
+		printf("    SOCKS %s\n",
+		       (info_flags & DCC_INFO_FG_SOCKS) ? "on" : "off");
+		return 1;
+	}
+
+	if (!strcmp(arg, "off")) {
+		new_use_socks = 0;
+	} else if (!strcmp(arg, "on")) {
+		new_use_socks = DCC_INFO_FG_SOCKS;
+	} else {
+		return -1;
+	}
+
+	if (!ck_cmd_priv(ce, 0, 1))
+		return 0;
+
+	if (!init_map(1, 1))
+		return 0;
+	if ((dcc_clnt_info->flags & DCC_INFO_FG_SOCKS) == new_use_socks)
+		return cdcc_unlock(1);	/* nothing to do */
+
+	dcc_clnt_info->flags ^= DCC_INFO_FG_SOCKS;
+	info_flags = dcc_clnt_info->flags;
+
+	fix_info(0);
+	return 1;
+}
+
+
+
+static int
+passwd_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	DCC_PASSWD new_passwd;
+
+	if (arg[0] == '\0') {
+		if (which_map == MAP_INFO) {
+			printf("    using password in %s\n",
+			       fnm2abs_err(0, info_map_nm));
+			if (passwd_set)
+				printf("    but the password for explicitly"
+				       " named servers is "DCC_PASSWD_PAT"\n",
+				       passwd);
+		} else {
+			if (passwd_set)
+				printf("    password "DCC_PASSWD_PAT"\n",
+				       passwd);
+			else
+				printf("    password not set\n");
+		}
+		return 1;
+	}
+
+	arg = parse_passwd(0, new_passwd, arg, "password", 0, 0);
+	if (!arg || *arg != '\0')
+		return -1;
+	memcpy(passwd, new_passwd, sizeof(passwd));
+	passwd_set = 1;
+	set_which_map(MAP_TMP);
+	return 1;
+}
+
+
+
+static int
+id_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	DCC_CLNT_ID id;
+
+	if (arg[0] == '\0') {
+		printf("    ID=%d\n", srvr_clnt_id);
+		return 1;
+	}
+
+	id = dcc_get_id(0, arg, 0, 0);
+	if (id == DCC_ID_INVALID)
+		return -1;
+
+	srvr_clnt_id = id;
+	set_which_map(MAP_TMP);
+	return 1;
+}
+
+
+
+static int
+debug_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	char debug_str[24];
+	char ttl_str[24];
+	int new_ttl, new_debug;
+	char *p;
+
+	if (arg[0] == '\0') {
+		if (!dcc_clnt_debug)
+			snprintf(debug_str, sizeof(debug_str),
+				 "debug off");
+		else if (dcc_clnt_debug == 1)
+			snprintf(debug_str, sizeof(debug_str),
+				 "debug on");
+		else
+			snprintf(debug_str, sizeof(debug_str),
+				 "debug on+%d\n", dcc_clnt_debug-1);
+		if (dcc_debug_ttl != 0)
+			snprintf(ttl_str, sizeof(ttl_str),
+				 "    TTL=%d", dcc_debug_ttl);
+		else
+			ttl_str[0] = '\0';
+		printf("    %s%s\n", debug_str, ttl_str);
+		return 1;
+	}
+
+	new_ttl = dcc_debug_ttl;
+	new_debug = dcc_clnt_debug;
+	for (;;) {
+		if (!CLITCMP(arg, "off")) {
+			new_debug = 0;
+			arg += LITZ("off");
+		} else if (!CLITCMP(arg, "on")) {
+			++new_debug;
+			arg += LITZ("on");
+		} else if (!CLITCMP(arg, "ttl=")) {
+			new_ttl = strtoul(arg+LITZ("ttl="), &p, 10);
+#if defined(IPPROTO_IP) && defined(IP_TTL)
+			if (new_ttl < 256)
+				arg = p;
+#else
+			printf("    TTL setting not supported\n");
+#endif
+		}
+
+		if (*arg == '\0')
+			break;
+		if (*arg == ' ' || *arg == '\t') {
+			arg += strspn(arg, DCC_WHITESPACE);
+		} else {
+			return -1;
+		}
+	}
+	dcc_debug_ttl = new_ttl;
+	if (dcc_debug_ttl != 0)
+		set_which_map(MAP_TMP);
+	dcc_clnt_debug = new_debug;
+	if (dcc_clnt_debug > 1)
+		printf("    debug on+%d\n", dcc_clnt_debug-1);
+	return 1;
+}
+
+
+
+static int
+no_fail_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	if (!init_map(1, 1))
+		return 0;
+	DCC_GREY2CLASS(grey_on)->fail_time= 0;
+	cdcc_unlock(1);
+	return 1;
+}
+
+
+
+static int
+quiet_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	if (arg[0] == '\0') {
+		printf("    %s\n", quiet ? "on" : "off");
+		return 1;
+	} else if (!CLITCMP(arg, "on")) {
+		quiet = 1;
+		return 1;
+	} else if (!CLITCMP(arg, "off")) {
+		quiet = 0;
+		return 1;
+	}
+	return -1;
+}
+
+
+
+static int
+ckludge_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	char *p;
+	long l;
+
+	if (arg[0] == '\0') {
+		printf("    clock kludge=%d\n", (int)clock_kludge);
+		return 1;
+	}
+
+	l = strtol(arg, &p, 10);
+	if (*p != '\0') {
+		dcc_error_msg("invalid clock kludge \"%s\"", arg);
+		return -1;
+	}
+	clock_kludge = l;
+	return 1;
+}
+
+
+
+static int
+delete_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	DCC_SRVR_CLASS *class;
+	DCC_SRVR_NM nm, *nmp;
+	DCC_SRVR_ADDR *addr;
+	u_char del_grey;
+
+	del_grey = grey_on;
+	if (!dcc_parse_srvr_nm(dcc_emsg, &nm, &del_grey, arg, 0, 0)) {
+		dcc_error_msg("%s", dcc_emsg);
+		return 0;
+	}
+
+	/* map and lock */
+	set_which_map(MAP_INFO);
+	if (!init_map(1, 1))
+		return 0;
+
+	class = DCC_GREY2CLASS(del_grey);
+	for (nmp = class->nms; nmp <= LAST(class->nms); ++nmp) {
+		if (strcasecmp(nmp->hostname, nm.hostname)
+		    || nmp->port != nm.port)
+			continue;
+
+		/* Found it. */
+
+		/* zap its IP addresses so they won't be used
+		 * if resolving the remaining names fails */
+		for (addr = class->addrs; addr <= LAST(class->addrs); ++addr) {
+			if (addr->nam_inx == nmp - class->nms) {
+				addr->rtt = DCC_RTT_BAD;
+				addr->nam_inx = NO_NAM;
+			}
+		}
+		if (nmp != LAST(class->nms))
+			memmove(nmp, nmp+1,
+				(LAST(class->nms) - nmp)*sizeof(*nmp));
+		memset(LAST(class->nms), 0, sizeof(*nmp));
+		++class->gen;
+		fix_info(class);
+		return 1;
+	}
+
+	dcc_error_msg("server \"%s,%d\" not found",
+		      nm.hostname, ntohs(nm.port));
+	cdcc_unlock(1);
+	return 0;
+}
+
+
+
+static int
+add_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	DCC_SRVR_CLASS *class;
+	DCC_SRVR_NM nm, *nmp, *tgt_nmp;
+	u_char add_grey;
+
+	add_grey = grey_set && grey_on;
+
+	if (0 >= dcc_parse_srvr_nm(dcc_emsg, &nm, &add_grey, arg, 0, 0)) {
+		dcc_error_msg("%s", dcc_emsg);
+		return 0;
+	}
+	if (nm.clnt_id == DCC_ID_ANON && add_grey) {
+		dcc_error_msg("anonymous client-ID invalid"
+			      " for Greylist server %s",
+			      nm.hostname);
+		return 0;
+	}
+
+	/* map and lock the information */
+	set_which_map(MAP_INFO);
+	if (!init_map(1, 1))
+		return 0;
+
+	/* look for the old entry or a new, free entry */
+	class = DCC_GREY2CLASS(add_grey);
+	tgt_nmp = 0;
+	for (nmp = class->nms; nmp <= LAST(class->nms); ++nmp) {
+		if (nmp->hostname[0] == '\0') {
+			if (!tgt_nmp)
+				tgt_nmp = nmp;
+			continue;
+		}
+		if (!strcmp(nmp->hostname, nm.hostname)
+		    && nmp->port == nm.port) {
+			printf("    overwriting existing entry\n");
+			tgt_nmp = nmp;
+			break;
+		}
+	}
+
+	if (tgt_nmp) {
+		memcpy(tgt_nmp, &nm, sizeof(*tgt_nmp));
+		fix_info(class);
+		return 1;
+	}
+
+	cdcc_unlock(1);
+	if (add_grey)
+		dcc_error_msg("too many Greylist server names");
+	else
+		dcc_error_msg("too many DCC server names");
+	return 0;
+}
+
+
+
+static void
+add_new_nms(const DCC_SRVR_NM new_nms[DCC_MAX_SRVR_NMS],
+	    DCC_SRVR_NM old_nms[DCC_MAX_SRVR_NMS])
+{
+	const DCC_SRVR_NM *new_nmp;
+	DCC_SRVR_NM *old_nmp;
+
+	for (new_nmp = new_nms;
+	     new_nmp < &new_nms[DCC_MAX_SRVR_NMS]
+	     && new_nmp->hostname[0] != '\0';
+	     ++new_nmp) {
+		for (old_nmp = old_nms;
+		     old_nmp <= &old_nms[DCC_MAX_SRVR_NMS];
+		     ++old_nmp) {
+			if (old_nmp->hostname[0] == '\0'
+			    || (!strcmp(old_nmp->hostname, new_nmp->hostname)
+				&& old_nmp->port == new_nmp->port)) {
+				memcpy(old_nmp, new_nmp, sizeof(*old_nmp));
+				break;
+			}
+		}
+	}
+}
+
+
+
+static int
+load_cmd(const char *lfile, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	u_char new_info_flags, load_grey;
+	int flags_set;
+	DCC_SRVR_NM new_nm;
+	DCC_SRVR_NM dcc_nms[DCC_MAX_SRVR_NMS];
+	int num_dcc_nms;
+	DCC_SRVR_NM grey_nms[DCC_MAX_SRVR_NMS];
+	int num_grey_nms;
+	char src_addr[INET6_ADDRSTRLEN+1];
+	char buf[sizeof(DCC_SRVR_NM)*3];
+	DCC_IP new_src;
+	const char *bufp, *cp;
+	FILE *f;
+	int fd, lno;
+
+	if (*lfile == '\0')
+		return -1;
+
+	dcc_rel_priv();
+	if (!strcmp(lfile,"-")) {
+		lfile = 0;
+		fd = dup(fileno(stdin));
+		if (fd < 0) {
+			dcc_error_msg("dup(stdin): %s", ERROR_STR());
+			return 0;
+		}
+		f = fdopen(fd, "r");
+		if (!f) {
+			dcc_error_msg("fdopen(): %s", ERROR_STR());
+			return 0;
+		}
+	} else {
+		f = dcc_open_srvr_nm(dcc_emsg, lfile);
+		if (!f) {
+			dcc_error_msg("%s", dcc_emsg);
+			return 0;
+		}
+	}
+
+	/* parse the text file to create a pair of lists of server names */
+	flags_set = 0;
+	new_info_flags = info_flags;
+	num_dcc_nms = 0;
+	memset(dcc_nms, 0, sizeof(dcc_nms));
+	num_grey_nms = 0;
+	memset(grey_nms, 0, sizeof(grey_nms));
+	memset(&new_src, 0, sizeof(new_src));
+	lno = 0;
+	for (;;) {
+		bufp = fgets(buf, sizeof(buf), f);
+		if (!bufp) {
+			if (ferror(f)) {
+				dcc_error_msg("fgets(%s): %s",
+					      !lfile
+					      ? "STDIN"
+					      : fnm2abs_err(0, lfile),
+					      ERROR_STR());
+				fclose(f);
+				return 0;
+			}
+			break;
+		}
+
+		++lno;
+
+		/* skip blank lines and comments */
+		bufp += strspn(bufp, DCC_WHITESPACE);
+		if (*bufp == '\0' || *bufp == '#')
+			continue;
+
+		/* look for flags in the first non-comment line */
+		if (!flags_set++) {
+			cp = bufp;
+			if (!CLITCMP(cp, DCC_INFO_USE_IPV4)) {
+				cp += LITZ(DCC_INFO_USE_IPV4);
+				new_info_flags &= ~DCC_INFO_FG_IPV6;
+			} else if (!CLITCMP(cp, DCC_INFO_USE_IPV6)) {
+				cp += LITZ(DCC_INFO_USE_IPV6);
+				new_info_flags |= DCC_INFO_FG_IPV6;
+			} else {
+				++flags_set;
+			}
+			if (flags_set == 1) {
+				/* We found "IPv6 on" or "off".
+				 * Look for "use SOCKS" and "src=x.y.z.w" */
+				cp += strspn(cp, DCC_WHITESPACE);
+				if (!CLITCMP(cp, DCC_INFO_USE_SOCKS)) {
+					new_info_flags |= DCC_INFO_FG_SOCKS;
+					cp += LITZ(DCC_INFO_USE_SOCKS);
+					cp += strspn(cp, DCC_WHITESPACE);
+				}
+				if (!CLITCMP(cp, DCC_INFO_USE_SRC)) {
+					cp += LITZ(DCC_INFO_USE_SRC);
+					cp = dcc_parse_word(dcc_emsg,
+							src_addr,
+							sizeof(src_addr),
+							cp, 0, 0, 0);
+					if (!cp) {
+					    dcc_error_msg("%s", dcc_emsg);
+					    continue;
+					}
+					if (!CLITCMP(cp, DCC_INFO_USE_SRCBAD)) {
+					    cp += LITZ(DCC_INFO_USE_SRCBAD);
+					    cp += strspn(cp, DCC_WHITESPACE);
+					}
+					ck_new_src(&new_src, src_addr,
+						   (new_info_flags
+						    & DCC_INFO_FG_SOCKS));
+				}
+			}
+			if (*cp == '\0')
+				continue;
+			/* the first non-comment line must be a server name */
+		}
+
+		load_grey = 0;
+		if (0 >= dcc_parse_srvr_nm(dcc_emsg, &new_nm, &load_grey,
+					   bufp, lfile, lno)) {
+			dcc_error_msg("%s", dcc_emsg);
+			fclose(f);
+			return 0;
+		}
+		if (load_grey) {
+			if (new_nm.clnt_id == DCC_ID_ANON) {
+				dcc_error_msg("anonymous client-ID invalid"
+					      " for Greylist server %s%s",
+					      new_nm.hostname,
+					      fnm_lno(&fnm_buf, lfile, lno));
+				fclose(f);
+				return 0;
+			}
+			if (num_grey_nms >= DIM(grey_nms)) {
+				dcc_error_msg("too many Greylist server names"
+					      "%s",
+					      fnm_lno(&fnm_buf, lfile, lno));
+				fclose(f);
+				return 0;
+			}
+			grey_nms[num_grey_nms++] = new_nm;
+		} else {
+			if (num_dcc_nms >= DIM(dcc_nms)) {
+				dcc_error_msg("too many DCC server names%s",
+					      fnm_lno(&fnm_buf, lfile, lno));
+				fclose(f);
+				return 0;
+			}
+			dcc_nms[num_dcc_nms++] = new_nm;
+		}
+	}
+	fclose(f);
+	if (num_grey_nms == 0 && num_dcc_nms == 0) {
+		dcc_error_msg("no DCC server names%s",
+			      fnm_lno(&fnm_buf, lfile, lno));
+		return 0;
+	}
+
+	/* create the map, without set-UID powers to prevent games,
+	 * and then lock, install, and unlock the information */
+	dcc_rel_priv();
+
+	if (which_map != MAP_INFO)
+		set_which_map(MAP_INFO);
+	if (!init_map(0, 1)) {
+		/* create a new map */
+		if (!dcc_create_map(0, info_map_nm, 0,
+				    0, 0, 0, 0, &new_src, new_info_flags))
+			return 0;
+		printf("    created %s\n", fnm2abs_err(0, info_map_nm));
+		if (!init_map(1, 1))
+			return 0;
+	}
+
+	/* merge the old and new entries */
+	add_new_nms(grey_nms, dcc_clnt_info->grey.nms);
+	add_new_nms(dcc_nms, dcc_clnt_info->dcc.nms);
+	dcc_clnt_info->flags = info_flags = new_info_flags;
+	if (new_src.family != AF_UNSPEC)
+		dcc_clnt_info->src = new_src;
+
+	fix_info(0);
+
+	if (!quiet) {
+		if (!lfile)
+			printf("##################\n\n");
+		return info_work("", DCC_CLNT_FG_BAD_SRVR_OK) ;
+	}
+	return 1;
+}
+
+
+
+static int
+info_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	/* map, copy, and unlock the information
+	 * prefer to talk to the server, but don't wait
+	 * unless we have changed the file */
+	return info_work(arg,
+			 map_changed
+			 ? DCC_CLNT_FG_BAD_SRVR_OK
+			 : (DCC_CLNT_FG_NO_PICK_SRVR
+			    | DCC_CLNT_FG_BAD_SRVR_OK));
+}
+
+
+
+static int
+rtt_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	if (!init_map(1, 1))
+		return 0;
+	dcc_force_measure_rtt(&dcc_clnt_info->dcc);
+	dcc_force_measure_rtt(&dcc_clnt_info->grey);
+	cdcc_unlock(1);
+
+	/* wait to talk to the server, but don't insist */
+	return info_work(arg, quiet ? DCC_CLNT_FG_BAD_SRVR_OK : 0);
+}
+
+
+/* delete a checksum */
+static int				/* 1=ok, 0=bad checksum, -1=fatal */
+delck_sub(DCC_EMSG emsg, DCC_WF *wf UATTRIB,
+	  DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts UATTRIB)
+{
+	struct timeval cmd_start, cmd_end;
+	char type_buf[DCC_XHDR_MAX_TYPE_LEN];
+	char ck_buf[sizeof(DCC_SUM)*3+2];
+	DCC_DELETE del;
+	DCC_OP_RESP resp;
+	char ob[DCC_OPBUF];
+	u_char result;
+
+	printf(" deleting %s  %s\n",
+	       dcc_type2str(type_buf, sizeof(type_buf), type, 0, 1, grey_on),
+	       dcc_ck2str(ck_buf, sizeof(ck_buf), type, sum, 0));
+
+	memset(&del, 0, sizeof(del));
+	gettimeofday(&cmd_start, 0);
+	del.date = htonl(cmd_start.tv_sec);
+	del.ck.type = type;
+	del.ck.len = sizeof(del.ck);
+	memcpy(&del.ck.sum, sum, sizeof(DCC_SUM));
+	result = dcc_clnt_op(emsg, ctxt, DCC_CLNT_FG_NO_FAIL,
+			     0, 0, 0, &del.hdr, sizeof(del),
+			     DCC_OP_DELETE, &resp, sizeof(resp));
+	gettimeofday(&cmd_end, 0);
+	if (!result) {
+		dcc_error_msg("%s", dcc_emsg);
+	} else {
+		switch (resp.hdr.op) {
+		case DCC_OP_OK:
+			break;
+
+		case DCC_OP_ERROR:
+			dcc_error_msg("   %.*s",
+				      (ntohs(resp.hdr.len)
+				       -(int)(sizeof(resp.error)
+					      - sizeof(resp.error.msg))),
+				      resp.error.msg);
+			result = 0;
+			break;
+
+		default:
+			dcc_error_msg("unexpected response: %s",
+				      dcc_hdr_op2str(ob,sizeof(ob), &resp.hdr));
+			result = 0;
+			break;
+		}
+	}
+
+	if (dcc_clnt_debug) {
+		printf("%.2f ms\n",
+		       ((cmd_end.tv_sec-cmd_start.tv_sec)*1000.0
+			+ (cmd_end.tv_usec-cmd_start.tv_usec)/1000.0));
+	}
+	return result;
+}
+
+
+
+/* delete a simple checksum */
+static int
+delck_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	char type_str[DCC_XHDR_MAX_TYPE_LEN+1];
+
+	if (*arg == '\0')
+		return -1;
+	arg = dcc_parse_word(0, type_str, sizeof(type_str),
+			     arg, 0, 0, 0);
+	if (!arg)
+		return -1;
+
+	if (!rdy_ctxt(0))
+		return 0;
+	return 0 < dcc_parse_hex_ck(0, &cmn_wf,
+				    type_str, dcc_str2type_del(type_str, -1),
+				    arg, 0, delck_sub);
+}
+
+
+
+static int
+sleep_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	double s;
+	char *p;
+
+	s = strtod(arg, &p);
+	if (*p != '\0' || s < 0.001 || s > 1000)
+		return -1;
+	usleep((u_int)(s*1000000.0));
+	return 1;
+}
+
+
+
+
+static const u_char *
+client_unpack4(const u_char *cp,
+	       u_int *vp)
+{
+	u_char c;
+	u_int v;
+	int shift;
+
+	v = 0;
+	shift = 0;
+	do {
+		c = *cp++;
+		v |= (c & 0x7f) << shift;
+		shift += 7;
+	} while (c & 0x80);
+
+	*vp = v;
+	return cp;
+}
+
+
+
+static int
+client_unpack(const u_char *cp0,
+	      u_char *flagsp,
+	      u_int *clnt_idp,
+	      u_int *last_usedp,
+	      u_int *requestsp,
+	      u_int *nopsp,
+	      u_char *versp,
+	      DCC_SOCKU *su)
+{
+	const u_char *cp;
+	u_char flags;
+	u_int v;
+	struct in6_addr in6_addr;
+	struct in_addr in_addr;
+
+#ifdef DCC_PKT_VERSION6
+	if (aop_resp.hdr.pkt_vers <= DCC_PKT_VERSION6) {
+#define CPY2(s) ((s[0]<<8) | s[1])
+#define CPY3(s) ((s[0]<<16) | (s[1]<<8) | s[2])
+#define CPY4(s) ((s[0]<<24) | (s[1]<<16) | (s[2]<<8) | s[3])
+		const DCC_ADMN_RESP_CLIENTSv6 *cl;
+
+		cl = (DCC_ADMN_RESP_CLIENTSv6 *)cp0;
+		flags = cl->flags;
+		*flagsp = flags & (DCC_ADMN_RESP_CLIENTS_BL
+				   | DCC_ADMN_RESP_CLIENTS_SKIP);
+		*clnt_idp = CPY4(cl->clnt_id);
+		*last_usedp = CPY4(cl->last_used);
+		if (flags & DCC_ADMN_RESP_CLIENTS_SKIP) {
+			/* skip place keepers */
+			*last_usedp = CPY3(cl->requests);
+			*requestsp = 0;
+		} else {
+			*requestsp = CPY3(cl->requests);
+		}
+		*nopsp = CPY2(cl->nops);
+		if (flags & DCC_ADMN_RESP_CLIENTS_IPV6) {
+			memcpy(&in6_addr, &cl->addr, sizeof(in6_addr));
+			dcc_mk_su(su, AF_INET6, &in6_addr, 0);
+			return (sizeof(*cl) - sizeof(cl->addr)
+				+ sizeof(cl->addr.ipv6));
+		}
+		memcpy(&in_addr, &cl->addr, sizeof(in_addr));
+		dcc_mk_su(su, AF_INET, &in_addr, 0);
+		return (sizeof(*cl) - sizeof(cl->addr)
+			+ sizeof(cl->addr.ipv4));
+	}
+#undef CPY2
+#undef CPY3
+#undef CPY4
+#endif
+
+	cp = cp0;
+	flags = *cp++;
+	*flagsp = flags & (DCC_ADMN_RESP_CLIENTS_BL
+			   | DCC_ADMN_RESP_CLIENTS_BAD
+			   | DCC_ADMN_RESP_CLIENTS_SKIP
+			   | DCC_ADMN_RESP_CLIENTS_LAST);
+	/* if the version is absent,
+	 * then it must be the same as the previous value */
+	if (flags & DCC_ADMN_RESP_CLIENTS_VERS)
+		*versp = *cp++;
+	v = *cp++ << 24;
+	v |= *cp++ << 16;
+	v |= *cp++ << 8;
+	v |= *cp++;
+	*last_usedp = v;
+	if ((flags & DCC_ADMN_RESP_CLIENTS_ID1) != 0)
+		*clnt_idp = DCC_ID_ANON;
+	else
+		cp = client_unpack4(cp, clnt_idp);
+	cp = client_unpack4(cp, requestsp);
+	cp = client_unpack4(cp, nopsp);
+	if (flags & DCC_ADMN_RESP_CLIENTS_IPV6) {
+		memcpy(&in6_addr, cp, sizeof(in6_addr));
+		dcc_mk_su(su, AF_INET6, &in6_addr, 0);
+		cp += 16;
+	} else {
+		memcpy(&in_addr, cp, sizeof(in_addr));
+		dcc_mk_su(su, AF_INET, &in_addr, 0);
+		cp += 4;
+	}
+	return cp - cp0;
+}
+
+
+
+/* get the server's list of recent clients */
+static int
+clients_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	u_char nonames, sort, ids, req_flags;
+	u_char passed_flags, passed_max_clients, passed_thold, passed_cidr;
+	struct in6_addr addr6;
+	DCC_AOP_CLIENTS_CIDR addr_bits;
+	u_int max_clients, thold;
+	u_int total, subtotal;
+	u_int max_ops, max_nops;
+	int ops_width, nops_width;
+	u_int offset;			/* next client wanted from server */
+	u_int num_clients;
+	DCC_SOCKU su;
+	struct ct {
+	    struct ct *lt, *gt, *up;
+	    time_t	last_used;
+	    u_int	requests;
+	    u_int	nops;
+	    u_int	rank;
+	    u_char	flags;
+	    u_char	vers;
+	    DCC_CLNT_ID	clnt_id;
+	    DCC_SOCKU	su;
+	} *clist, **ctptr, *ctup, *ct, *ctnew;
+	u_int versions[DCC_PKT_VERSION_MAX+1];
+
+	char date_buf[40];
+	struct tm last, now;
+	char *p;
+	const char *ac;
+	u_char need_head;
+	int i;
+
+	passed_flags = 0;
+	thold = 0;
+	passed_thold = 0;
+	max_clients = DCC_ADMIN_RESP_MAX_CLIENTS;
+	passed_max_clients = 0;
+	passed_cidr = 0;
+	memset(addr_bits, 0, sizeof(addr_bits));
+
+	ac = strpbrk(arg, "/.:");
+
+	/* look for "-n", "-ns", "-n -s", etc. */
+	nonames = 0;
+	sort = 0;
+	ids = 0;
+	req_flags = 0;
+	while (*arg != 0) {
+		arg += strspn(arg, " \t");
+		if (*arg == '-' && !passed_flags) {
+			++arg;
+			do {
+				switch (*arg) {
+				case 'n':
+					nonames = 1;
+					break;
+				case 's':
+					sort = 1;
+					break;
+				case 'i':
+					ids = 1;
+					break;
+				case 'a':
+					req_flags |= DCC_AOP_CLIENTS_AVG;
+					break;
+				case 'V':
+					req_flags |= DCC_AOP_CLIENTS_VERS;
+					break;
+				case 'A':
+					req_flags |= DCC_AOP_CLIENTS_ANON;
+					req_flags &= ~DCC_AOP_CLIENTS_NON_ANON;
+					break;
+				case 'K':
+					req_flags |= DCC_AOP_CLIENTS_NON_ANON;
+					req_flags &= ~DCC_AOP_CLIENTS_ANON;
+					break;
+				default:
+					help_cmd("clients", 0);
+					return -1;
+				}
+			} while (*++arg != ' ' && *arg != '\t' && *arg != '\0');
+			continue;
+		}
+		if (!passed_cidr && ac && !strpbrk(arg, DCC_WHITESPACE)) {
+			int bits;
+
+			bits = dcc_str2cidr(0,  &addr6, 0, 0, arg, 0, 0);
+			if (bits <= 0)
+				return -1;
+			memcpy(addr_bits, &addr6, sizeof(addr6));
+			addr_bits[sizeof(addr6)] = bits;
+			arg = "";
+			passed_cidr = 1;
+			passed_flags = 1;
+			passed_max_clients = 1;
+			passed_thold = 1;
+			continue;
+		}
+		if (!passed_max_clients
+		    && (i = strtoul(arg, &p, 10)) != 0
+		    && (*p == ' ' || *p == '\t' || *p == '\0')) {
+			max_clients = i;
+			arg = p;
+			passed_max_clients = 1;
+			passed_flags = 1;
+			continue;
+		}
+		if (!passed_thold
+		    && (i = strtoul(arg, &p, 10)) > 0
+		    && (*p == ' ' || *p == '\t' || *p == '\0')
+		    && i <= DCC_ADMIN_RESP_CLIENTS_MAX_THOLD) {
+			thold = i;
+			arg = p;
+			passed_thold = 1;
+			passed_max_clients = 1;
+			passed_flags = 1;
+			continue;
+		}
+		help_cmd("clients", 0);
+		return -1;
+	}
+
+	if (ids)
+		req_flags &= ~DCC_AOP_CLIENTS_VERS;
+
+	/* Require a server password for client IP addresses
+	 * The server demands only client ID for "clients -i" */
+	if (!ids
+	    && !ck_cmd_priv(ce, 1, 0))
+		return 0;
+
+	if (!rdy_ctxt(0))
+		return 0;
+
+	/* Collect all of the information before printing it to minimize
+	 * the changes in the position of hosts and so deleted or missing
+	 * entries. */
+	total = 0;
+	subtotal = 0;
+	max_ops = 0;
+	max_nops = 0;
+	memset(versions, 0, sizeof(versions));
+	offset = 0;
+	num_clients = 0;
+	clist = 0;
+	for (;;) {
+		DCC_OPS result;
+		int len, result_len;
+		u_char vers, result_flags;
+#	define BL_FLAGS (DCC_ADMN_RESP_CLIENTS_BL | DCC_ADMN_RESP_CLIENTS_BAD)
+		u_int clnt_id, last_used, requests, nops;
+
+		if (offset > DCC_AOP_CLIENTS_MAX_OFFSET) {
+			dcc_error_msg("%d are too many clients", offset);
+			break;
+		}
+
+		gettimeofday(&op_start, 0);
+		result = dcc_aop(dcc_emsg, ctxt, grey_on ? DCC_CLNT_FG_GREY : 0,
+				 NO_SRVR, clock_kludge,
+				 ids ? DCC_AOP_CLIENTS_ID : DCC_AOP_CLIENTS,
+				 (offset << 16)
+				 + min(thold,
+				       DCC_ADMIN_RESP_CLIENTS_MAX_THOLD),
+				 ISZ(aop_resp.resp.val.string
+				     ) >> DCC_ADMIN_RESP_CLIENTS_SHIFT,
+				 req_flags,
+				 offset >> 16,
+				 addr_bits, passed_cidr ? sizeof(addr_bits) : 0,
+				 &aop_resp, &op_result_su);
+		if (result == DCC_OP_INVALID
+		    || result == DCC_OP_ERROR) {
+			dcc_error_msg("%s", dcc_emsg);
+			break;
+		}
+
+		/* print heading before the first chunk */
+		if (!offset)
+			print_aop(-1);
+
+		result_len = (ntohs(aop_resp.hdr.len)
+			      - (sizeof(aop_resp.resp)
+				 - sizeof(aop_resp.resp.val.string)));
+		/* stop when the server has nothing to add */
+		if (result_len <= 1)
+			break;
+
+		len = 0;
+		vers = 0;
+		do {
+			len += client_unpack(&aop_resp.resp.val.clients[len],
+					     &result_flags, &clnt_id,
+					     &last_used,
+					     &requests, &nops, &vers,
+					     &su);
+			if (result_flags & DCC_ADMN_RESP_CLIENTS_SKIP) {
+				offset += last_used;
+				continue;
+			}
+
+			if (vers != 0) {
+				if (vers < DIM(versions))
+					versions[vers] += requests;
+				else
+					versions[0] += requests;
+			}
+
+
+			/* quit if we are in some kind of loop */
+			if (++num_clients > DCC_ADMIN_RESP_MAX_CLIENTS)
+				goto stop;
+			++offset;
+
+			/* add the new entry to the possibly sorted list */
+
+			ctnew = dcc_malloc(sizeof(*ctnew));
+			memset(ctnew, 0, sizeof(*ctnew));
+			ctnew->flags = (result_flags & BL_FLAGS );
+			ctnew->vers = vers;
+			ctnew->clnt_id = clnt_id;
+			ctnew->last_used = last_used;
+			ctnew->requests = requests;
+			if (max_ops < requests)
+				max_ops = requests;
+			total += requests;
+			ctnew->nops = nops;
+			if (max_nops < nops)
+				max_nops = nops;
+			ctnew->su = su;
+
+			ctptr = &clist;
+			ctup = 0;
+			for (;;) {
+				ct = *ctptr;
+				if (!ct) {
+					ctnew->up = ctup;
+					*ctptr = ctnew;
+					break;
+				}
+				i = !sort;
+				if (!i) {
+					i = (0!= (ct->flags & BL_FLAGS));
+					i -= (0 != (ctnew->flags & BL_FLAGS));
+				}
+				if (!i) {
+					i = ct->requests;
+					i -= ctnew->requests;
+				}
+				ctup = ct;
+				if (i >= 0) {
+					ctptr = &ct->lt;
+				} else {
+					/* update the threshold if sorting */
+					if (++ct->rank >= max_clients
+					    && thold < ct->requests) {
+					    thold = ct->requests;
+					}
+					ctptr = &ct->gt;
+				}
+			}
+		} while (len < result_len);
+		if (len != result_len) {
+			dcc_error_msg("wrong sized clients response; %d != %d",
+				      result_len, len);
+			break;
+		}
+
+		/* quit if the server ran out of things to say */
+		if (result_flags & DCC_ADMN_RESP_CLIENTS_LAST)
+			break;
+
+		/* Quit if we want only part of the list and we have it.
+		 * We must get everything the server sends if we are sorting.
+		 * The server uses our threshold to avoid sending everything
+		 * it know. */
+		if (!sort && offset >= max_clients)
+			break;
+#undef BL_FLAGS
+	}
+stop:
+	if (!total)
+		total = 1;
+
+	dcc_localtime(time(0), &now);
+
+	if (max_ops > 99*1000*1000)
+		ops_width = 9;
+	else if (max_ops > 9*1000*1000)
+		ops_width = 8;
+	else
+		ops_width = 7;
+	if (max_nops > 99*1000)
+		nops_width = 6;
+	else if (max_nops > 9*1000)
+		nops_width = 5;
+	else
+		nops_width = 4;
+
+	/* print the list */
+	num_clients = 0;
+	for (ct = clist; ct; ct = ctnew) {
+		ctnew = ct->gt;
+		if (ctnew) {
+			ct->gt = 0;
+			continue;
+		}
+
+		if (num_clients == 0) {
+			if (sort) {
+				printf("          %*s %*s ",
+				       ops_width, "ops",
+				       nops_width, "nops");
+				if (ids)
+					fputs("   ID ", stdout);
+				fputs("   last       ", stdout);
+				if (req_flags & DCC_AOP_CLIENTS_VERS)
+					fputs(" v", stdout);
+			} else {
+				printf("%*s %*s    last           ID ",
+				       ops_width, "ops",
+				       nops_width, "nops");
+				if (req_flags & DCC_AOP_CLIENTS_VERS)
+					fputs(" v", stdout);
+			}
+			putchar('\n');
+		}
+		if (++num_clients <= max_clients) {
+			if (sort) {
+				subtotal += ct->requests;
+				printf("%3d%% %3d%% ",
+				       (int)(ct->requests*100.0/total),
+				       (int)(subtotal*100.0/total));
+			}
+			printf("%*d %*d",
+			       ops_width, ct->requests,
+			       nops_width, ct->nops);
+			if (sort && ids)
+				printf(" %6d", ct->clnt_id);
+			/* print year and no time if it was long ago */
+			dcc_localtime(ct->last_used, &last);
+			printf(" %s", dcc_time2str(date_buf, sizeof(date_buf),
+						   (last.tm_year != now.tm_year
+						    && (last.tm_mon < 6
+							|| now.tm_mon > 2))
+						   ? "%Y/%m/%d" : "%m/%d %X",
+						   ct->last_used));
+			if (!sort)
+				printf(" %6d", ct->clnt_id);
+			if (req_flags & DCC_AOP_CLIENTS_VERS) {
+				if (ct->vers != 0)
+					printf(" %d", ct->vers);
+				else
+					fputs(" ?", stdout);
+			}
+			if (ct->flags & DCC_ADMN_RESP_CLIENTS_BL)
+				fputs(" BLACKLIST", stdout);
+			else if (ct->flags & DCC_ADMN_RESP_CLIENTS_BAD)
+				fputs(" BAD", stdout);
+			if (!ids) {
+				char name[DCC_MAXDOMAINLEN];
+				char sustr[DCC_SU2STR_SIZE];
+
+				if (nonames) {
+					printf(" %s",
+					       dcc_su2str2(sustr, sizeof(sustr),
+							&ct->su));
+				} else {
+					printf(" %-16s %s",
+					       dcc_su2str2(sustr, sizeof(sustr),
+							&ct->su),
+					       dcc_su2name(name, sizeof(name),
+							&ct->su));
+				}
+			}
+			putchar('\n');
+
+			ctnew = ct->lt;
+			if (!ctnew) {
+				ctnew = ct->up;
+			} else {
+				ctnew->up = ct->up;
+			}
+		}
+
+		memset(ct, 0, sizeof(*ct));
+		dcc_free(ct);
+	}
+	putchar('\n');
+
+
+	need_head = 1;
+	for (i = 0; i < DIM(versions); ++i) {
+		if (versions[i] == 0)
+			continue;
+		if (need_head) {
+			need_head = 0;
+			fputs("version    total\n", stdout);
+		}
+		printf("%6d %8d\n", i, versions[i]);
+	}
+
+	return 1;
+}
+
+
+
+/* get and set the server's default anonymous client delay */
+static int
+anon_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	int new_delay, old_delay, inflate;
+	DCC_OPS result;
+	char *inflate_str, *p;
+
+	inflate = 0;
+	if (*arg == '\0') {
+		new_delay = DCC_NO_ANON_DELAY;
+	} else {
+		if (!strcasecmp(arg, "forever")) {
+			new_delay = DCC_ANON_DELAY_FOREVER;
+		} else {
+			new_delay = strtoul(arg, &inflate_str, 10);
+			if (new_delay > DCC_ANON_DELAY_MAX
+			    || (*inflate_str != '\0' && *inflate_str != ','
+				&& *inflate_str != '*')) {
+				dcc_error_msg("invalid delay: \"%s\"", arg);
+				return 0;
+			}
+			if (*inflate_str != '\0') {
+				++inflate_str;
+				inflate_str += strspn(inflate_str,
+						      DCC_WHITESPACE);
+			}
+			if (*inflate_str != '\0'
+			    && strcasecmp(inflate_str, "none")) {
+				inflate = strtoul(inflate_str, &p, 10);
+				if (*p != '\0') {
+					dcc_error_msg("invalid delay inflation:"
+						      " \"%s\"", inflate_str);
+					return 0;
+				}
+			}
+		}
+		if (!ck_cmd_priv(ce, 1, 0))
+			return 0;
+	}
+
+	if (!rdy_ctxt(0))
+		return 0;
+
+	gettimeofday(&op_start, 0);
+	result = dcc_aop(dcc_emsg, ctxt, grey_on ? DCC_CLNT_FG_GREY : 0,
+			 NO_SRVR, clock_kludge, DCC_AOP_ANON_DELAY,
+			 inflate, new_delay>>8, new_delay, 0, 0, 0,
+			 &aop_resp, &op_result_su);
+	if (result == DCC_OP_INVALID
+	    || result == DCC_OP_ERROR) {
+		dcc_error_msg("%s", dcc_emsg);
+		return 0;
+	}
+
+	old_delay = ((aop_resp.resp.val.anon_delay.delay[0]<<8)
+		     + aop_resp.resp.val.anon_delay.delay[1]);
+	if (old_delay == DCC_ANON_DELAY_FOREVER) {
+		printf("    anon delay %s FOREVER\n",
+		       new_delay != DCC_NO_ANON_DELAY ? "was" : "is");
+	} else {
+		printf("    anon delay %s %d",
+		       new_delay != DCC_NO_ANON_DELAY ? "was" : "is",
+		       old_delay);
+		inflate = ((aop_resp.resp.val.anon_delay.inflate[0]<<24)
+			   +(aop_resp.resp.val.anon_delay.inflate[1]<<16)
+			   +(aop_resp.resp.val.anon_delay.inflate[2]<<8)
+			   +aop_resp.resp.val.anon_delay.inflate[3]);
+		if (inflate != 0)
+			printf(",%d", inflate);
+		putchar('\n');
+	}
+	return 1;
+}
+
+
+
+/* rewind the flood from a single server */
+static int
+flod_rewind(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	DCC_CLNT_ID id;
+
+	if (!arg)
+		return -1;
+	id = dcc_get_id(0, arg, 0, 0);
+	if (id == DCC_ID_INVALID)
+		return -1;
+
+	return do_aop(DCC_AOP_FLOD, id*256 + DCC_AOP_FLOD_REWIND, NO_SRVR, 1);
+}
+
+
+
+/* fast forward the flood to a single server */
+static int
+ffwd_out(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	DCC_CLNT_ID id;
+
+	if (!arg)
+		return -1;
+	id = dcc_get_id(0, arg, 0, 0);
+	if (id == DCC_ID_INVALID)
+		return -1;
+
+	return do_aop(DCC_AOP_FLOD, id*256 + DCC_AOP_FLOD_FFWD_OUT, NO_SRVR, 1);
+}
+
+
+
+/* fast forward the flood to a single server */
+static int
+ffwd_in(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	DCC_CLNT_ID id;
+
+	if (!arg)
+		return -1;
+	id = dcc_get_id(0, arg, 0, 0);
+	if (id == DCC_ID_INVALID)
+		return -1;
+
+	return do_aop(DCC_AOP_FLOD, id*256 + DCC_AOP_FLOD_FFWD_IN, NO_SRVR, 1);
+}
+
+
+
+/* get the flood counts for a server */
+static int
+flod_stats(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	u_int32_t id, next_id;
+	DCC_AOP_FLODS op;
+	u_char heading;
+	int sresult;
+
+	if (!arg)
+		return -1;
+	if (!CLITCMP(arg, "clear")) {
+		arg += LITZ("clear");
+		arg += strspn(arg, DCC_WHITESPACE);
+		op = DCC_AOP_FLOD_STATS_CLEAR;
+	} else {
+		op = DCC_AOP_FLOD_STATS;
+	}
+
+	heading = 1;
+	if (!strcasecmp(arg, "all")) {
+		id = DCC_SRVR_ID_MAX+1;
+		for (;;) {
+			if (!start_aop(DCC_AOP_FLOD, id*256 + op, NO_SRVR))
+				return 0;
+			sresult = sscanf(aop_resp.resp.val.string,
+					 DCC_AOP_FLOD_STATS_ID, &next_id);
+			if (1 == sresult
+			    && id == next_id) {
+				if (id == DCC_SRVR_ID_MAX+1) {
+					BUFCPY(aop_resp.resp.val.string,
+					       " (no flooding peers)");
+					fin_aop(NO_SRVR, 1);
+				}
+				return 1;
+			}
+			fin_aop(NO_SRVR, heading);
+			heading = 0;
+			if (1 != sresult)
+				return 0;
+			id = next_id+DCC_SRVR_ID_MAX+1;
+		}
+	}
+
+	id = dcc_get_id(0, arg, 0, 0);
+	if (id == DCC_ID_INVALID)
+		return -1;
+	return do_aop(DCC_AOP_FLOD, id*256 + op, NO_SRVR, heading);
+}
+
+
+
+static const char *stats_help = "";
+
+/* get the statistics from all known servers */
+static int
+stats_cmd(const char *arg, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	DCC_SRVR_CLASS *class;
+	SRVR_INX srvr_inx;
+	int srvrs_gen;
+	DCC_AOPS aop;
+
+	/* look for "clear" or "all" */
+	srvr_inx = NO_SRVR;
+	aop = DCC_AOP_STATS;
+	while (*arg != 0) {
+		arg += strspn(arg, " \t");
+		if (srvr_inx == NO_SRVR
+		    && !CLITCMP(arg, "clear")) {
+			arg += LITZ("clear");
+			aop = DCC_AOP_STATS_CLEAR;
+			if (!get_passwd(aops_tbl[aop].privileged)) {
+				dcc_error_msg("\"stats clear\""
+					      " is a privileged operation"
+					      PRV_MSG, ids_path);
+				return 0;
+			}
+		} else if (aop == DCC_AOP_STATS
+			   && !CLITCMP(arg, "all")) {
+			arg += LITZ("all");
+			srvr_inx = 0;
+		}
+		if (*arg != '\0' && *arg != ' ' && *arg != '\t')
+			return -1;
+	}
+
+	if (!rdy_ctxt(0))
+		return 0;
+	class = DCC_GREY2CLASS(grey_on);
+	srvrs_gen = class->gen;
+	do {
+		if (srvrs_gen != class->gen) {
+			dcc_error_msg("list of servers changed");
+			return 0;
+		}
+		/* skip dead servers */
+		if (srvr_inx != NO_SRVR
+		    && class->addrs[srvr_inx].srvr_id == DCC_ID_INVALID )
+			continue;
+
+		do_aop(aop, sizeof(aop_resp.resp.val.string), srvr_inx, 1);
+		fflush(stderr);
+		fflush(stdout);
+	} while (srvr_inx != NO_SRVR && ++srvr_inx < class->num_srvrs);
+
+	return 1;
+}
+
+
+
+static int
+clock_ck_cmd(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	if (!rdy_ctxt(0))
+		return 0;
+	do_aop(DCC_AOP_CLOCK_CHECK, sizeof(aop_resp.resp.val.string),
+	       NO_SRVR, 1);
+
+	return 1;
+}
+
+
+
+/* restore tracing to default */
+static int
+trace_def(const char *arg UATTRIB, const CMD_TBL_ENTRY *ce UATTRIB)
+{
+	if (!rdy_ctxt(0))
+		return 0;
+
+	return (do_aop(DCC_AOP_TRACE_ON, DCC_TRACE_ON_DEF_BITS,
+		       NO_SRVR, 1)
+		&& do_aop(DCC_AOP_TRACE_OFF, DCC_TRACE_OFF_DEF_BITS,
+			  NO_SRVR, 1));
+
+}
diff -r 000000000000 -r c7f6b056b673 cdcc/win32.mak
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cdcc/win32.mak	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,53 @@
+# Makefile for cdcc for WIN32.
+
+#   This assumes Borland's free command line tools FreeCommandLineTools.exe 
+#   available in 2004 at
+#	http://www.borland.com/products/downloads/download_cbuilder.html
+#   and elsewhere
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.7 $Revision$
+
+!include "../win32.makinc1"
+
+TARGET	=cdcc
+SRCS	=$(TARGET).c
+OBJS	=$(SRCS:.c=.obj)
+
+$(TARGET): $(OBJS) ../dcclib/dcclib.lib
+	$(CC) $(LFLAGS) $?
+
+!include "../win32.makinc2"
diff -r 000000000000 -r c7f6b056b673 cgi-bin/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,12 @@
+Makefile.in
+README
+chgpasswd.in
+common.in
+edit-whiteclnt.in
+footer
+header
+http2https
+list-log.in
+list-msg.in
+webuser-notify.in
+.manifest
diff -r 000000000000 -r c7f6b056b673 cgi-bin/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,72 @@
+# install the Distributed Checksum Clearinghouse sample white list CGI files
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.15 $Revision$
+# @configure_input@
+
+# Install only the CGI scripts in the cgi-bin directory to minimize
+#   the risks of letting Apache execute files in it.  Install the other
+#   scripts in the usual DCC libexec directory.
+
+DEPTH	=..
+CSCRIPTS=chgpasswd edit-whiteclnt list-log list-msg http2https webuser-notify
+OFILES	=README common
+LOCFILES=header footer
+
+@MAKE_PROG@
+@MAKE_INC2@
+
+INST_BINDIR=@installroot@@cgibin@
+
+all:
+	@:
+
+install:
+	$(INSTALL) -d $(SET_BINOWN) -m 755 $(INST_BINDIR)
+	for NM in $(CSCRIPTS); do $(INSTALL) $(SET_BINOWN) \
+		-m $(BINMODE) -c $$NM $(INST_BINDIR)/$$NM; done
+	for NM in $(OFILES); do $(INSTALL) $(SET_BINOWN) \
+		-m 644 -c $$NM $(INST_BINDIR)/$$NM; done
+	for NM in $(LOCFILES); do \
+	    if test ! -f $(INST_BINDIR)/$$NM; then \
+	      $(INSTALL) $(SET_BINOWN) -m 644 -c $$NM $(INST_BINDIR)/$$NM; \
+	    fi; done
+
+deinstall:
+	-if test -d $(INST_BINDIR); then\
+	    (cd $(INST_BINDIR); rm -f  $(OFILES) $(CSCRIPTS));\
+	    rmdir $(INST_BINDIR);\
+	    fi
diff -r 000000000000 -r c7f6b056b673 cgi-bin/README
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/README	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,137 @@
+Sample CGI scripts for managing per-user dccm, dccifd, and dccproc whitelists
+and logs.
+    Each user with a white list directory can
+	- browse logged messages
+	- point-and-click to add checksums from logged messages to an
+	    individual white list
+	- choose to receive a daily notice about messages since the user's
+	    log was last checked, but no more than one notice per week
+	    when the log is not checked.
+
+...............................................................................
+
+    newwebuser		see misc/README
+			It is installed in the DCC libexec directory
+			instead of the cgi-bin directory so that the HTTP
+			server need not be tempted by distant users to
+			execute it.
+
+    webuser-notify	send a mail message notifying a user of new DCC log
+			files.  This file must be edited, copied to the DCC
+			libexec directory, and made executable so that the
+			DCC cron script can use it.
+
+    common		utility functions
+
+    header		common HTML used near top of the web pages
+    footer		common HTML used near bottom of the web pages
+			The scripts look first for a copy of the file
+			in the per-user directory and then in the cgi-bin
+			directory.
+
+    list-log		list a user's log files
+
+    list-msg		list a single message among the log files
+
+    edit-whiteclnt	edit a user's white list file
+
+    chgpasswd		change a user's password.
+			BEWARE that this script users `htpasswd -b` which
+			momentarily exposes passwords to other users on the
+			system using the `ps` command.  On systems with user
+			shell accounts, this script should be turned off or
+			replaced with something like the HTTPD::UserAdmin
+			Perl module.  To get it to work at all, you may need
+			to adjust $PATH to reach htpasswd.
+
+    http2https		CGI script to redirect HTTP accesses to HTTPS.
+
+...............................................................................
+
+
+These scripts are intended to be portable and usable instead of fast or fancy.
+Large organizations should consider perl_mod, templates, and so forth.
+
+Instead of modifying them in place, copying them to a directory other
+    than /var/dcc/cgi-bin will avoid difficulties when installing new
+    versions of the DCC.
+
+They are intended to be used with dccm and dccifd, but can be used with dccproc
+    if dccproc is told to follow the per-user logging and whitelist
+    conventions used by dccm or dccifd with
+	dccproc -E -l /var/dcc/userdirs/local/$USER/log \
+		-w /var/dcc/userdirs/local/$USER/whiteclnt
+    It might be good to use the "include" facility to add a global
+    whiteclnt file to those per-user files.  The /var/dcc/libexec/newwebuser
+    script starts per-user whiteclnt files from a prototype file and creates
+    a log directory.
+    It is not necessary to include the global whiteclnt file in each per-user
+    file with dccm or dccifd.  The global whiteclnt file is consulted if a
+    per-user's file fails to yield a black or white answer.
+
+    These scripts base their decisions about which additional or
+    "subsititute" headers to show on the -S parameters in DCCM_ARGS in
+    /var/dcc/dcc_conf.  If you are not use dccm or dccifd but are using dccproc,
+    you must still set DCCM_ARGS for any local substitute SMTP headers.
+    Less likely to be useful SMTP headers such as non-null Message-IDs are
+    not supported to avoid confusing end-users.
+
+The log directory and whitelist for a local user in .../userdirs/local/name
+    are mapped to the htpasswd username "name", while those for remote
+    users in such as .../userdirs/esmtp/xxx@example.com are mapped to
+    esmtp/name@example.com
+
+These scripts should be installed and protected with an equivalent to the
+following in httpd.conf with Apache:
+    ScriptAlias /DCC-cgi-bin/ /var/dcc/cgi-bin/
+    <Directory /var/dcc/cgi-bin/>
+	Order deny,allow
+	allow from all
+	AuthType Basic
+	AuthName "DCC user"
+	SetEnv AuthName "DCC user"
+	AuthUserFile /var/dcc/userdirs/webusers
+	require valid-user
+    #
+	SSLCipherSuite ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP
+	SSLRequireSSL
+    # install the http2http2 script in your main /cgi-bin/ directory and
+    #	    add something like the following line to redirect HTTP to HTTPS
+    #   ErrorDocument 403 /cgi-bin/http2https
+    </Directory>
+
+Httpd must be able to read and write the per-user files and directories,
+usually by sharing a GID with the DCC user and having the directories
+writable-by-group.  By default, the newwebuser script uses the group www.
+
+
+This scripts can be used with the main client DCC log directory and whitelist by
+
+    1. let httpd read the main DCC log files.
+	Make the /var/dcc/log directory readable and searchable by 'group'
+	    but neither searchable nor readable by 'other'.
+	Give the log directory the group used by httpd.
+	On SVR4 and Solaris systems, also make the directory set-GID
+
+    2. use `/var/dcc/libexec/newwebuser %postmaster`
+	to recreate a per-user directory for a local username that is
+	invalid and will not be hit by spammer dictionary attacks
+
+    3. replace the resulting userdirs/local/%postmaster/log directory with a
+	symbolic link to the main log directory:
+	    rmdir /var/dcc/userdirs/local/%postmaster/log
+	    ln -s ../../../log /var/dcc/userdirs/local/%postmaster/log
+
+    4. replace the resulting userdirs/%postmaster/whiteclnt file with a
+	symbolic link to the DCC client white list:
+	    rm /var/dcc/userdirs/local/%postmaster/whiteclnt
+	    ln -f -s ../../../whiteclnt /var/dcc/userdirs/local/%postmaster
+
+	ensure that the /var/dcc/whiteclnt file can be read and written
+	    by the httpd group.  If you don't trust your httpd daemon,
+	    it might be best to forget this idea.
+
+    5. follow the hints above for installing the sample CGI scripts.
+
+
+	Rhyolite Software DCC 1.3.103-1.12 $Revision$
diff -r 000000000000 -r c7f6b056b673 cgi-bin/chgpasswd.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/chgpasswd.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,167 @@
+#! @PERL@ -wT
+
+# Change a DCC end-user's password
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.19 $Revision$
+#	@configure_input@
+
+# This file must protected with equivalents to the httpd.conf lines
+#   in the README file.
+
+use strict 'subs';
+use 5.004;
+use Fcntl qw(:DEFAULT :flock);
+
+
+sub emsg {
+    my($msg) = html_str_encode(@_);
+
+    $msg =~ s/^\s+//;
+    $msg =~ s/\s+$//;
+    $msg =~ s/\n/<BR>\n/g;
+
+    return "<P class=warn>$msg";
+}
+
+
+my($preq, $passwd1, $passwd2, @file, %dict,
+   $locked, $result_msg, $restart_url);
+
+# get DCC parameters
+local($DCCM_USERDIRS, 
+      $hostname,
+      $user,
+      $whiteclnt_lock,
+      $edit_url, $passwd_url,
+      $url_ques, $url_suffix,
+      $form_hidden);
+do('@cgibin@/common') || die("could not get DCC configuration: $!\n");
+
+read_whiteclnt(\@file, \%dict);
+
+
+$webusers="@prefix@/$DCCM_USERDIRS/webusers";
+$webusers_lock="$webusers.lock";
+
+$passwd1 = $query{passwd1} ? $query{passwd1} : "";
+$passwd2 = $query{passwd2} ? $query{passwd2} : "";
+if ($hostname eq "www.rhyolite.com"
+    && $ENV{AuthName} && $ENV{AuthName} eq "DCC-demo-cgi"
+    && $user eq "cgi-demo"
+    && $passwd1 && $passwd2 && $passwd1 eq $passwd2) {
+    $passwd1 = "cgi-demo";
+    $passwd2 = "cgi-demo";
+}
+
+$preq="The password must be 4 or more characters.";
+$locked = ($whiteclnt_lock =~ /\blocked/) ? " disabled" : "";
+if ($locked) {
+    $result_msg = emsg("$whiteclnt locked; password not changed");
+} elsif (!$passwd1) {
+    if ($locked) {
+	$result_msg = emsg("$whiteclnt locked");
+    } else {
+	$result_msg = html_str_encode($preq);
+    }
+} elsif (length($passwd1) < 4) {
+    $result_msg = emsg("$preq");
+
+} elsif ($passwd1 ne $passwd2) {
+    $result_msg = emsg("The two copies of the password differ.");
+} elsif ($passwd1 !~ /^([^'"`]+)$/) {
+    $result_msg = emsg("Quotes are not allowed in passwords.");
+} else {
+    $passwd1 = $1;			# quite Perl taint warnings
+
+    # use a separate lock file in case htpasswd does some locking of its own
+    if (!sysopen(LOCKFH, "$webusers_lock", O_WRONLY | O_CREAT)) {
+	$result_msg = emsg("open($webusers_lock): $!");
+    } elsif (!flock(LOCKFH, LOCK_EX | LOCK_NB)) {
+	$result_msg = emsg("$webusers_lock busy: $!\nTry again");
+	close(LOCKFH);
+    } else {
+	$locked = " disabled";
+	open(CMD, "@HTPASSWD@ -b $webusers '$user' '$passwd1' 2>&1 |");
+	if (!read(CMD, $result_msg, 1000)) {
+	    $result_msg = emsg("read(htpasswd): $!");
+	    # put the error message into the Apache error log
+	    print STDERR "DCC cgi chgpasswd $result_msg\n";
+	    $result_msg = emsg($result_msg);
+	    close(CMD);
+	    close(LOCKFH);
+	} else {
+	    close(LOCKFH);
+	    if (!close(CMD)) {
+		$result_msg = ($! ? "$result_msg\nclose(htpasswd): $!"
+			       : "$result_msg\nhtpasswd exit status $?");
+		# put the error message into the Apache error log
+		print STDERR "DCC cgi chgpasswd $result_msg\n";
+		$result_msg = emsg($result_msg);
+	    } else {
+		$restart_url = ($query{goback} && $query{goback} ne $passwd_url
+				? "$query{goback}$url_suffix"
+				: $edit_url);
+		$restart_url .= $url_ques;
+	    }
+	}
+    }
+}
+
+html_head("Change DCC Password for $user", $restart_url);
+
+print "<H3>Change DCC Password for <EM>$user</EM></H3>\n<P>\n";
+
+common_buttons();
+print <<EOF;
+</TABLE>
+
+<P>
+<FORM action="$ENV{SCRIPT_NAME}" name=form method=POST>
+<TABLE border=0 cellspacing=1 cellpadding=1>
+<TR><TD align=right><LABEL for=passwd1>Password</LABEL>
+    <TD><INPUT$locked id=passwd1 type=password name=passwd1 maxlength=12 value="$passwd1">
+<TR><TD align=right><LABEL for=passwd2>Confirm</LABEL>
+    <TD><INPUT$locked id=passwd2 type=password name=passwd2 maxlength=12 value="$passwd2">
+<TR><TD><INPUT type=submit $locked value="Change">$form_hidden
+</TABLE>
+</FORM>
+
+<P>
+$result_msg
+
+EOF
+html_footer();
+print "</BODY>\n</HTML>\n";
diff -r 000000000000 -r c7f6b056b673 cgi-bin/common.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/common.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,2031 @@
+#! @PERL@ -wT
+
+# get local DCC parameters for DCC whitelist CGI scripts.
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.89 $Revision$
+#	@configure_input@
+
+# check this file by running it separately
+use strict 'subs';
+
+use integer;
+
+use 5.004;
+use Fcntl qw(:DEFAULT :flock);
+use POSIX qw(strftime);
+
+# quiet Perl taint checks with a path that should work everywhere for
+#   the few commands these scripts use.
+$ENV{PATH}="/sbin:/bin:/usr/sbin:/usr/bin";
+
+# global variables
+#   $DCCM_USERDIRS,		# from dcc_conf
+#   $whiteclnt,			# path to the per-user whitelist file
+#   %query,
+#   $thold_cks,			# checksums that can have thresholds
+#   $user,
+#   $hostname,
+#   $user_dir,
+#   $edit_url,
+#   $list_log_url,
+#   $list_log_link,
+#   $list_msg_link,
+#   $edit_url, $edit_link,
+#   $passwd_url, $passwd_link,
+#   $logoutID,
+#   $url_ques, $url_suffix,
+#   $sub_white,			# 'subsitute' headers from dcc_conf
+#   $form_hidden		# state for main form
+
+
+
+# so this file can be used with do('@cgibin@/common')
+#   besides, check_user() must be called before html_head()
+return check_user();
+
+
+
+sub debug_time {
+    my($label) = @_;
+
+    return if (!$query{debug});
+
+    my(@ts, $ts);
+    require 'sys/syscall.ph';
+
+    $ts = pack("LL", ());
+    syscall(&SYS_gettimeofday, $ts, 0);
+    @ts = unpack("LL", $ts);
+
+    chomp($label);
+    printf STDERR "%38s", $label;
+    print STDERR strftime(" %X", localtime($ts[0]));
+    printf STDERR ".%03d", $ts[1]/1000;
+    printf STDERR " %.3f", $_ foreach times;
+    print STDERR "\n";
+}
+
+
+
+sub debug_printf {
+    my($label, $str) = @_;
+
+    return if (!$query{debug});
+    $str =~ s/\n/\\n/g;
+    print STDERR "$label='$str'\n";
+}
+
+
+# emit HTTP/HTML header
+sub html_head {
+    my($title,			# title of the web page
+       $refresh_url) = @_;	# next step in re-login sequence if not null
+    my($header, $style);
+
+    print <<EOF;
+Content-type: text/html; charset=iso-8859-1
+Expires: Thu, 01 Dec 1994 16:00:00 GMT
+pragma: no-cache
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML>
+<HEAD>
+    <TITLE>$title</TITLE>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+EOF
+
+    print "<META HTTP-EQUIV=refresh content=\"1;url=$refresh_url\">\n"
+	if ($refresh_url);
+
+    # Use header if supplied
+    #	it is mostly text for the start of the <BODY>,
+    #	but it can also contain either <LINK rel="stylesheet"...
+    #	or <STYLE>...</STYLE>
+    $header = "\n";
+    if (open(HEADER, "$user_dir/header")
+	|| open(HEADER, "@cgibin@/header")) {
+	my $line;
+
+	$header .= $line while ($line = <HEADER>);
+	close(HEADER);
+    }
+
+    # Use our style style if the supplied header has none
+    if ($header =~ s/([ \t]*<STYLE[^>]*>.*<\/STYLE>\s*)//si) {
+	$style = $1;
+    } elsif ($header =~ s/([ \t]*<link[^>]*rel=['"]?stylesheet[^>]*>)//si) {
+	$style = $1;
+    } else {
+	$style = <<EOF;
+    <STYLE type="text/css">
+	<!--
+	BODY {background-color:white; color:black}
+	.warn {color:red}
+	.mono {font-family:monospace}
+	.small {font-size:smaller}
+	.strong {font-weight:bolder}
+	.nopad {margin:0; padding:0}
+	INPUT.selected {font-size:smaller; font-weight:bolder; color:blue}
+	TABLE {white-space:nowrap}
+	TD.first {text-align:right; vertical-align:baseline}
+	IMG.logo {width:6em; vertical-align:middle}
+	ADDRESS {font-size:smaller}
+	-->
+    </STYLE>
+EOF
+    }
+
+    print <<EOF;
+$style
+</HEAD>
+<BODY>
+<H2>$title</H2>
+$header
+EOF
+}
+
+
+
+sub html_footer {
+    if (open(FOOTER, "$user_dir/footer")
+	|| open(FOOTER, "@cgibin@/footer")) {
+	my $line;
+
+	print $line while ($line = <FOOTER>);
+	close(FOOTER);
+    }
+}
+
+
+
+sub common_buttons {
+    my($msg, $cur, $list_log, $edit, $passwd, $id);
+
+
+    $msg = $query{msg} ? "${url_ques}msg=$query{msg}" : "";
+
+    $cur = "$ENV{SCRIPT_NAME}$url_suffix";
+    $list_log = ($cur ne $list_log_url
+		 ? "$list_log_link$msg\">Log</A>"
+		 : "List Log");
+    $edit = ($cur ne $edit_url
+	     ? "$edit_link\">Settings</A>"
+	     : "Settings");
+    $passwd = ($cur ne $passwd_url
+	       ? "$passwd_link\">Password</A>"
+	       : "Password");
+
+    print <<EOF;
+<TABLE>
+<TR><TD>$list_log
+    <TD>$edit
+    <TD>$passwd
+    <TD><A HREF="$cur${url_ques}logoutID=$logoutID">LogOut/In</A>
+EOF
+}
+
+
+
+# give up, but not entirely, with an HTML whine
+sub html_whine {
+    my($msg) = @_;
+
+    html_head("Internal Error");
+    common_buttons();
+    print <<EOF;
+</TABLE>
+<H1>Internal Error</H1>
+<P class=warn>$msg
+<P><HR>
+$ENV{SERVER_SIGNATURE}
+</BODY>
+</HTML>
+EOF
+    exit;
+}
+
+
+
+# die with an HTML whine
+sub html_die {
+    my($msg) = @_;
+
+    # put the message into the httpd error log
+    print STDERR "DCC CGI script internal error: $msg\n";
+
+    html_head("Internal Error");
+    print <<EOF;
+<P class=warn>$msg
+<P><HR>
+$ENV{SERVER_SIGNATURE}
+</BODY>
+</HTML>
+EOF
+    exit;
+}
+
+
+# punt to some other web page, perhaps after the logout/in kludge
+#   this cannot be used after html_head()
+sub punt2 {
+    my($msg,				# message saying why
+       $url) = @_;			# the other web page
+
+    # don't punt a punt
+    html_die($msg) if ($query{result});
+
+    $url = ((($ENV{HTTPS} && $ENV{HTTPS} eq "on") ? "https://" : "http://")
+	    . $ENV{SERVER_NAME}
+	    . $url);
+    $url .= $url_ques."result=".url_encode($msg) if ($msg);
+
+    print "Status: 302 Moved Temporarily\nLocation: $url\n";
+    html_head("redirect to $url");
+    print "redirecting to $url\n</BODY>\n</HTML>\n";
+    exit;
+}
+
+
+
+# Check authentication and gather system parameters.
+#   Require a user name as well as one that can't be used as a sneaky path.
+sub check_user {
+    my($sub_args, $cks, $thold, $line, $var);
+
+    if ($ENV{HTTP_NAME}) {
+	$hostname = $ENV{HTTP_NAME};
+    } elsif ($ENV{SERVER_NAME}){
+	$hostname = $ENV{SERVER_NAME};
+    } else {
+	$hostname=`hostname`;
+	chop($hostname);
+    }
+
+    $user = $ENV{REMOTE_USER};
+    if (!$user){
+	$user = '';
+	html_die("no user name")
+    }
+    # allow the user name to be a subdirectory
+    html_die("user name $user is invalid")
+	if ($user =~ /\.\./ || $user !~ /^([-\/.,#_%a-z0-9]+)$/i);
+    $user = $1;				# stop Perl taint warnings
+
+    # convert the user name to lower case because sendmail likes to
+    $user =~ tr/A-Z/a-z/;
+
+    # rely on the /var/dcc/dcc_conf configuration file for almost everything
+    $DCC_HOMEDIR = "@prefix@";		# unneeded except for compatibility
+    $DCCM_USERDIRS = "userdirs";
+    $DCCM_ENABLE = "on";
+    $DCCIFD_ENABLE = "off";
+    open(CONF, '2>/dev/null sh -c \'. @prefix@/dcc_conf;
+		echo DCCM_ENABLE="$DCCM_ENABLE";
+		echo DCCM_USERDIRS="$DCCM_USERDIRS";
+		echo DCCM_ARGS="$DCCM_ARGS";
+		echo DCCM_REJECT_AT="$DCCM_REJECT_AT";
+		echo DCCM_CKSUMS="$DCCM_CKSUMS";
+		echo DCCIFD_USERDIRS="$DCCIFD_USERDIRS";
+		echo DCCIFD_ENABLE="$DCCIFD_ENABLE";
+		echo DCCIFD_ARGS="$DCCIFD_ARGS";
+		echo DCCIFD_REJECT_AT="$DCCIFD_REJECT_AT";
+		echo DCCIFD_CKSUMS="$DCCIFD_CKSUMS";
+		echo GREY_CLIENT_ARGS="$GREY_CLIENT_ARGS";
+		echo DNSBL_ARGS="$DNSBL_ARGS";
+		\'|')
+	|| html_die("cannot get DCC configuration");
+    while ($line = <CONF>) {
+	chomp($line);
+	if ($line !~ s/(^[A-Z_]+)=//) {
+	    print STDERR "unrecognized dcc_conf line $line";
+	    next;
+	}
+	$var = $1;
+	if ($line =~ /^([-0-9,.\/a-z_]*)$/i) {
+	    ${$var} = $1;		# suppress taint warnings on good paths
+	} else {
+	    ${$1} = $line;
+	}
+    }
+    close(CONF);
+
+    $main_whiteclnt = "@prefix@/whiteclnt";
+    if ($DCCM_ENABLE eq "off" && $DCCIFD_ENABLE eq "on") {
+	$sub_args = $DCCIFD_ARGS;
+	$cks = $DCCIFD_CKSUMS;
+	$thold = $DCCIFD_REJECT_AT;
+	$logout_tmpdir = "@prefix@/$DCCIFD_USERDIRS/tmp";
+	# Assume "name" per-user directory for simple dccifd user names.
+	$user_dir = "@prefix@/$DCCIFD_USERDIRS/$user";
+    } else {
+	$sub_args = $DCCM_ARGS;
+	$cks = $DCCM_CKSUMS;
+	$thold = $DCCM_REJECT_AT;
+	$logout_tmpdir = "@prefix@/$DCCM_USERDIRS/tmp";
+	# Assume "local/name" per-user directory for simple dccm user names.
+	$user_dir = ($user =~ /\//) ? $user : "local/$user";
+	$user_dir = "@prefix@/$DCCM_USERDIRS/$user_dir";
+    }
+    html_die("no user directory $user_dir")
+	if (! -d $user_dir) ;
+    $logdir = "$user_dir/log";
+    $whiteclnt = "$user_dir/whiteclnt";
+
+    # Figure out which substitute headers are turned on
+    #	This does not detect all possible SMTP "field names," but it also
+    #	won't get Perl confused with field names such as 'foo[bar]'.
+    $sub_hdrs = "";
+    $sub_hdrs .= "|$1"
+	while ($sub_args && $sub_args =~ s/(?:-[VdbxANQW]*S\s*)
+	       ((?i:[-a-z_0-9]+))
+	       ($|\s+)
+	       /$2/x);
+    $sub_white = $sub_hdrs;
+    # pattern matching optional or substitute SMTP headers
+    $sub_hdrs =~ s/^\|+//;
+    # pattern matching optional or substitute checksum types
+    $sub_white =~ s/\|/)|(substitute\\s+/g;
+    $sub_white =~ s/^[|)(]*/(/;
+    $sub_white .= ')';
+
+    # names of checksums whose thresholds can be set
+    $thold_cks_cmn = 'Body,Fuz1,Fuz2';
+    $thold_cks = $thold_cks_cmn;
+    # all checksums including those not kept by (almost all) DCC servers
+    #$thold_cks_all = 'IP,env_From,From,env_To,Message-ID,' . $thold_cks;
+
+    # compute default checksum thresholds
+    if ($thold) {
+	$cks = $thold_cks_cmn if (!$cks);
+	foreach my $ck (split(/,/,$cks)) {
+	    my ($t,$v) = ($ck, $thold);
+	    $conf_cks_tholds{$t} = "<STRONG>$v</STRONG> <SMALL>by default in @prefix@/dcc_conf</SMALL>"
+		if (parse_thold_value($t, $v));
+	}
+    }
+
+    $cgibin = $ENV{SCRIPT_NAME};
+    # trim the name of our script from the path
+    $cgibin =~ s!/+[^/]+$!!;
+    # trim extra leading /s that can mess up our generated links
+    $cgibin =~ s!^/{2,}!/!;
+
+    get_query();
+
+    return 1;
+}
+
+
+
+# Get user's parameters
+sub get_query {
+    my($buffer, $name, $value);
+
+    if ($ENV{REQUEST_METHOD} && $ENV{REQUEST_METHOD} =~ /GET|HEAD/) {
+	$buffer = $ENV{'QUERY_STRING'};
+    } elsif (!$ENV{CONTENT_LENGTH}) {
+	$buffer = '';
+    } else {
+	read(STDIN, $buffer, $ENV{CONTENT_LENGTH});
+    }
+    $buffer =~ tr/+/ /;
+    foreach my $pair (split(/&/, $buffer)) {
+	($name, $value) = split(/=/, $pair);
+	$name =~ s/%([a-fA-F0-9]{2})/pack("C", hex($1))/eg;
+	if ($value) {
+	    $value =~ s/%([a-fA-F0-9]{2})/pack("C", hex($1))/eg;
+	} else {
+	    $value = "";
+	}
+	$query{$name} = $value;
+    }
+
+    if (!$query{debug} || $query{debug} !~ /^\d+$/) {
+	$url_ques = "?";
+	$url_suffix = "";
+	$form_hidden = "";
+    } else {
+	if ($query{debug} > 1) {
+	    debug_time("start $ENV{SCRIPT_NAME}");
+	    print STDERR "  $_=\"$query{$_}\"\n" foreach (keys %query);
+	    print STDERR "  AuthName=\"$ENV{AuthName}\"\n"
+		if ($ENV{AuthName});
+	    print STDERR "  SCRIPT_NAME=\"$ENV{SCRIPT_NAME}\"\n"
+		if ($ENV{SCRIPT_NAME});
+	}
+	$url_suffix = "?debug=$query{debug}";
+	$url_ques = '&amp;';
+	$form_hidden = "\n    <INPUT type=hidden name=debug value=$query{debug}>";
+    }
+
+    $list_log_url = "$cgibin/list-log$url_suffix";
+    $list_log_link = "<A HREF=\"$list_log_url";
+    $list_msg_link = "<A HREF=\"$cgibin/list-msg$url_suffix";
+    $edit_url = "$cgibin/edit-whiteclnt$url_suffix";
+    $edit_link = "<A HREF=\"$edit_url";
+    $passwd_url = "$cgibin/chgpasswd$url_suffix";
+    $passwd_link = "<A HREF=\"$passwd_url${url_ques}goback=$ENV{SCRIPT_NAME}";
+
+    $logoutID = $ENV{UNIQUE_ID};
+    # do the best we can if Apache mod_unique_id is not present
+    $logoutID = "$ENV{REMOTE_ADDR}-$ENV{REMOTE_PORT}-$$-" . time()
+	if (!$logoutID);
+    $logoutID = url_encode($logoutID);
+
+    # kludge to handle "logout" button including recognizing that we have
+    #   already handled it.  The usual tactic of requiring the user to
+    #   specifying a new username and then using a cookie seems ugly.
+    $tfile = $query{logoutID};
+    if ($tfile && $tfile =~ /^([-.A-Za-z0-9@]+)$/) {
+	$tfile = "$logout_tmpdir/logout.$1";
+
+	# delete any old logout marker files
+	my($old_tfiles) = `find $logout_tmpdir -name 'logout.*' -mtime +1`;
+	if ($old_tfiles && $old_tfiles =~ /^(.+)\s*$/) {
+	    $old_tfiles = $1;			# untaint
+	    my @old_tfiles = split /\s/,$old_tfiles;
+	    print "unlink($old_tfiles): $!\n"
+		if ($#old_tfiles >= unlink @old_tfiles);
+	}
+
+	# Look for our logout marker file.
+	if (-f $tfile) {
+	    # If it exists, then we have been here before, so just delete it.
+	    # and refresh
+	    unlink $tfile;
+	    punt2("", "$ENV{SCRIPT_NAME}$url_suffix");
+
+	} else {
+	    # If it does not exist, create it & force a cycle of authentication.
+	    if (!open(TFILE, "> $tfile")) {
+		print STDERR "open($tfile): $!\n";
+		html_whine("open($tfile): $!", $edit_url);
+	    }
+	    while (($name,$value) = each %ENV) {
+		print TFILE "$name=$value\n";
+	    }
+
+	    # Demand a new user name and password
+	    my($AuthName) = $ENV{AuthName} ? $ENV{AuthName} : "DCC user";
+	    print <<EOF;
+WWW-authenticate: Basic realm="$AuthName"
+Status: 401 Unauthorized
+EOF
+	    html_head("Access Failure");
+	    print "<P class=warn>\n";
+	    print $msg ? $msg : "Access Failure";
+	    print "\n</BODY></HTML>\n";
+	    exit;
+	}
+    }
+}
+
+
+
+
+##########################################################################
+
+# %-encode text for a URL
+sub url_encode {
+    my($out) = @_;
+
+    $out =~ s/([^-_.+!*(),0-9a-zA-Z])/sprintf("%%%02X",ord($1))/eg;
+    return $out;
+}
+
+
+
+# encode text for ordinary HTML to avoid special HTML flags such as '<'
+#   retain newlines
+sub html_str_encode {
+    my($out) = @_;
+
+    $out =~ s/&/&amp;/g;
+    $out =~ s/</&lt;/g;
+    $out =~ s/>/&gt;/g;
+    $out =~ s/([\00-\10\13-\17\42\47\177-\377])/sprintf("&#%d;",ord($1))/eg;
+    return $out;
+}
+
+
+
+# encode text for HTML, and replace newlines with <BR>
+sub html_text_encode {
+    my($out) = html_str_encode(@_);
+    $out =~ s/\n/<BR>\n/g;
+    return $out;
+}
+
+
+
+# encode text for HTML, trimmed to at most 32 characters with the end replaced
+#   by an ellipsis if too long
+sub hdr_trim_encode {
+    my($out) = @_;
+
+    return "&nbsp;" if (!$out);
+
+    return html_str_encode($out) if (length($out) <= 32);
+
+    $out = substr($out, 0, 28)
+	if ($out !~ s/(^.{20,28}[^<>.@\t ])[<>.@\t ].*/$1/);
+    $out = html_str_encode($out);
+    $out .=  "&nbsp;...";
+    return $out;
+}
+
+
+
+##########################################################################
+# Open and parse a log message
+# sets these globals
+#	$msg_date		# envelope
+#	$msg_helo		# envelope
+#	$msg_ip			# envelope
+#	$msg_client_name	# envelope
+#	$msg_env_from		# envelope
+#	@msg_env_to		# envelope
+#	$msg_mail_host		# envelope
+#	$msg_from		# header
+#	$msg_subject
+#	$msg_hdrs
+#	$msg_body
+#	$msg_cksums
+#	$msg_result
+
+
+# globals
+#   %msgs_cache,		# key=compressed name, [0]=mtime [1]=i-number
+#   $cache_line_len,
+#   $cache_pack,
+#   $cache_version,
+#   $msg_encode_str,
+#   %msgs_cache_state,		# key=day, value=1 if good
+#   %msgs_date, %msgs_result,
+#   %msgs_from, %msgs_subject,
+#   $msg_day_first, $msg_day_last,
+#   $msg_first, $msg_last,
+#   $msg_newer, $msg_part_num,
+#   @msgs_num			# compressed names sorted by mtime
+
+
+sub parse_log_msg {
+    my($msg, $no_body) = @_;
+    my(@error, $path, $line, $num_hdrs, $cur_hdr, $hdr_type,
+       $misc_hdr, $seen_message_id, $ise_msg, $cksum_marker, $cksum_marker_p);
+
+    undef $msg_date;
+    undef $msg_helo;
+    undef $msg_ip;
+    undef $msg_client_name;
+    undef $msg_env_from;
+    undef @msg_env_to;
+    undef $msg_mail_host;
+    undef $msg_from;
+    undef $msg_subject;
+    $msg_hdrs = '';
+    $msg_body = '';
+    $msg_cksums = '';
+    $msg_result = ': ';
+
+    $num_hdrs = 0;
+
+    $ise_msg = "Internal Server Error";
+    $cksum_marker = "### end of message body ########################\n";
+    $cksum_marker_p = qr/^### end of message body ########################\s*$/;
+
+    $no_body = "" if ($no_body && $no_body ne "no body");
+    $path = msg2path($msg);
+
+    sysopen(MSG, $path, O_RDONLY, 0)
+	|| return ($ise_msg, "open($path): $!");
+
+    return ($ise_msg, "empty $path") if (!($msg_date = <MSG>));
+
+    if ($msg_date !~ /^VERSION/) {
+	close(MSG);
+	return ($ise_msg, "format of $path unrecognized");
+    }
+    if (!($msg_date = <MSG>)) {
+	close(MSG);
+	return ($ise_msg, "$path truncated after VERSION line");
+    }
+    if (!($msg_date =~ s/^DATE: +(.*) +[^ ]+/$1/)) {
+	close(MSG);
+	return ($ise_msg, "unrecognized DATE line $msg_date in message $msg");
+    }
+
+    if (!($msg_ip = <MSG>)) {
+	close(MSG);
+	return ($ise_msg, "message $msg truncated in envelope");
+    }
+    if ($msg_ip =~ /^IP: ([^ :]*) *([:.0-9a-fA-F]*) *$/) {
+	$msg_ip = $2;
+	$msg_client_name = $1;
+	$msg_ip =~ s/^::ffff://i;
+	$msg_client_name =~ s/^\[.*]$//;
+	$msg_client_name = ' ' if ($msg_client_name eq '');
+	if (!($msg_helo = <MSG>)) {
+	    close(MSG);
+	    return ($ise_msg, "message $msg truncated in envelope");
+	}
+	chop($msg_helo);
+    } else {
+	# no IP line
+	$msg_helo = $msg_ip;
+	undef $msg_ip;
+    }
+    if (!($msg_helo =~ s/^HELO: //)) {
+	# no HELO line
+	$msg_env_from = $msg_helo;
+	undef($msg_helo);
+    } else {
+	if (!($msg_env_from = <MSG>)) {
+	    close(MSG);
+	    return ($ise_msg, "message $msg truncated after HELO line");
+	}
+	chop($msg_env_from);
+    }
+    if (!($msg_env_from =~ s/^env_From: //)) {
+	# no env_from line
+	$line = $msg_env_from;
+	undef($msg_env_from);
+    } else {
+	$msg_mail_host = $msg_env_from;
+	$msg_mail_host =~ s/.*mail_host=(.*)/$1/;
+	$msg_env_from =~ s/<?([^\t> ]*).*/$1/;
+	$line = <MSG>;
+    }
+
+    # Save the envelope env_To lines.
+    for (;;) {
+	if (!$line) {
+	    close(MSG);
+	    return ($ise_msg, "message $msg truncated in envelope");
+	}
+	last if ($line =~ /^[\r\n]*$/);
+	if ($line eq "abort\n") {
+	    close(MSG);
+	    return ("aborted transaction", "");
+	}
+	push(@msg_env_to, $1) if ($line =~ /env_To:[\t ]*<?([^\t> ]+).*/);
+	$line = <MSG>;
+    }
+
+
+    # Look for header lines that get checksums as we collect the whole message.
+    $new_hdr = "";
+    undef($hdr_type);
+    for (;;) {
+	if (!($line = <MSG>)) {
+	    close(MSG);
+	    return ($ise_msg, "message $msg truncated in headers");
+	}
+
+	# dccifd logs header lines with <CR><LF> but dccm uses <LF>
+	$line =~ s/\r\n$/\n/;
+
+	# deal with header continuation
+	if ($line =~ /^[\t ]+/) {
+	    $new_hdr .= $line;
+	    $$cur_hdr .= $line if ($cur_hdr);
+	    next;
+	}
+
+	if ($cur_hdr) {
+	    # end a preceding interesting header
+	    $$cur_hdr =~ s/[\t ]*\n[\r\s]*/ /g;
+	    $$cur_hdr =~ s/^\s+//;
+	    $$cur_hdr =~ s/\s+$//;
+	    # emit a link
+	    if (!$no_body) {
+		if ($hdr_type) {
+		    $msg_hdrs .= "$edit_link${url_ques}type=$hdr_type&amp;val=";
+		    $msg_hdrs .= url_encode($$cur_hdr);
+		    $msg_hdrs .= "&amp;msg=$msg&amp;auto=1#cur_key\">";
+		    chop($new_hdr);
+		    $msg_hdrs .= html_str_encode($new_hdr);
+		    $msg_hdrs .= "</A>\n";
+		    undef($hdr_type);
+		} else {
+		    $msg_hdrs .= html_str_encode($new_hdr);
+		}
+	    }
+	    undef $cur_hdr;
+	} else {
+	    # end preceding boring header
+	    $msg_hdrs .= html_str_encode($new_hdr);
+	}
+
+	# stop after the headers
+	last if ($line eq "\n");
+
+	++$num_hdrs;
+
+	$new_hdr = $line;
+
+	# Start an interesting header
+
+	if ($line =~ s/^from:\s*//i) {
+	    $hdr_type = "from";
+	    $msg_from = $line;
+	    $cur_hdr = \$msg_from;
+	    next;
+	}
+	if ($line =~ s/^(Message-ID):\s*//i) {
+	    $hdr_type = "Message-ID";
+	    $misc_hdr = $line;
+	    $cur_hdr = \$misc_hdr;
+	    $seen_message_id = 1;
+	    next;
+	}
+	if ($line =~ s/^subject:\s*//i && 'subject:' =~ /^($sub_hdrs):/i) {
+	    $hdr_type = url_encode("substitute subject");
+	    $msg_subject = $line;
+	    $cur_hdr = \$msg_subject;
+	    next;
+	}
+
+	if (!$no_body && $line =~ s/^($sub_hdrs):\s*//i) {
+	    $hdr_type = $1;
+	    $hdr_type =~ tr/A-Z/a-z/;
+	    $hdr_type = url_encode("substitute $hdr_type");
+	    $misc_hdr = $line;
+	    $cur_hdr = \$misc_hdr;
+	    next;
+	}
+    }
+
+    # fake empty Message-ID if required
+    if (!$seen_message_id && $num_hdrs) {
+	$msg_hdrs .= "$edit_link${url_ques}type=";
+	$msg_hdrs .= "Message-ID";
+	$msg_hdrs .= "&amp;val=%3c%3e&amp;msg=$msg&amp;auto=1#cur_key\">missing Message-ID</A>\n";
+    }
+
+    # copy the body of the message
+    for (;;) {
+	if (!($line = <MSG>)) {
+	    close(MSG);
+	    return ($ise_msg, "message $msg truncated in body");
+	}
+	last if ($line =~ $cksum_marker_p);
+	$line =~ s/[ \t\r]+$//mg;
+	$msg_body .= html_text_encode($line) if (!$no_body);
+    }
+
+
+    # copy the checksums
+    while ($line = <MSG>) {
+	# notice quoted checksums that are part of the body
+	if ($line =~ $cksum_marker_p) {
+	    if (!$no_body) {
+		$msg_body .= "<PRE class=mono>\n";
+		$msg_body .= $cksum_marker;
+		$msg_body .= $msg_cksums;
+		$msg_body .= "</PRE>\n";
+	    }
+	    $msg_cksums = '';
+	    $msg_result = ': ';
+	    next;
+	}
+
+	$msg_cksums .= $line;
+
+	# Build a string of all of the reasons why the message should
+	#   have been accepted or rejected as we build the list of checksums.
+	#   Use italics for disabled checks.
+	$msg_result .= "MTA " if ($line =~ /\bMTA-->spam(|\(first\))\b/);
+	$msg_result .= "MTA-OK " if ($line =~ /\bMTA-->OK(|\(first\))\b/);
+	$msg_result .= "BL " if ($line =~ /\bwlist-->spam\b/);
+	$msg_result .= "WL " if ($line =~ /\bwlist-->OK\b/);
+	$msg_result .= "DCC " if ($line =~ /\bDCC-->spam\b/);
+	$msg_result .= "<I>DCC</I> " if ($line =~ /\bDCC-->spam\(off\)\b/);
+	$msg_result .= "OK-DCC " if ($line =~ /\bDCC-->OK\b/);
+	$msg_result .= "<I>OK-DCC</I> " if ($line =~ /\bDCC-->OK\(off\)\b/);
+	$msg_result .= "Rep " if ($line =~ /\bRep-->spam\b/);
+	$msg_result .= "<I>Rep</I> " if ($line =~ /\bRep-->spam\(off\)\b/);
+	$msg_result .= "$1 " while ($line =~ s/\b(DNSBL\d?)-->spam\b//);
+	$msg_result .= "<I>$1</I> "
+		if ($line =~ s/\b(DNSBL\d?)-->spam\(off\)\b//	);
+
+	# Prefix the string of reasons with what was done.
+	if ($line =~ /^result: temporary greylist embargo/) {
+	    $msg_result = "Grey" . $msg_result;
+	} elsif ($line =~ /^result: accept after greylist embargo/) {
+	    $msg_result = "OK-Grey" . $msg_result;
+	} elsif ($line =~ /^result: accept/) {
+	    $msg_result = "OK" . $msg_result;
+	} elsif ($line =~ /^result: reject temporarily/) {
+	    $msg_result = "Delay" . $msg_result;
+	} elsif ($line =~ /^result: reject/) {
+	    $msg_result = "Reject" . $msg_result;
+	} elsif ($line =~ /^result: discard/) {
+	    $msg_result = "Discard" . $msg_result;
+	} elsif ($line =~ /^result: .*abort/) {
+	    $msg_result = "abort";
+	}
+    }
+    $msg_result =~ s/^: //;
+    $msg_cksums = html_str_encode($msg_cksums) if (!$no_body);
+
+
+    close(MSG);
+    return undef;
+}
+
+
+
+sub decode_msg_name {
+    my($str) = @_;
+    my($val, $i, $c);
+
+    use integer;
+
+    $val = 0;
+    for ($i = 0; $i < length($str); ++$i) {
+	$c = ord(substr($str, $i, 1));
+	if ($c >= ord('a')) {
+	    $c = $c - ord('a') + 10;
+	} elsif ($c >= ord('A')) {
+	    $c = $c - ord('A') + 10+26;
+	} else {
+	    $c -= ord('0');
+	}
+	$val = ($val * (10+26+26)) + $c;
+    }
+    return $val;
+}
+
+
+
+sub msg2path {
+    my($msg, $path) = @_;
+
+    $path = $logdir . '/' if (!defined $path);
+
+    if (length($msg) >= 8) {
+	$path .= sprintf("%03d/", decode_msg_name(substr($msg, 6, 2)));
+	if (length($msg) >= 9) {
+	    $path .= sprintf("%02d/", decode_msg_name(substr($msg, 8, 1)));
+	    if (length($msg) >= 10) {
+		$path .= sprintf("%02d/", decode_msg_name(substr($msg, 9, 1)));
+	    }
+	}
+    }
+
+    return $path . 'msg.' . substr($msg, 0, 6);
+}
+
+
+
+# flush one cache file
+sub cache_write_file {
+    my($buf, $cnum) = @_;
+    my($tmp, $cfname, $date);
+
+    $tmp = "msg.cache." . "new." . $$;
+    if (!sysopen(CFILE, $tmp, O_WRONLY | O_CREAT, 0660)){
+	print STDERR "open($tmp): $!\n";
+	return undef;
+    }
+    if (syswrite(CFILE, $cache_version) != length($cache_version)
+	|| syswrite(CFILE, $buf) != length($buf)) {
+	print STDERR "syswrite $tmp: $!\n";
+	close(CFILE);
+	unlink($tmp);
+	return undef;
+    }
+
+    close(CFILE);
+    $cnum =~ /(\d+)/; $cnum = $1;	# suppress Perl taint warning
+    $date = $cnum * (24*3600);
+    if ($date <= time) {
+	utime($date, $date, $tmp)
+	    || print STDERR "utime($date, $date, $tmp): $!\n";
+    }
+    $cfname = "msg.cache." . $cnum;
+    if (!rename($tmp, $cfname)) {
+	print STDERR "rename($tmp, $cfname): $!\n";
+	unlink($tmp);
+	return undef;
+    }
+
+    $msgs_cache_state{$cnum} = 1;
+    return 1;
+}
+
+
+
+# flush the cache files
+sub cache_flush {
+    my($cache_files, $log_files,
+       $cnum, $cfname, $state, $new_cnum, $msg, $buf, $buf_start);
+
+    if (! -w ".") {
+	my $marker = "$logout_tmpdir/msg.$user-nocache";
+	if (! -f $marker
+	    || (stat(_))[9] < time()-(4*3600)) {
+	    if (!open(CFILE,">>",$marker)){
+		print STDERR "open($marker): $!\n";
+	    } else {
+		print CFILE "$logdir not writable for cache files\n";
+		close CFILE;
+	    }
+	    print STDERR "$logdir not writable for cache files\n";
+	}
+	return;
+    }
+
+    $cache_files = 0;
+    $log_files = 0;
+    $buf_start = 0;
+
+    $cnum = 0;
+    foreach $msg (@msgs_num) {
+	# one cache file per day, so pick the cache for this log file
+	#   note that the files are sorted by mtime
+	$new_cnum = $msgs_cache{$msg}[0] / (24*3600);
+
+	# skip this log file if its cache file is good
+	next if ($msgs_cache_state{$new_cnum});
+
+	# close the current cache file if we are dealing with a new day
+	#	and so a new cache file
+	if ($cnum != $new_cnum) {
+	    if ($log_files - $buf_start > 10) {
+		++$cache_files;
+		return if (!cache_write_file($buf, $cnum));
+		$buf_start = $log_files;
+	    } else {
+		# forget the cache file if it would be tiny
+		$log_files= $buf_start;
+	    }
+	    undef $buf;
+	    $cnum = $new_cnum;
+	}
+
+	$buf .= pack($cache_pack,
+		     $msgs_cache{$msg}[0],
+		     $msgs_cache{$msg}[1],
+		     $msg);
+	++$log_files;
+    }
+    if ($log_files - $buf_start > 10) {
+	++$cache_files;
+	cache_write_file($buf, $cnum);
+    } else {
+	$log_files= $buf_start;
+    }
+
+    # delete junk cache files
+    while (($cnum, $state) = each %msgs_cache_state) {
+	next if ($state);
+	$cfname = "msg.cache." . $cnum;
+	if (-f $cfname && !unlink($cfname)) {
+	    print STDERR "unlink($cfname): $!\n";
+	    return;
+	}
+    }
+
+    debug_time("flushed $cache_files cache files with $log_files files");
+}
+
+
+
+# get the list of messages
+#   The first arg is the current file
+#   Try to limit the size of the table to the second arg
+#	divide days worth of files to fit the page size if it is <0
+#
+#   sets globals %msgs_date, %msgs_result, %msgs_from, %msgs_subject,
+#	$msg_day_first, $msg_day_last, $msg_first, $msg_last,
+#	$msg_newer, $msg_part_num, @msgs_num
+sub get_log_msgs {
+    my($page_msg,			# target log message
+       $page_size,			# log files / web page
+       $mode				# 0=old, 1=reverse sort & divide days
+       ) = @_;
+    my($cache_len, $need_flush, $cache_parse_limit, $dir_len,
+       $line, $msg, $entry, $days, $sort_order,
+       $msg_tgt, $date_tgt, $date_cur, $date1, $msg_num, $msg_num_prev, $start);
+
+    $cache_parse_limit = 100;
+
+    $cache_version = "DCC msg.cache version 3\n";
+    $cache_pack = "LLA10";
+    $cache_line_len = length(pack($cache_pack, 0));
+    $msg_encode_str = ("0123456789"
+		       . "abcdefghijklmnopqrstuvwxyz"
+		       . "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+
+    # reverse the sort for old callers
+    $sort_order = !$mode ? -1 : 1;
+
+
+    # Build a list of log file names and dates
+    #	Use cache files of names and dates.  Validate the cache
+    #	files by checking i-numbers.  Use the `ls` command because the
+    #	the Perl readdir() function does not provide d_ino/d_fileno.
+
+    chdir($logdir) || html_whine("chdir($logdir): $!");
+
+    # ls -ifC1 would be faster, but it does not work on files on Solaris
+    html_whine("ls -iC1 $logdir: $!")
+	if (!open(DIR, "find . -name 'msg.*' | @DCC_XARGS@ /bin/ls -iC1 |"));
+    $dir_len = 0;
+    while ($line = <DIR>) {
+	# find simple log files as well as log files in DDD, DDD/HH, and
+	#   DDD/HH/MM subdirectories
+	if ($line =~ /^\s*(\d+)\s+\.\/((\d\d\d)(\/(\d\d))?(\/(\d\d))?\/)?msg\.([A-Za-z\d]{6})\s*$/) {
+	    my ($filename, $subdir);
+	    $filename = $8;
+	    if (defined $3) {
+		# encode the subdirectory
+		next if ($3 > 366);
+		use integer;
+		$filename .= (substr($msg_encode_str,
+				    $3 / length($msg_encode_str), 1)
+			      . substr($msg_encode_str,
+				       $3 % length($msg_encode_str), 1));
+		if (defined $5) {
+		    next if ($5 >= 24);
+		    $filename .= substr($msg_encode_str, $5, 1);
+		    if (defined $7) {
+			next if ($7 >= 60);
+			$filename .= substr($msg_encode_str, $7, 1);
+		    }
+		}
+	    }
+	    $msgs_cache{$filename}[1] = $1;
+	    ++$dir_len;
+	    next;
+	}
+	# notice cache files
+	if ($line =~ /^\s*\d+\s+\.\/(msg\.cache\.(\d{5}))\s*$/
+	    && -f $1
+	    && (((stat(_))[7] - length($cache_version))
+		% $cache_line_len) == 0) {
+	    $msgs_cache_state{$2} = 0;
+	    next;
+	}
+    }
+    close(DIR);
+    debug_time("$dir_len files found");
+
+    # load the cache files
+    #	    Because cache files are named with dates, are read in sorted
+    #	    order, and have sorted contents, we do not need to sort
+    #	    what we read from them.
+    $cache_len = 0;
+    $need_flush = 0;
+    foreach my $cnum (sort keys(%msgs_cache_state)) {
+	my($total, $good, $date_lo, $date_hi);
+
+	next if (!open(CFILE, "msg.cache." . $cnum));
+
+	if (!read(CFILE, $buf, length($cache_version))
+	    || $buf ne $cache_version) {
+	    close CFILE;
+	    next;
+	}
+
+	# the names in a cache file are for a single day
+	$date_lo = $cnum * 24*3600;
+	$date_hi = $date_lo + 24*3600 - 1;
+	$good = $total = 0;
+	while (read(CFILE, $buf, $cache_line_len)) {
+	    my($date, $ino, $msg) = unpack($cache_pack, $buf);
+	    ++$total;
+
+	    # a cache file is bogus if it contains bad dates
+	    last if ($date < $date_lo || $date > $date_hi);
+
+	    # skip deleted log files
+	    next if (!exists($msgs_cache{$msg}));
+
+	    # skip cached file names that have been recycled
+	    next if ($msgs_cache{$msg}[1] != $ino);
+
+	    $msgs_cache{$msg}[0] = $date;
+	    push @msgs_num, $msg;
+
+	    ++$good;
+	}
+	close(CFILE);
+	if ($good == 0 || $good+20 < $total) {
+	    $need_flush = 1;
+	} elsif ($good == $total) {
+	    $msgs_cache_state{$cnum} = 1;
+	}
+	$cache_len += $good;
+    }
+    debug_time("$cache_len files cached");
+
+    # If there are any new log files,
+    #	then we must get their dates and then sort all of the names
+    if ($cache_len != $dir_len) {
+	$need_flush = 1 if ($dir_len > $cache_len+100);
+	$msg_num = 0;
+	while (($msg, $entry) = each %msgs_cache) {
+	    next if (@$entry[0]);	# we know about this file from cache
+
+	    my $date = (stat msg2path($msg))[9];
+	    if (!$date) {
+		# forget this file if we cannot stat() it
+		delete $msgs_cache{$msg};
+		next;
+	    }
+	    @$entry[0] = $date;
+	    $msgs_cache_state{$date / (24*3600)} = 0;
+	    ++$msg_num;
+	}
+	debug_time("$msg_num files dated");
+
+	# this is obscure but much faster than using comparison functions that
+	#	do hash lookups for each comparison of the sort
+	@msgs_num = map {my @a = unpack("NA10",$_); $a[1]}
+			(sort map pack("NA10",
+				       $msgs_cache{$_}[0]*$sort_order,
+				       $_),
+				    keys %msgs_cache);
+	debug_time("sorted " . ($#msgs_num+1) . " files");
+    }
+
+    # find the target message that must be listed
+    $msg_tgt = ($sort_order > 0 && $#msgs_num >= 0) ? $#msgs_num : 0;
+    if ($page_msg) {
+	for ($msg_num = 0; $msg_num <= $#msgs_num; ++$msg_num) {
+	    if ($msgs_num[$msg_num] eq $page_msg) {
+		$msg_tgt = $msg_num;
+		last;
+	    }
+	}
+	debug_time("found #" . $msg_tgt);
+    }
+
+    # we are finished if the caller only wanted the list of files
+    #   perhaps for URLs pointing to previous and next files
+    if (!$page_size || $page_size < 1 || $#msgs_num < 0) {
+	cache_flush() if ($need_flush);
+	$msg_first = $msg_tgt;
+	$msg_last = $#msgs_num;
+	$msg_newer = $#msgs_num;
+	$msg_part_num = 0;
+	$msgs_mtime{$page_msg} = 1 if (!$mode && $page_msg);
+	return;
+    }
+
+    # Get summary information from all of the files on the target day, the
+    #	last file on the previous day, and on the first file on the next day.
+    #
+    # walk backward from the target to the first log file of the target day
+    $date_tgt = $date_cur = (localtime $msgs_cache{$msgs_num[$msg_tgt]}[0])[7];
+    for ($msg_day_first = $msg_tgt;
+	 $msg_day_first > 0;
+	 $msg_day_first = $msg_num) {
+	$msg_num = $msg_day_first-1;
+	$date_cur = (localtime $msgs_cache{$msgs_num[$msg_num]}[0])[7];
+	last if ($date_cur != $date_tgt);
+    }
+    if (!$mode) {
+	$msg_part_num = 0;
+	$msg_first = $msg_day_first;
+    } else {
+	$msg_part_num = ($msg_tgt - $msg_day_first) / $page_size;
+	$msg_first = $msg_day_first + ($msg_part_num * $page_size);
+    }
+
+    # walk forward to the end of the day or $page_size files
+    $days = 0;				# count space used by date headings
+    $msg_last = $msg_first + $page_size-1;
+    $msg_last = $#msgs_num if ($msg_last > $#msgs_num);
+    $msg_newer = $msg_first + $page_size;
+    $msg_newer = $#msgs_num if ($msg_newer > $#msgs_num);
+    $msg_day_last = $#msgs_num;
+    $date1 = $date_tgt;
+    for ($msg_num = $msg_tgt+1; $msg_num <= $#msgs_num; ++$msg_num) {
+	$date_cur = (localtime $msgs_cache{$msgs_num[$msg_num]}[0])[7];
+	next if ($date_cur == $date1);
+
+	++$days;
+
+	if ($date1 == $date_tgt) {
+	    $msg_day_last = $msg_num-1;
+
+	    # the "newer" link goes to the first file of the next day if
+	    #	the current day fits on the web page
+	    $msg_newer = $msg_num if (!$msg_newer || $msg_num < $msg_newer);
+	}
+
+	if ($msg_num > $msg_first + $page_size - $days) {
+	    $msg_last = $msg_num-1 if (!$mode);
+	    last;
+	}
+
+	$msg_last = $msg_num-1 if ($#msgs_num > $msg_first+$page_size-$days);
+	$date1 = $date_cur;
+    }
+
+    if ($mode) {
+	++$msg_part_num if ($msg_part_num != 0
+			    || $msg_first + $page_size <= $msg_day_last);
+	# overlap the parts of a day by a line
+	++$msg_last if ($msg_part_num != 0 && $msg_last < $msg_day_last);
+    }
+
+    # parse the log files to get the data
+    for ($msg_num = $msg_first; $msg_num <= $msg_last; ++$msg_num) {
+	$msg = $msgs_num[$msg_num];
+	my(@error) = parse_log_msg($msg, "no body");
+	if (defined $error[0]) {
+	    $msgs_date{$msg} = strftime("%x %X",
+					localtime($msgs_cache{$msg}[0]))
+		if (!$msgs_date{$msg} && $msgs_cache{$msg}[0]);
+	    $msgs_from{$msg} = "<STRONG class=warn>$error[0]</STRONG>";
+	    $msgs_result{$msg} = '';
+	    $msgs_subject{$msg} = "<STRONG class=warn>$error[1]</STRONG>";
+	} else {
+	    $msgs_date{$msg} = $msg_date;
+	    $msgs_from{$msg} = hdr_trim_encode($msg_from
+					       ? $msg_from
+					       : $msg_env_from);
+	    $msgs_result{$msg} = $msg_result ? $msg_result : "&nbsp;";
+	    $msgs_subject{$msg} = hdr_trim_encode($msg_subject);
+	}
+    }
+    debug_time(($msg_last - $msg_first + 1) . " log files parsed");
+
+    cache_flush() if ($need_flush);
+}
+
+
+
+##########################################################################
+# whiteclnt file functions
+
+# The file is represented as an array or list of references to 3-tuples.
+#   The first of the three is the whitelist entry in a canonical form
+#	as a key uniquely identifying the entry.
+#   The second is a comment string of zero or more comment lines.
+#   The third is the DCC whiteclnt entry.
+#
+#   The canonical form and the whiteclnt line of the first 3-tuple for a file
+#   are null, because it contains the comments, if any, before the file's
+#   preamble of dans when the file has been changed and flags.
+#   The file[1] is an empty slot for adding option settings.
+#   The last triple in a file may also lack a whitelist entry.
+
+# There is a hash or dictionary of references to entries in the list
+
+
+# lock, read, and parse the file
+sub read_whiteclnt {
+    my($file_ref, $dict_ref) = @_;
+    my($entry, $prev_entry, $comment);
+
+    @$file_ref = ();
+    %$dict_ref = ();
+
+    # Creating the file here is usually a waste of effort, because
+    #	it must be writable by both the HTTP server and dccm or dccifd.
+    #	They are probably not in any common group.
+    # Let the @libexecdir@/newwebuser script create the per-user
+    #	directories and files.
+    # Because whitelists might be a little sensitive, they should not be
+    #	readable by "other"
+    html_whine("open($whiteclnt): $!")
+	if (!sysopen(WHITECLNT, $whiteclnt, O_RDWR | O_CREAT, 0660));
+    chmod(0660, $whiteclnt);
+
+    html_whine("flock($whiteclnt): $!")
+	if (!flock(WHITECLNT, LOCK_EX | LOCK_NB));
+
+    $comment = "";
+    while ($entry = <WHITECLNT>) {
+	# end the last line properly even if the file doesn't
+	$entry .= "\n" if (substr($entry,-1) ne "\n");
+
+	# collect lines until we get a non-comment
+	if ($entry =~ /^\s*(#|$)/) {
+	    $comment .= $entry;
+	    next;
+	}
+
+	# use the previous count if the current value is missing,
+	#	because that is what dcclib/parse_whitefile.c does.
+	$entry = "$1$entry"
+	    if ($entry =~ /^[ \t]/
+		&& $#$file_ref > 0
+		&& ($prev_entry = ${${$file_ref}[$#$file_ref]}[2])
+		&& $prev_entry =~ /^(\S+)/);
+
+	add_white_entry($file_ref, $dict_ref, $comment, $entry);
+	$comment = "";
+    }
+
+    # save a non-trivial trailing comment
+    add_white_entry($file_ref, $dict_ref, $comment, "")
+	if ($comment && $comment !~ /^\s*$/);
+}
+
+
+
+# read the main whiteclnt file to determine the default option settings
+sub read_whitedefs {
+    my($def_ref) = @_;
+    my(@sb1, @sb2, $line, @parsed, $bydef);
+
+
+    # these defaults for the defaults must match dcclib/parse_whitefile.c
+    #	or elsewhere in the DCC client source (e.g. for discardok)
+    %$def_ref = ();
+    $bydef = " <SMALL>by default</SMALL>";
+    ${$def_ref}{dccenable} = "<STRONG>on</STRONG>$bydef";
+    ${$def_ref}{greyfilter} = "<STRONG>on</STRONG>$bydef";
+    ${$def_ref}{greylog} = "<STRONG>on</STRONG>$bydef";
+    ${$def_ref}{mtafirst} = "<STRONG>last</STRONG>$bydef";
+    ${$def_ref}{rep} = "<STRONG>off</STRONG>$bydef";
+    ${$def_ref}{dnsbl1} = "<STRONG>off</STRONG>$bydef";
+    ${$def_ref}{dnsbl2} = "<STRONG>off</STRONG>$bydef";
+    ${$def_ref}{dnsbl3} = "<STRONG>off</STRONG>$bydef";
+    ${$def_ref}{logall} = "<STRONG>off</STRONG>$bydef";
+    ${$def_ref}{discardok} = "<STRONG>delay mail</STRONG>$bydef";
+
+    foreach my $ck (split(/,/,$thold_cks)) {
+	my $nm = "thold-$ck";
+	if (!$conf_cks_tholds{$ck}) {
+	    ${$def_ref}{$nm} = "<STRONG>Never</STRONG>$bydef";
+	} else {
+	    ${$def_ref}{$nm} = $conf_cks_tholds{$ck};
+	}
+    }
+
+    if (!sysopen(MAINWHITE, $main_whiteclnt, O_RDONLY, 0)) {
+	print STDERR "open(${main_whiteclnt}: $!\n";
+	return;
+    }
+
+    if (!(@sb1 = stat(MAINWHITE))) {
+	print STDERR "stat(${main_whiteclnt}: $!\n";
+    } elsif (!(@sb2 = stat(WHITECLNT))) {
+	print STDERR "stat(${$whiteclnt}: $!\n";
+    } elsif ($sb1[0] == $sb2[0] && $sb1[1] == $sb2[1]) {
+	# ignore it if we are somehow working on the main file
+    } else {
+	while ($line = <MAINWHITE>) {
+	    # skip everything except option settings
+	    next if ($line !~ /^\s*option\s+/i);
+
+	    @parsed = parse_white_entry($line, "option");
+	    next if (!$parsed[1]);
+	    ${$def_ref}{$parsed[0]} = "<STRONG>$parsed[2]</STRONG> <SMALL>by default in $main_whiteclnt</SMALL>";
+	}
+    }
+    close(MAINWHITE);
+}
+
+
+
+# add an entry to our image of the file
+#   sets the globals:
+#   $whiteclnt_version,		#webuser version ...
+#   $whiteclnt_notify,		#webuser mail-notify=X mailbox=Y
+#   $whiteclnt_notify_pat,	#regex for #webuser mail-notify=X mailbox=Y
+#   $whiteclnt_lock,		#webuser (un)locked
+#   $whiteclnt_cur_key		#editing position in the file
+#   $whiteclnt_change_log,	#list of dates when file was changed
+
+sub add_white_entry {
+    my($file_ref, $dict_ref, $comment, $line) = @_;
+    my(@parsed);
+
+    # trim unneeded white space
+    $line =~ s/\s+$//;
+    $comment =~ s/[ \t]+$//mg;
+
+    # deal with the preamble.
+    #	The preamble consists of the comments that start the file.
+    if (! @$file_ref) {
+	my($preamble, @buf);
+
+	# remove the change-history, version, and parameters from the preamble
+	$whiteclnt_version = "#webuser version 1.0\n";
+	while ($comment =~ s/^#webuser version ([0-9.]+)\n/ \n/m) {
+	    # for now, insist on version 1.0
+	    html_whine("unrecognized version $1 in $whiteclnt")
+	       if ($1 ne "1.0");
+	}
+
+	$whiteclnt_notify_pat = '(#webuser mail-notify=)(on|off)( mailbox=)([-_a-z0-9]*)';
+	$whiteclnt_notify = "#webuser mail-notify=off mailbox=\n";
+	while ($comment =~ s/^$whiteclnt_notify_pat\n/ \n/im) {
+	    $whiteclnt_notify = "$1$2$3$4\n";
+	}
+
+	$whiteclnt_lock = "#webuser unlocked\n";
+	while ($comment =~ s/^#\s*webuser\s+unlocked\n/ \n/im) {
+	}
+	while ($comment =~ s/^#\s*webuser\s+locked\n/ \n/im) {
+	    $whiteclnt_lock = "#webuser locked\n";
+	}
+
+	$whiteclnt_cur_key = "";
+	while ($comment =~ s/^#\s*webuser\s+cur_key\s+(.*)\n/ \n/im) {
+	    $whiteclnt_cur_key = $1;
+	}
+
+	$whiteclnt_change_log = "";
+	while ($comment =~ s/^#\s*webuser created\s+(.+\n)/ \n/im) {
+	    $whiteclnt_change_log = "#webuser created $1";
+	}
+	undef(@buf);
+	while ($comment =~ s/^#webuser\s+changed\s+(.+\n)/ \n/im) {
+	    push(@buf, "#webuser changed $1");
+	}
+	# keep only the last 20 dates of change
+	if (@buf) {
+	    my($start);
+	    $start = $#buf-20;
+	    $start = 0
+		if ($start < 0);
+	    $whiteclnt_change_log .= join('', @buf[$start .. $#buf]);
+	}
+
+	# We have removed the parameter lines from the first comment of the
+	# file and replaced them with " \n"
+	# Before starting we remove blanks from the ends of lines.
+	# The first block of comments must now be divided between (1) comments
+	# about the file and (2) comments about the first real line of the file.
+	if ($comment =~ s/^(.* \n)//s) {
+	    # Take the comment lines through the last marker if there were any.
+	    # Add the first blank line after the markers if present.
+	    $preamble = $1;
+	    $preamble .= "\n" if ($comment =~ s/^\n//);
+	    # remove the markers for detected parameter lines
+	    $preamble =~ s/ \n//g;
+	} else {
+	    # without parameters, take lines through the first blank line as (1)
+	    $preamble = ($comment =~ s/(.*?\n\n)//s
+			 || $comment =~ s/(.*?\n[# \t]+\n)//s) ? $1 : "";
+	}
+
+	# start the memory copy of the file with the preamble
+	#   and the spare slot for option changes
+	@$file_ref = ([undef, $preamble, ""], ["", undef, undef]);
+
+	# finished if the file has no entries,
+	return if (!$line && !$comment);
+
+	# or deal with the first entry
+    }
+
+    # If the line makes sense, remember where it will be.
+    # Treat the line as a comment if it makes no sense
+    @parsed = parse_white_entry($line, "");
+    if (!$parsed[1]) {
+	$comment .= $line;
+	$comment .= "\n" if ($comment !~ /\n$/);
+	push @$file_ref, [undef, $comment, ""];
+    } else {
+	my($cur_key, $entry, $i, $k);
+
+	$cur_key = $parsed[0];
+	$entry = [$cur_key, $comment, $parsed[1]];
+	push @$file_ref, $entry;
+
+	if (${$dict_ref}{$cur_key}) {
+	    $i = 0;
+	    # mark duplicate values for eventual deletion
+	    #	keep the last setting in the file
+	    while (${$dict_ref}{$k = "DUP-$i-$cur_key"}) {
+		++$i;
+	    }
+	    ${$dict_ref}{$k} = ${$dict_ref}{$cur_key};
+	}
+	${$dict_ref}{$cur_key} = $entry;
+    }
+}
+
+
+# check the syntax of IP addresses and CIDR blocks
+#   return undef if ok but an error string if not
+sub check_ip {
+    my($value) = @_;
+
+    return "blank or missing IP address" if (!$value);
+
+    my $addr = $value;
+    if ($addr =~ s/(.*)\/(\d+)/$1/) {
+	my $cidr = $2;
+	$cidr += 96 if ($addr =~ /:/);
+	return "\"$value\" is not a valid CIDR block"
+	    if ($cidr > 128 || $cidr <= 0);
+    }
+
+    # it would be better to use some library to parse the IP address,
+    #	but in 2005, there was no Perl module that could handle IPv6
+    #	addresses and is almost always available
+    return "\"$value\" is not a valid IP address"
+	if ($addr !~ /^[.:0-9a-f]+$/);
+
+    if ($addr =~ /:/) {
+	# IPv6
+	my $colons = $addr;
+	$colons =~ s/[^:]+//g;
+	$colons = length($colons);
+	if ($addr =~ /^::/ && $colons <= 7) {
+	    ++$colons while ($colons < 7 && $addr =~ s/^::/::0:/);
+	    $addr =~ s/^::/0:/ if ($colons == 7);
+	} elsif ($addr =~ /::$/ && $colons <= 7) {
+	    ++$colons while ($colons < 7 && $addr =~ s/::$/:0::/);
+	    $addr =~ s/::$/:0/ if ($colons == 7);
+	} else {
+	    ++$colons while ($colons < 7 && $addr =~ s/::/::0:/);
+	    $addr =~ s/::/:0:/ if ($colons == 7);
+	}
+	return "$value is not a valid IP address" if ($colons > 7);
+
+	$addr =~ s/^([0-9a-f]{1,4}:)+//;
+	$addr =~ s/^[0-9a-f]{1,4}$/127.0.0.1/;
+	return "\"$value\" is not a valid IP address" if ($addr =~ /:/);
+
+	# stop looking at IPv6 address with either the IPv4 trailing part
+	#or a fake 127.0.0.1
+    }
+
+    my $quads = 0;
+    while ($addr =~ s/^(\d{1,3})\.//) {
+	return "\"$value\" is not a valid IP address" if ($1 > 255);
+	++$quads;
+    }
+    return "$value is not a valid IP address" if ($addr > 255 || $quads > 3);
+
+    # we should now check for collisions among addresses
+
+    return undef;
+}
+
+
+
+sub check_present {
+    my($type, $value) = @_;
+
+    # dcc_str2ck() via dcc_parse_ck() ignores outside quotes and <>,
+    # whitespace, and upper/lower case, and trailing periods.  So our key must
+    #	also.
+    # The value for the line in the file need not be as clean.
+    $value =~ s/^\s+//;
+    $value =~ s/\s+$//;
+    $value =~ s/^<\s*(.+)\s*>$/$1/
+	if ($value !~ s/^"\s*(.+)\s*"$/$1/);
+    $value =~ s/\.+$//;
+
+    return ($type, $value) if ($value);
+    return ($type, "<>", "blank or missing $type value");
+}
+
+
+
+sub check_hex {
+    my($type, $value) = @_;
+
+    return ($type, $value, "blank or missing $type value")
+	if (!$value);
+
+    return ($type, $value) if ($value =~ s/([0-9a-f]{8})\s+([0-9a-f]{8})
+					\s+([0-9a-f]{8})\s+([0-9a-f]{8})$
+					/$1 $2 $3 $4/ix);
+
+    return ($type, $value, "\"$value\" is an invalid $type checksum");
+}
+
+
+
+# canonicalize a whitelist checksum "type value" string
+#   return a (type, value) pair or (x, x, "error string") triple
+sub parse_type_value {
+    my $value = $_[0];
+
+    # Check for type
+    #	    Don't support received checksums.
+    #	    Body checksums must be hex.
+    $value =~ s/\s+$//;
+
+    return ("IP", $value, check_ip($value))
+	if ($value =~ s/^IP:?(\s*|$)//i);
+
+    return check_present("env_From", $value)
+	if ($value =~ s/^env[-_]from:?(\s+|$)//i);
+
+    return check_present("env_To", $value)
+	if ($value =~ s/^env[-_]To:?(\s+|$)//i);
+
+    return check_present("From", $value)
+	if ($value =~ s/^from:?(\s+|$)//i);
+
+    return check_present("Message-ID", $value)
+	if ($value =~ s/^message[-_]id:?(\s+|$)//i);
+
+    # don't worry much about substitute types.
+    return check_present("substitute $1", $value)
+	if ($value =~ s/^substitute\s+([-a-z_0-9]+)+:?(\s+|$)//i);
+
+    return check_hex("hex Body", $value)
+	if ($value =~ s/^hex\s+body:?(\s+|$)//i);
+
+    return check_hex("hex Fuz$1", $value)
+	if ($value =~ s/^hex\s+fuz([12]):?(\s+|$)//i);
+
+    return (undef, undef, "unrecognized whiteclnt value \"$value\"");
+}
+
+
+
+# canonicalize a threshold setting
+sub parse_thold_value {
+    my($pat, $type, $val);
+
+    # check the name of the checksum by converting it into a pattern
+    #	and matching it against the list of checksum types that can have
+    #	per-user thresholds
+    $pat = ",($_[0]),";
+    $pat =~ s/[-_]/[-_]/g;
+    $type = ',' . $thold_cks . ',';
+    return 0 if ($type !~ /$pat/i);
+    $type = $1;
+
+    # check the threshold value
+    if ($_[1] =~ /^Never$/i) {
+	$val = 'Never';
+    } elsif ($_[1] =~ /^many/i) {
+	# reputation threshold is a % and reputation total is finite
+	return 0 if ($type =~ /^rep/);
+	$val = "many";
+    } elsif ($_[1] =~ /^\d+$/) {
+	$val = $_[1];
+	if ($type =~ /^rep$/i) {
+	    return 0 if ($val > 100);
+	    $val .= '%';
+	}
+    } elsif ($_[1] =~ /^(\d+)%$/) {
+	# reputation threshold is a %
+	return 0 if ($1 > 100 || $type !~ /^rep$/i);
+	$val = $_[1];
+    } else {
+	return 0;
+    }
+
+
+    $_[0] = $type;
+    $_[1] = $val;
+    return 1;
+}
+
+
+
+# See if a whiteclnt line makes sense
+#   If so, return a list of key and canonicalized line.
+#	If it is an option setting, return a third string that is the value
+#	for the edit form.
+#   If not, return only an error message.
+sub parse_white_entry {
+    my($line,					# line to parse
+       $mode					# ''=accept from file,
+						# 'option'=new option setting
+						# 'strict'=new whitelist entry
+       ) = @_;
+
+    my($count, $key, $type, $value, $emsg);
+
+    # recognize options
+    if (!$mode || $mode eq "option") {
+	return ("dccenable", "option dcc-$1\n", "$1")
+	    if ($line =~ /^\s*option\s+DCC-(on|off)\s*$/i);
+
+	return ("greyfilter", "option greylist-$1\n", "$1")
+	    if ($line =~ /^\s*option\s+greylist-(on|off)\s*$/i);
+
+	return ("greylog", "option greylist-log-$1\n", "$1")
+	    if ($line =~ /^\s*option\s+greylist-log-(on|off)\s*$/i);
+
+	return ("mtafirst", "option MTA-$1\n", "$1")
+	    if ($line =~ /^\s*option\s+MTA-(first|last)\s*$/i);
+
+	return ("rep", "option DCC-rep-$1\n", "$1")
+	    if ($line =~ /^\s*option\s+DCC-reps?-(on|off)\s*$/i);
+
+	return ("dnsbl1", "option dnsbl1-$1\n", "$1")
+	    if ($line =~ /^\s*option\s+dnsbl-(on|off)\s*$/i);
+	return ("dnsbl$1", "option dnsbl$1-$2\n", "$2")
+	    if ($line =~ /^\s*option\s+dnsbl([123])-(on|off)\s*$/i);
+
+	return ("logall", "option log-all\n", "on")
+	    if ($line =~ /^\s*option\s+log-all\s*$/i);
+	return ("logall", "option log-normal\n", "off")
+	    if ($line =~ /^\s*option\s+log-normal\s*$/i);
+
+	return ("logsubdir", "option log-subdirectory-$1\n", "$1")
+	   if ($line =~ /^\s*option\s+log-subdirectory-(day|hour|minute)\s*$/i);
+
+	return ("discardok", "option forced-discard-ok\n", "discard spam")
+	    if ($line =~ /^\s*option\s+forced-discard-ok\s*$/i);
+	return ("discardok", "option no-forced-discard\n", "delay mail")
+	    if ($line =~ /^\s*option\s+no-forced-discard\s*$/i
+		|| $line =~ /^\s*option\s+forced-discard-nok\s*$/i); # obsolete
+
+	if ($line =~ /^\s*option\s+threshold\s+(\S+),(\S+)\s*$/i) {
+	    $type = $1;
+	    $value = $2;
+	    return ("thold-$type", "option threshold $type,$value\n", "$value")
+		if (parse_thold_value($type, $value));
+	}
+
+	# recognize old logging options
+	return ("greylog", "option greylist-log-on\n", "on")
+	    if ($line =~ /^\s*log\s+all-grey\s*$/i);
+	return ("greylog", "option greylist-log-off\n", "off")
+	    if ($line =~ /^\s*log\s+no-grey\s*$/i);
+
+	# we are finished if only parsing a new option line we know is ok
+	return "unrecognized option line" if ($mode && $mode eq "option");
+    }
+
+    # must be "" with a bad option or "strict" when we should see an option
+    return "unrecognized option line"
+	if ($line =~/^log/i || $line =~ /^option/i);
+
+    return "unrecognized line" if ($line !~ /^(\S+)\s+(.*)/);
+    $count = $1;
+    $value = $2;
+
+    return "unrecognized count \"$count\"" if ($count !~ /many|ok|ok2/i);
+
+    ($type, $value, $emsg) = parse_type_value($value);
+    return $emsg if ($emsg);
+
+    # build the whiteclnt line
+    $line = "$count\t$type";
+    $line .= (length($type) < 8) ? "\t" : ' ';
+    $line .= "$value\n";
+
+    $value =~ s/\s//g;
+    $value =~ tr/A-Z/a-z/;
+    $key = "$type $value";
+
+    return ($key, $line);
+}
+
+
+
+# check a proposed entry
+#   return an array of the error message if the proposed entry is bogus
+#	or an array of (key, comment, line) if it make sense
+sub ck_new_white_entry {
+    my($comment, $count, $type, $value) = @_;
+    my(@parsed, @entry);
+
+    return "missing comment" if (!defined($comment));
+    return "missing count" if (!$count);
+    return "missing type" if (!$type);
+    return "missing value" if (!$value);
+
+    # trim trailing whitespace from the comment lines
+    $comment =~ s/\s+\n/\n/g;
+    # ensure comment lines start with '#'
+    $comment =~ s/^([ \t]*[^# \t\n])/#$1/gm;
+    # trim trailing blank lines from the comment
+    $comment =~ s/\s+$//s;
+    $comment .= "\n" if (length($comment) != 0);
+
+    @parsed = parse_white_entry("$count $type $value", "strict");
+    return ($parsed[0])	if (!defined($parsed[1]));
+
+    $entry[0] = $parsed[0];
+    $entry[2] = $parsed[1];
+    $entry[1] = $comment;
+    return @entry;
+}
+
+
+
+# add, change, or delete a whitelist entry
+#   write our image of the file to disk, changing it as we go
+#   then read the file
+sub chg_white_entry {
+    my($file_ref,			# the file in memory
+       $dict_ref,			# dictionary for the file
+       $cur_key,			# change or delete this entry
+       $entry_ref,			# change this if not null
+       $add_pos				# add before this if defined
+       ) = @_;
+    my($msg, $i, $k, @file);
+
+    return "$whiteclnt locked" if ($whiteclnt_lock =~ /\blocked/);
+
+    @file = @$file_ref;
+
+    if (!${$dict_ref}{$cur_key}) {
+	# it is a new entry if it exists
+	if ($entry_ref) {
+	    # add it to the list that will go to the disk
+	    ${$dict_ref}{$cur_key} = @$entry_ref;
+	    if (!$add_pos || $add_pos > $#file) {
+		# append to the file without a good position
+		push @file, $entry_ref;
+	    } else {
+		# insert at the right position
+		@file = (@file[0 .. $add_pos-1],
+			 $entry_ref,
+			 @file[$add_pos .. $#file]);
+	    }
+	}
+
+    } else {
+	# changing or deleting existing entry, so delete duplicates
+	$i = 0;
+	while (${$dict_ref}{$k = "DUP-$i-$cur_key"}) {
+	    ${$dict_ref}{$k}[1] = undef;
+	    ++$i;
+	}
+
+	if (!$entry_ref) {
+	    # delete an entry
+	    ${$dict_ref}{$cur_key}[1] = undef;
+
+	} else {
+	    # change an entry
+	    @{${$dict_ref}{$cur_key}} = @$entry_ref;
+	}
+    }
+
+    # put the changes on the disk
+    $msg = write_whiteclnt(@file);
+    return $msg if ($msg);
+
+    # set the web form that includes the response
+    read_whiteclnt($file_ref, $dict_ref);
+    return undef;
+}
+
+
+
+# write a new version of the file
+sub write_whiteclnt {		# return undef or error message
+    my(@file) = @_;
+    local(*DIR, *BAK);
+    my(@baks, $bak, $buf, $entry, $preamble);
+
+    # delete old backup files and find the name of the next one
+    # keep only the last few and fairly recent revisions
+    opendir(DIR, "$user_dir") or html_whine("opendir($user_dir): $!");
+    @baks = map("$user_dir/$_",
+		sort grep {/^(whiteclnt\.bak\d+$)/ && -f "$user_dir/$1"}
+			readdir(DIR));
+    closedir(DIR);
+    while ($#baks > 1 && ($baks[0] =~ /(.*\/whiteclnt\.bak\d+$)/)
+	   && ((-M $1) >= 1 || $#baks >= 19)) {
+	unlink $1;			# suppress taint warning
+	shift(@baks);
+    }
+    if ($#baks >= 0) {
+	$baks[$#baks] =~ /\/whiteclnt\.bak(\d+)$/;
+	$bak = sprintf("%s/whiteclnt.bak%06d", $user_dir, $1+1);
+    } else {
+	$bak = "$whiteclnt.bak000000";
+    }
+
+    # create the undo file and copy the real file to it
+    #	It could be smoother to rename the current file, but we might
+    #	not have permission to create the new file with the correct owner.
+    #	There are also dangers with symbolic links and rename().
+    return "cannot create $bak: $!"
+	if (!sysopen(BAK, $bak, O_WRONLY | O_CREAT | O_EXCL, 0660));
+    return "seek($whiteclnt): $!"
+	if (!seek(WHITECLNT, 0, 0));
+    while (read(WHITECLNT, $buf, 8*1024)) {
+	return "write($bak): $!"
+	    if (!syswrite(BAK, $buf));
+    }
+    close(BAK);
+
+    # rewrite the real file
+    return "seek($whiteclnt): $!"
+	if (!seek(WHITECLNT, 0, 0));
+    return "truncate($whiteclnt): $!"
+	if (!truncate(WHITECLNT, 0));
+
+    $preamble = 0;
+    foreach $entry (@file) {
+	# skip deleted entries,
+	next if (!defined($$entry[1]));
+
+	# put the parameters in the preamble
+	if (!$preamble) {
+	    $preamble = $$entry[1];
+	    $whiteclnt_change_log .= strftime("#webuser changed %x %X%n",
+					      localtime);
+	    $preamble =~ s/\n(\n?)$/\n/;
+	    $whiteclnt_change_log .= $1;
+	    print WHITECLNT $preamble;
+	    print WHITECLNT $whiteclnt_version;
+	    print WHITECLNT $whiteclnt_notify;
+	    print WHITECLNT $whiteclnt_lock;
+	    print WHITECLNT "#webuser cur_key $whiteclnt_cur_key\n"
+		if ($whiteclnt_cur_key);
+	    print WHITECLNT $whiteclnt_change_log;
+	} else {
+	    print WHITECLNT $$entry[1];
+	    print WHITECLNT $$entry[2];
+	}
+    }
+
+    return undef;
+}
+
+
+
+# undo the most recent operation by copying from the newest backup
+sub undo_whiteclnt {
+    my($bak, $buf);
+    local(*BAK);
+
+    return "$whiteclnt locked" if ($whiteclnt_lock =~ /\blocked/);
+
+    $bak = newest_whiteclnt_bak();
+    return "nothing undone"
+	if (!$bak);
+
+    return "open($bak): $!"
+	if (!open(BAK, "< $bak"));
+
+    return "seek($whiteclnt): $!"
+	if (!seek(WHITECLNT, 0, 0));
+    return "truncate($whiteclnt): $!"
+	if (!truncate(WHITECLNT, 0));
+    while (read(BAK, $buf, 8*1024)) {
+	return "write($whiteclnt): $!"
+	    if (!print(WHITECLNT $buf));
+    }
+
+    return "unlink($bak): $!"
+	if (!unlink($bak));
+
+    return undef;
+}
+
+
+
+# find the newest backup file
+sub newest_whiteclnt_bak {
+    local(*DIR);
+    my(@baks, $bak);
+
+    opendir(DIR, "$user_dir") || return undef;
+    @baks = sort grep {/^whiteclnt\.bak\d+/ && -f "$user_dir/$_"}
+		    readdir(DIR);
+    closedir(DIR);
+
+    return undef
+	if ($#baks < 0);
+    $bak = "$user_dir/$baks[$#baks]";
+    return undef
+	if (-M $bak >= 1);
+    return undef				# suppress taint warning
+	if ($bak !~ /(.*\/whiteclnt\.bak\d+$)/);
+    return $1;
+}
diff -r 000000000000 -r c7f6b056b673 cgi-bin/edit-whiteclnt.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/edit-whiteclnt.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,1068 @@
+#! @PERL@ -wT
+
+# display and edit a DCC whitelist file
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.58 $Revision$
+#	@configure_input@
+
+# This file must protected with an equivalent to httpd.conf lines
+#   in the README file.
+
+use strict 'subs';
+
+my($main_whiteclnt);		# path to the main whiteclnt file
+
+my(@file);			# list representation of the file
+my(%dict);			# dictionary of checksums and options
+my(%def_options);		# option settings from main whiteclnt file
+
+my($have_entry_form, $form_marked,
+   $cur_pos, $cur_key, $cur_entry, $cur_index);
+
+my $form_num = 0;
+
+
+# get DCC parameters
+local($whiteclnt,		# path to the per-user whitelist file
+      %query,
+      $hostname,
+      $thold_cks,		# checksums that can have thresholds
+      $user,
+      $edit_url,
+      $list_msg_link,
+      $edit_url, $edit_link,
+      $url_ques,
+      $sub_white,		# 'subsitute' headers from dcc_conf
+      $form_hidden,		# state for main form
+      $whiteclnt_version,	#webuser version ...
+      $whiteclnt_notify,	#webuser mail-notify=X mailbox=Y
+      $whiteclnt_notify_pat,	#regex for #webuser mail-notify=X mailbox=Y
+      $whiteclnt_lock,		#webuser (un)locked
+      $whiteclnt_change_log,	#list of dates when file was changed
+      $whiteclnt_cur_key);	#editing position in the file
+
+do('@cgibin@/common') || die("could not get DCC configuration: $!\n");
+
+
+# display the file literally
+if ($query{literal}) {
+    my($buf);
+
+    open(WHITECLNT, "< $whiteclnt") or html_whine("open($whiteclnt): $!");
+
+    print "Content-type: text/plain\n";
+    print "Expires: Thu, 01 Dec 1994 16:00:00 GMT\n";
+    print "pragma: no-cache\n\n";
+    print $buf
+	while (read(WHITECLNT, $buf, 4*1024));
+    print "\n";
+
+    close(WHITECLNT);
+    exit;
+}
+
+# lock, read and parse the whiteclnt file
+read_whiteclnt(\@file, \%dict);
+
+# get option defaults from the main whiteclnt file
+read_whitedefs(\%def_options);
+
+
+# get current position for the entry editing form
+$cur_pos = $query{pos};
+
+# find a whitecnt file entry to edit
+if ($query{key}) {
+    $cur_key = $query{key};
+} elsif ($query{auto} && $query{type} && $query{val}) {
+    my @new_entry = ck_new_white_entry("", "ok", $query{type}, $query{val});
+    $cur_key = $new_entry[0] if (defined($new_entry[1]));
+}
+$cur_entry = $dict{$cur_key} if ($cur_key);
+
+
+html_head("DCC Whitelist for $user at $hostname");
+common_buttons();
+print "<TR><TD colspan=10>return to $list_msg_link${url_ques}msg=$query{msg}\">logged message $query{msg}</A>\n"
+    if ($query{msg});
+print <<EOF;
+<TR><TD colspan=10>$edit_link${url_ques}literal=yes"
+	     TARGET="DCC literal whiteclnt">Literal contents of whitelist</A>.
+</TABLE>
+
+EOF
+
+
+# add new entry
+if ($query{Add}) {
+    my(@new_entry, $msg, $prev, $cur, $add_pos);
+
+    @new_entry = ck_new_white_entry($query{comment}, $query{count},
+				    $query{type}, $query{val});
+    give_up($new_entry[0]) if (!defined($new_entry[1]));
+
+    # insert into the file instead of appending to the end if we have
+    #	    a valid position
+    if (defined($cur_pos)) {
+	$add_pos = next_index($cur_pos);
+    } elsif ($cur_key) {
+	($prev, $cur, $add_pos) = neighbors($cur_key);
+    }
+
+    $cur_key = $new_entry[0];
+    $cur_entry = \@new_entry;
+    give_up("entry already present") if ($dict{$cur_key});
+
+    # send the new entry to the disk with the rest of the file
+    $whiteclnt_cur_key = $cur_key;
+    $msg = chg_white_entry(\@file, \%dict, $cur_key, \@new_entry, $add_pos);
+    give_up($msg) if ($msg);
+
+    # re-prime the form with cleaned comment
+    $cur_entry = $dict{$cur_key};
+    give_up("new entry did not reach file") if (!$cur_entry);
+
+    finish("whitelist entry added");
+}
+
+
+
+# change current whitelist entry
+if ($query{Change}) {
+    my(@new_entry, $msg);
+
+    give_up("no entry selected to change") if (!$cur_key);
+    give_up("entry '$cur_key' has disappeared")
+	if (!$cur_entry || !$$cur_entry[0]);
+
+    @new_entry = ck_new_white_entry($query{comment}, $query{count},
+				    $query{type}, $query{val});
+    give_up($new_entry[0]) if (!defined($new_entry[1]));
+
+    give_up("no changes requested")
+	if ($$cur_entry[1] eq $new_entry[1]
+	    && $$cur_entry[2] eq $new_entry[2]);
+
+    # send the change to the disk with the rest of the file
+    $whiteclnt_cur_key = $cur_key;
+    $msg = chg_white_entry(\@file, \%dict, $cur_key, \@new_entry);
+    give_up($msg) if ($msg);
+
+    # re-prime the form with cleaned comment
+    $cur_entry = $dict{$cur_key};
+    give_up("changed entry did not reach file") if (!$cur_entry);
+
+    finish("whitelist entry changed");
+}
+
+
+
+# delete current entry
+if ($query{Delete}) {
+    my($prev, $cur, $next, $new_key, $msg);
+
+    give_up("no entry selected to delete") if (!$cur_key);
+    give_up("entry '$cur_key' has disappeared")
+	if (!$cur_entry || !$$cur_entry[0]);
+
+    # find a neighbor of the entry to be deleted
+    ($prev, $cur, $next) = neighbors($cur_key);
+    $new_key = ${$file[$prev]}[0] if (defined($prev));
+
+    # write everything to the new file except the deleted entry
+    $whiteclnt_cur_key = $cur_key;
+    $msg = chg_white_entry(\@file, \%dict, $cur_key, undef);
+    give_up($msg) if ($msg);
+
+    # keep the add/change/delete form in place if possible
+    $cur_key = $new_key;
+    undef($cur_entry);
+    delete $query{comment};
+    delete $query{count};
+    delete $query{type};
+    delete $query{val};
+    $cur_entry = $dict{$cur_key} if ($cur_key);
+
+    finish("whitelist entry deleted");
+}
+
+
+# move the current entry up
+if ($query{Up}) {
+    up(1);
+}
+
+if ($query{Up5}) {
+    up(5);
+}
+
+sub up {
+    my($delta) = @_;
+    my($prev, $cur, $next, $moved, @new_file, $msg);
+
+    if ($cur_entry) {
+	# move an existing entry
+	while ($delta) {
+	    --$delta;
+	    # It is inefficient but easy and clearly correct to repeated
+	    #	search the array of entries for the target and then build
+	    #	a new array.
+	    ($prev, $cur, $next) = neighbors($cur_key);
+	    if (!$prev) {
+		give_up("cannot move above the top") if (!$moved);
+		last;
+	    }
+
+	    @new_file = (@file[0 .. $prev-1],
+			 $file[$cur], $file[$prev],
+			 @file[$cur+1 .. $#file]);
+	    @file = @new_file;
+	    $moved = 1;
+	}
+	$whiteclnt_cur_key = $cur_key;
+	$msg = write_whiteclnt(@file);
+	give_up($msg) if ($msg);
+	read_whiteclnt(\@file, \%dict);
+
+    } else {
+	# move a new or proposed entry
+	$cur_pos = prev_index($cur_pos, $delta);
+    }
+    print_form_file();
+}
+
+
+# move the current entry down
+if ($query{Down}) {
+    down(1);
+}
+
+if ($query{Down5}) {
+    down(5);
+}
+
+sub down {
+    my($delta) = @_;
+    my($prev, $cur, $next, @new_file, $msg);
+
+    if ($cur_entry) {
+	# move an existing entry
+	while ($delta) {
+	    --$delta;
+	    ($prev, $cur, $next) = neighbors($cur_key);
+	    if (!$next) {
+		give_up("cannot move below the bottom")  if (!$moved);
+		last;
+	    }
+
+	    @new_file = (@file[0 .. $cur-1],
+			 $file[$next], $file[$cur],
+			 @file[$next+1 .. $#file]);
+	    @file = @new_file;
+	    $moved = 1;
+	}
+	$whiteclnt_cur_key = $cur_key;
+	$msg = write_whiteclnt(@file);
+	give_up($msg) if ($msg);
+	read_whiteclnt(\@file, \%dict);
+
+    } elsif (defined($cur_pos)) {
+	# move position of a future entry
+	$cur_pos = next_index($cur_pos, $delta);
+    }
+    print_form_file();
+}
+
+
+# undo the previous change
+if ($query{Undo}) {
+    my $msg = undo_whiteclnt();
+    give_up($msg) if ($msg);
+
+    $cur_key = $whiteclnt_cur_key;
+    read_whiteclnt(\@file, \%dict);
+    $cur_key = $whiteclnt_cur_key if ($whiteclnt_cur_key);
+
+    # put the add/change/delete form back in place
+    if ($cur_key) {
+	$cur_entry = $dict{$cur_key};
+    } else {
+	undef($cur_entry);
+	delete $query{comment};
+	delete $query{count};
+	delete $query{type};
+	delete $query{val};
+    }
+
+    finish("change undone");
+}
+
+
+# change new log file mail notifcations
+my $old_notify = $whiteclnt_notify;
+if ($query{notify}) {
+    if ($query{notify} =~ /off/) {
+	$whiteclnt_notify =~ s/$whiteclnt_notify_pat$/${1}off$3$4/i;
+    } elsif ($query{notify} =~ /on/) {
+	$whiteclnt_notify =~ s/$whiteclnt_notify_pat$/${1}on$3$4/i;
+    }
+}
+if (defined($query{notifybox})) {
+    my $new_box = $query{notifybox};
+    $new_box =~ s/^\s+(.*)\s*$/$1/;
+    $whiteclnt_notify =~ s/$whiteclnt_notify_pat$/$1$2$3$new_box/i;
+
+    give_up('The notification mailbox is limited to  -, _, letters, and digits')
+	if ($whiteclnt_notify !~ /^$whiteclnt_notify_pat$/);
+}
+if ($whiteclnt_notify ne $old_notify) {
+    $whiteclnt_cur_key = "";
+    my $msg = write_whiteclnt(@file);
+    give_up($msg) if ($msg);
+    read_whiteclnt(\@file, \%dict);
+}
+
+# process requests to change options
+option_form("dccenable", "On", "dcc-on", "Off", "dcc-off");
+option_form("greyfilter", "On", "greylist-on", "Off", "greylist-off");;
+option_form("greylog", "On", "greylist-log-on", "Off", "greylist-log-off");
+option_form("mtafirst", "first", "MTA-first", "last", "MTA-last");
+option_form("rep", "On", "DCC-rep-on", "Off", "DCC-rep-off");
+option_form("dnsbl1", "On", "dnsbl1-on", "Off", "dnsbl1-off");
+option_form("dnsbl2", "On", "dnsbl2-on", "Off", "dnsbl2-off");
+option_form("dnsbl3", "On", "dnsbl3-on", "Off", "dnsbl3-off");
+option_form("logall", "On", "log-all", "Off", "log-normal");
+option_form("logsubdir", "day", "log-subdirectory-day",
+	    "hour", "log-subdirectory-hour",
+	    "minute", "log-subdirectory-minute");
+option_form("discardok", "discard spam", "forced-discard-ok",
+	    "delay mail", "no-forced-discard");
+
+# process requests from the HTTP client to change the threshold
+foreach my $ck (split(/,/, $thold_cks)) {
+    my $nm = "thold-$ck";
+    foreach my $val ($query{$nm}, $query{"text-$nm"}) {
+	next if (!$val);
+	if ($val =~ /^Default/) {
+	    set_option($nm);
+	} elsif (!parse_thold_value($ck, $val)) {
+	    give_up("invalid threshold setting $nm='$val'");
+	} else {
+	    set_option($nm, "option threshold $ck,$val\n");
+	}
+	last;
+    }
+}
+
+# nothing to do?
+give_up("entry '$cur_key' has disappeared")
+    if (!$query{auto} && $cur_key && (!$cur_entry || !$$cur_entry[0]));
+print_form_file($query{result}
+		? "<P class=warn>$query{result}</STRONG>\n"
+		: "");
+
+
+
+
+#############################################################################
+
+# display the whiteclnt file with the option setting form and and quit
+sub print_form_file {
+    my($result) = @_;			# "" or some kind of error message
+
+    close(WHITECLNT);
+
+    my $locked = ($whiteclnt_lock =~ /\blocked/) ? " disabled" : "";
+
+    # display any error message from the previous action
+    print $result ? $result : "<P>&nbsp;\n";
+
+    # generate table of forms to control option lines
+    print "<P>\n<TABLE border=0>\n";
+
+    print_form_start("<TR><TD>", "", "");
+    print "\t";
+    undo_form($locked);
+    print "\t</FORM>\n";
+
+    # two HTML forms for the '#webuser...' line
+    $whiteclnt_notify =~ /$whiteclnt_notify_pat/;
+    my $notify_cur = $2;
+    my $notifybox = $4;
+    my $notify_on_locked = ($notify_cur eq "on") ? " disabled" : $locked;
+    my $notify_off_locked = ($notify_cur eq "off") ? " disabled" : $locked;
+    print_form_start("<TR><TD class=first>", "", "");
+    print <<EOF;
+	mail notifications to
+	<INPUT $notify_off_locked type=text name=notifybox value='$notifybox' size=12>
+	<STRONG>$notify_cur</STRONG>
+	</FORM>
+EOF
+    print_form_start("    <TD>", "", "");
+    print_button("\t", "notify", $notify_on_locked, "on");
+    print_button("\t", "notify", $notify_off_locked, "off");
+    print "\t</FORM>\n";
+
+    table_row_form("dccenable", "DCC", $locked, "dcc-off", "dcc-on");
+    if ($DCCM_ARGS =~ /-G/ || $DCCIFD_ARGS =~ /-G/
+	|| (defined($GREY_CLIENT_ARGS) && $GREY_CLIENT_ARGS ne "")) {
+	table_row_form("greyfilter", "greylist filter", $locked,
+		       "greylist-off", "greylist-on");
+	table_row_form("greylog", "greylist log", $locked,
+		       "greylist-log-off", "greylist-log-on");
+    }
+    table_row_form("mtafirst", "check MTA blacklist", $locked,
+		   "MTA-last", "MTA-first", "last", "first");
+    # ask about DNSBLs if they are available
+    my $args = "";
+    $args = "$DNSBL_ARGS" if (defined($DNSBL_ARGS));
+    $args .= " $DCCM_ARGS" if (defined($DCCM_ARGS));
+    $args .= " $DCCIFD_ARGS" if (defined($DCCIFD_ARGS));
+    if ($args =~ /-B/) {
+	if ($args !~ /-B\s*set:group=\d+/i) {
+	    # only one question if there are no groups
+	    table_row_form("dnsbl1", "DNS blacklist checking", $locked);
+	} else {
+	    table_row_form("dnsbl1", "DNS blacklist #1 checking", $locked);
+	    table_row_form("dnsbl2", "DNS blacklist #2 checking", $locked);
+	    table_row_form("dnsbl3", "DNS blacklist #3 checking", $locked);
+	}
+    }
+    table_row_form("logall", "debug logging", $locked,
+		   "log-normal", "log-all");
+    table_row_form("discardok",
+		   "<STRONG></STRONG> also addressed to others",
+		   $locked, "no-forced-discard", "forced-discard-ok",
+		   "delay mail", "discard spam");
+
+    # forms for checksum thresholds
+    foreach my $ck (split(/,/, $thold_cks)) {
+	my($cur_val, $sw_val, $nm, $def_label, $bydef,
+	   $dis_field, $dis_def, $dis_never);
+
+	$nm = "thold-" . $ck;
+	# construct label for the default button from default value
+	$def_label = $def_options{$nm};
+	$def_label =~ s/.*<STRONG>([^<]+)<.*/Default ($1)/;
+	if (defined($dict{$nm})) {
+	    $cur_val = $dict{$nm}[2];
+	    $cur_val =~ s/.*,([-_a-z0-9%]+)\s+$/$1/i;
+	    $bydef = '';
+	    $sw_val = $cur_val;
+	} else {
+	    $cur_val = $def_options{$nm};
+	    $cur_val =~ s@<STRONG>(.*)</STRONG>(.*)@$1@;
+	    $bydef = $2;
+	    $sw_val = 'Default';
+	}
+	$dis_field = $locked;
+	$dis_def = $locked;
+	$dis_never = $locked;
+	$dis_def = " class=selected disabled" if ($sw_val eq "Default");
+	$dis_never = " class=selected disabled" if ($sw_val eq "Never");
+	# changing reputation thresholds ought to affect tagging
+	#	even if reputation checking is turned off
+
+	print_form_start("<TR><TD class=first>", "", "");
+	print <<EOF;
+	<EM>$ck</EM> threshold$bydef
+	<INPUT type=text$dis_field name='text-$nm' value='$cur_val' size=5>
+	</FORM>
+EOF
+	print_form_start("    <TD>", "", "");
+	print_button("\t", $nm, $dis_def, $def_label);
+	print_button("\t", $nm, $dis_never, "Never");
+	# "many" makes no sense for either reputation threshold
+	print_button("\t", $nm,
+		     $sw_val eq "many" ? " disabled" : $locked,
+		     "many")
+	    if ($ck !~ /^rep/i);
+	print "\t</FORM>\n";
+    }
+
+    print "</TABLE>\n\n<P>\n<HR>\n";
+
+    # display a form for a new entry before the file if we have not
+    #	been given a position or an entry to modify
+    print_entry_form($locked) if (!$cur_key && !defined($cur_pos));
+
+    print_whiteclnt_file($result, $locked);
+}
+
+
+
+# display the common start of forms
+sub print_form_start {
+    my($before,			# HTML before start of form
+       $tag,			# tag on action
+       $after			# HTML after start of form
+       ) = @_;
+
+    print $before if ($before);
+    print "<FORM class=nopad ACTION='$edit_url";
+    print $tag if ($tag);
+    print "' method=POST>$form_hidden\n";
+    print $after if ($after);
+    print "\t<INPUT type=hidden name=msg value='$query{msg}'>\n"
+	if ($query{msg});
+    if ($cur_key) {
+	print "\t<INPUT type=hidden name=key value='";
+	print html_str_encode($cur_key);
+	print "'>\n";
+    }
+    if ($cur_pos) {
+	print "\t<INPUT type=hidden name=pos value='";
+	print html_str_encode($cur_pos);
+	print "'>\n";
+    }
+}
+
+
+
+sub undo_form {
+    my($locked) = @_;
+
+    print "<INPUT class=small";
+    print newest_whiteclnt_bak() ? $locked : " disabled";
+    print " type=submit name='Undo' value='Undo Previous Change'>\n";
+}
+
+
+
+# display the entry editing form
+sub print_entry_form {
+    my($locked, $result) = @_;
+    my($add_str, $new_val, $comment, $change_ok, $prev, $cur, $next);
+
+    return if ($have_entry_form);
+    $have_entry_form = 1;
+
+    # prime the form with the currently selected whiteclnt entry, if any
+    if ($cur_entry) {
+	$comment = $$cur_entry[1];
+	$query{comment} = html_str_encode($comment);
+
+	my $value = $$cur_entry[2];
+	$value =~ s/(\S+)\s+//;
+	$query{count} = $1;
+	($query{type}, $query{val}) = parse_type_value($value);
+	$change_ok = $locked;
+    } else {
+	# "disabled" does not work with Netscape 4.*, but we have to handle
+	#   changes without a valid key, so don't worry about it
+	$change_ok = " disabled";
+    }
+
+    # compute a comment if this came from a log file
+    if ($query{auto} && !$cur_entry) {
+	$comment = " \n#";
+	$comment .= "    added from logged message $query{msg}"
+	    if ($query{msg});
+	$comment .= strftime(" %x", localtime);
+	$comment = html_str_encode($comment);
+	$query{count} = "OK";
+    } else {
+	$comment = $query{comment};
+	if (!$comment) {
+	    $comment = "";
+	} else {
+	    $comment =~ s/\s+$//mg;
+	    # need a blank on a leading blank line to preserve it in Mozilla
+	    $comment =~ s/^\n/ \n/;
+	}
+    }
+
+    if (!$form_marked) {
+	print "<A NAME='cur_key'></A>";
+	$form_marked = 1;
+    }
+    print_form_start("", "#cur_key", "<TABLE border=0>\n<TR><TD>&nbsp;");
+    print "    <TD>";
+    undo_form($locked);
+    print_button("\t", "Add", $locked, "Add");
+    if (defined($cur_pos)) {
+	$prev = prev_index($cur_pos);
+	$next = next_index($cur_pos);
+    } elsif ($cur_key) {
+	($prev, $cur, $next) = neighbors($cur_key);
+    } else {
+	undef($prev);
+	undef($next);
+    }
+    print_button("\t", "Up", !defined($prev) ? " disabled" : $locked, "Up");
+    print_button("\t", "Up5", !defined($prev) ? " disabled" : $locked, "Up 5");
+    print_button("\t", "Down", !$next ? " disabled" : $locked, "Down");
+    print_button("\t", "Down5", !$next ? " disabled" : $locked, "Down 5");
+    if ($query{auto} && !$cur_entry) {
+	print "\tfrom $list_msg_link${url_ques}msg=$query{msg}\">logged message $query{msg}</A>\n";
+    } else {
+	print_button("\t", "Change", $change_ok, "Change");
+	print_button("\t", "Delete", $change_ok, "Delete");
+	print "whitelist entry\n";
+    }
+    print <<EOF;
+<TR><TD>Description
+    <TD><TEXTAREA$locked name=comment rows=3 cols=70>$comment</TEXTAREA>
+<TR><TD>&nbsp;
+    <TD><SELECT class=small$locked name=count>
+EOF
+    $query{count} = "OK" if (!$query{count});
+    print_option("count", "OK");
+    print_option("count", "OK2");
+    print_option("count", "many");
+    print "\t</SELECT>\n";
+
+    print "\t<SELECT class=small$locked name=type>\n";
+    $query{type} = "env_From" if (!$query{type});
+    print_option("type", "env_From");
+    print_option("type", "env_To");
+    print_option("type", "From");
+    print_option("type", "IP");
+    print_option("type", "Message-ID");
+    # allow selection of checksums specified with -S in /var/dcc/dcc_conf
+    foreach my $hdr (split(/[|)(]+/, $sub_white)) {
+	my($label);
+	$hdr =~ s/\\s\+/ /;
+	next if ($hdr =~ /^s*$/);
+	$label = $hdr;
+	$label =~ s/^substitute\s+//i;
+	print_option("type", $label, $hdr);
+    }
+    print_option("type", "Hex Body");
+    print_option("type", "Hex Fuz1");
+    print_option("type", "Hex Fuz2");
+    print "\t</SELECT>\n";
+
+    print "\t<INPUT  type=text name=val size=40";
+    if ($query{val}) {
+	print " value='";
+	print html_str_encode($query{val});
+	print "'";
+    }
+    print ">\n";
+
+    print "<TR><TD colspan=10>return to $list_msg_link${url_ques}msg=$query{msg}\">logged message $query{msg}</A>\n"
+	if ($query{msg});
+
+    print "<TR><TD colspan=10>\n";
+    print $result ? $result : "&nbsp;";
+    print "</TABLE>\n</FORM>\n\n";
+}
+
+
+
+# find indeces of previous, current, and next entries
+#   return a list of 3 entries of the preceding, current, and following indeces
+sub neighbors {
+    my($tgt_key) = @_;
+    my($prev, $cur, $index, $entry);
+
+    # look for the current entry while tracking predecessors
+    $index = 0;
+    foreach $entry (@file) {
+	next if (!ref($entry));
+
+	# ignore deleted lines, options, and include lines
+	next if (!$$entry[0] || !defined($$entry[1])
+		 || $$entry[2] =~ /^option/);
+
+	# stop at the first entry when there is no current position
+	return ($prev, $cur, $index) if (!$tgt_key);
+
+	if ($$entry[0] eq $tgt_key) {
+	    $cur = $index;
+	    last;
+	}
+	$prev = $index;
+    } continue { ++$index; }
+
+    do {
+	return ($prev, $cur, undef) if ($index >= $#file);
+	$entry = $file[++$index];
+    } while (!$$entry[0] || !defined($$entry[1]) || $$entry[2] =~ /^option/);
+    return ($prev, $cur, $index);
+}
+
+
+
+sub prev_index {
+    my($pos, $delta) = @_;
+    my ($entry);
+
+    $pos = $#file if (!$pos);
+
+    while (--$pos >= 0) {
+	$entry = $file[$pos];
+	# skip deleted entries
+	return $pos
+	    if ($$entry[0] && defined($$entry[1]) && $$entry[2] !~ /^option/
+		&& (!$delta || !--$delta));
+    }
+    return undef;
+}
+
+
+
+sub next_index {
+    my($pos, $delta) = @_;
+    my ($entry);
+
+    $pos = $#file if (!$pos);
+
+    while (++$pos <= $#file) {
+	$entry = $file[$pos];
+	# skip deleted entries
+	return $pos
+	    if ($$entry[0] && defined($$entry[1]) && $$entry[2] !~ /^option/
+		&& (!$delta || !--$delta));
+    }
+    return undef;
+}
+
+
+
+sub set_option {
+    my($key, $line) = @_;
+    my($msg);
+
+    # put the new value, if any, into the spare slot created when the file
+    #	was read into memory
+    $file[1] = ["", "", $line] if ($line);
+
+    # delete the old value if any
+    $whiteclnt_cur_key = "";
+
+    $msg = chg_white_entry(\@file, \%dict, $key);
+    give_up($msg) if ($msg);
+}
+
+
+
+# see if an form for an option was selected and process the result if so
+#	The first arg is the name of the option.  It is followed by
+#	(form-value,file-value) pairs
+sub option_form {
+    my($key, $new_formval, $formval, $fileval);
+
+    $key = shift @_;
+    $new_formval = $query{$key};
+    return if (!$new_formval);
+
+    if ($new_formval =~ /^Default/) {
+	set_option("$key");
+	return;
+    }
+    while ($#_ > 0) {
+	$formval = shift @_;
+	$fileval = shift @_;
+	if ($new_formval eq $formval) {
+	    set_option("$key", "option $fileval\n");
+	    return;
+	}
+    }
+    give_up("invalid setting $key='$new_formval'");
+}
+
+
+
+sub finish {
+    print_form_file("<P><STRONG>" . html_str_encode($_[0]) . "</STRONG>\n");
+}
+
+
+
+sub give_up {
+    print_form_file("<P class=warn><STRONG>"
+		    . html_str_encode($_[0]) . "</STRONG>\n");
+}
+
+
+
+# You cannot use real HTML 4 buttons because Microsoft gets them all wrong.
+#   Contrary to the standard, they return all type=submit buttons.
+#	They also return any text label instead of the value, thereby removing
+#	most or all reason to use <BUTTON> instead of <INPUT>.
+sub print_button {
+    my($lead,				# HTML text before the control
+       $nm,				# control name
+       $lock,				# "" or " disabled"
+       $val) = @_;			# value when selected
+
+    $lock = " class=small$lock" if ($lock !~ /class=/i);
+    print $lead;
+    print "<INPUT $lock type=submit name='$nm' value='$val'>\n";
+}
+
+
+
+# one line of the table of forms
+sub table_row_form {
+    my($nm,				# name of the option
+       $label,				# label; <STRONG></STRONG> gets current
+       $locked,				# "" or "disabled" when file read-only
+       $off, $on,			# replace "off" and "on" in the file
+       $off_label, $on_label,		# "off" & "on" for user
+       ) = @_;
+    my($button_cur, $dis_on, $dis_off, $dis_def, $label_cur,
+       $val_cur, $bydef);
+
+
+    $off = "$nm-off" if (!$off);
+    $on = "$nm-on" if (!$on);
+    $dis_on = $locked;
+    $dis_off = $locked;
+    $dis_def = $locked;
+    $button_cur = $locked ? $locked : " class=selected disabled";
+    if ($dict{$nm}
+	&& $dict{$nm}[2] eq "option $on\n") {
+	$label_cur = $on_label ? $on_label : "<STRONG>on</STRONG>";
+	$val_cur = $label_cur;
+	$bydef = "";
+	$dis_on = $button_cur;
+    } elsif ($dict{$nm}
+	     && $dict{$nm}[2] eq "option $off\n") {
+	$label_cur = $off_label ? $off_label : "<STRONG>off</STRONG>";
+	$val_cur = $label_cur;
+	$bydef = "";
+	$dis_off = $button_cur;
+    } else {
+	$label_cur = $def_options{$nm};
+	$val_cur = $label_cur;
+	$val_cur =~ s@(<STRONG>.*</STRONG>)(.*)@$1@;
+	$bydef = $2;
+	$dis_def = $button_cur;
+    }
+    # construct labels for "on" and "off" buttons
+    if ($on_label) {
+	$on_label =~ s/.*<STRONG>([^<]+)<.*/$1/;
+    } else {
+	$on_label = "On";
+    }
+    if ($off_label) {
+	$off_label =~ s/.*<STRONG>([^<]+)<.*/$1/;
+    } else {
+	$off_label = "Off";
+    }
+    # construct label for the default button from default value
+    $def_label = $def_options{$nm};
+    $def_label =~ s/.*<STRONG>([^<]+)<.*/Default ($1)/;
+    # construct label for the group of buttons
+    #	use it as a pattern if the provided label contains "<STRONG></STRONG>",
+    if ($label !~ s@<STRONG></STRONG>(.*)@<STRONG>$val_cur</STRONG>$1$bydef@) {
+	$label .= " $label_cur";
+    }
+
+    print "<TR><TD class=first>$label\n";
+    print_form_start("    <TD>", "", "");
+    print_button("\t", $nm, $dis_def, $def_label);
+    print_button("\t", $nm, $dis_on, $on_label);
+    print_button("\t", $nm, $dis_off, $off_label);
+    print "\t</FORM>\n";
+}
+
+
+
+sub print_str {
+    my($lineno, $leader, $str) = @_;
+
+    while ($str =~ s/(.*\n?)// && $1) {
+	my $line = $1;
+	if ($line =~ /\n/) {
+	    ++$lineno;
+	} else {
+	    $line .= "\n";
+	    $leader .= "? ";
+	}
+	print $lineno if ($query{debug});
+	print $leader;
+	print $line;
+    }
+    return $lineno;
+}
+
+
+
+sub print_option {
+    my($field, $label, $value) = @_;
+    my($s);
+
+    $s = "";
+    if ($query{$field}) {
+	if ($value && $query{$field} =~ /^$value$/i) {
+	    $s =  " selected"
+	} elsif ($query{$field} =~ /^$label$/i) {
+	    $s =  " selected";
+	}
+    }
+    if ($value) {
+	$value = " value=\"$value\"";
+    } else {
+	$value = "";
+    }
+    print "\t    <OPTION class=small$s$value>$label</OPTION>\n";
+}
+
+
+
+# display the current contents of the whiteclnt file
+#   It is represented as an array or list of references to 3-tuples.
+#   The first of the three is the whitelist entry in a canonical form
+#	as a key uniquely identifying the entry.
+#   The second is a comment string of zero or more comment lines.
+#   The third is the DCC whiteclnt entry.
+#
+#   The canonical form and the whiteclnt line of the first 3-tuple for a file
+#   are null, because it contains the comments, if any, before the file's
+#   preamble of dans when the file has been changed and flags.
+#   The file[1] is an empty slot for adding option settings.
+#   The last triple in a file may also lack a whitelist entry.
+
+sub print_whiteclnt_file {
+    my($result, $locked) = @_;
+    my($preamble, $str, $url, $entry, $lineno, $in_pre, $leader, $end_select,
+       $tgt_key, $prev_key);
+
+    $url = $edit_link . $url_ques;
+    $url .= "msg=" . $query{msg} . "&amp;" if ($query{msg});
+    $url .= "key=";
+
+    $tgt_key = defined($cur_pos) ? ${$file[$cur_pos]}[0] : $cur_key;
+
+    # try to find an entry before the current entry to start the display
+    #	in the browser's window
+    if ($tgt_key) {
+	my @prev_keys;
+
+	foreach $entry (@file) {
+	    # ignore deleted lines, options, and include lines
+	    next if (!$$entry[0] || !defined($$entry[1])
+		     || $$entry[2] =~ /^option/);
+	    shift(@prev_keys) if ($#prev_keys >= 2);
+	    push(@prev_keys, $$entry[0]);
+	    last if ($$entry[0] eq $tgt_key);
+	}
+	$prev_key = shift(@prev_keys);
+    }
+
+    $lineno = 1;
+    foreach $entry (@file) {
+	# do not list deleted entries
+	next if (!defined($$entry[1]));
+
+	# no options if not debugging
+	next if ($$entry[2] =~ /^option/ && !$query{debug});
+
+	# tell the browser that the form will be soon
+	if ($prev_key && $$entry[0] && $$entry[0] eq $prev_key) {
+	    print "<A NAME='cur_key'></A>";
+	    $form_marked = 1;
+	}
+
+	# mark the currently selected entry
+	if ($tgt_key && $$entry[0] && $$entry[0] eq $tgt_key) {
+	    print "<STRONG>";
+	    $leader = " &brvbar;\t";
+	    $end_select = 1;
+	} else {
+	    $leader = "\t";
+	    $end_select = undef;
+	}
+
+	if ($query{debug}) {
+	    if ($in_pre) {
+		$in_pre = undef;
+		print "</PRE>";
+	    }
+	    print "<HR>" if ($query{debug});
+	}
+	if (!$in_pre) {
+	    $in_pre = 1;
+	    print "<PRE class=nopad>";
+	}
+
+	# display comment lines
+	$str = $$entry[1];
+	if (!$preamble) {
+	    # Display the preamble parameters after comments in first triple
+	    #	but before the ultimate blank line in the comments, if present.
+	    $preamble = $whiteclnt_version;
+	    $preamble .= $whiteclnt_notify;
+	    $preamble .= $whiteclnt_lock;
+	    $preamble .= "#webuser cur_key $whiteclnt_cur_key\n"
+		if ($whiteclnt_cur_key);
+	    $preamble .= $whiteclnt_change_log;
+	    if ($query{debug}) {
+		$str .= $preamble if ($str !~ s/(\n?)\n$/\n$preamble$1/);
+	    }
+	}
+	$lineno = print_str($lineno, $leader, html_str_encode($str));
+
+	$str = $$entry[2];
+	if ($$entry[0] && $$entry[2] !~ /^option/) {
+	    # Display an ordinary entry as a link for editing.
+	    chomp($str);
+	    # Suppress "substitute" noise
+	    $str =~ s/^(\S*\s+)substitute\s+/$1/;
+	    # use tab for blanks between the type and value
+	    $str =~ s/^(\S+)\s+(\S+)\s+/$1\t$2\t/;
+	    # make columns
+	    $str =~ s/^(\S+\s+\S{1,7})\t/$1\t\t/;
+	    $str = $url . url_encode($$entry[0]) . "#cur_key\">"
+		    . html_str_encode($str)  . "</A>\n";
+	} else {
+	    # just display option lines
+	    $str = html_str_encode($str);
+	}
+	$lineno = print_str($lineno, $leader, $str);
+
+	# put the editing form after the selected entry
+	if ($end_select) {
+	    print "</STRONG></PRE>\n";
+	    $in_pre = undef;
+	    print_entry_form($locked, $result);
+	}
+    }
+    print "</PRE>\n" if ($in_pre);
+
+    print_entry_form($locked, $result) if (!$have_entry_form);
+
+    close(WHITECLNT);
+
+    html_footer();
+    print "</BODY>\n</HTML>\n";
+
+    exit;
+}
diff -r 000000000000 -r c7f6b056b673 cgi-bin/footer
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/footer	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,14 @@
+<!-- common HTML footing used near the bottom of the web pages
+
+	This file can appear in the cgi directory or a per-user directory -->
+
+<P>
+<HR>
+<ADDRESS>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG class=logo ALT="DCC logo"
+	SRC="http://logos.dcc-servers.net/border.png">
+    </A>
+</ADDRESS>
+
+<!-- common footing HTML   Rhyolite Software DCC 1.3.103-1.5 $Revision$ -->
diff -r 000000000000 -r c7f6b056b673 cgi-bin/header
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/header	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,3 @@
+<!-- common HTML heading used near the top of the web pages
+
+	This file can appear in the cgi directory or a per-user directory -->
diff -r 000000000000 -r c7f6b056b673 cgi-bin/http2https
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/http2https	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,101 @@
+#! /bin/sh
+
+# Redirect HTTP URLs to equivalent HTTPs when that seems to be the problem.
+#   Turn on this kludge with
+#	ErrorDocument 403 /cgi-bin/http2https
+#   after installing this script in your CGI-bin directory.
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.6 $Revision$
+
+
+# This script is only intended for 403 errors
+if test "$REDIRECT_STATUS" != "403"; then
+    cat <<EOF
+Status: 500 Internal Server Error
+Content-type: text/html
+
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<HTML><HEAD>
+<TITLE>500 Internal Server Error</TITLE>
+</HEAD><BODY>
+<H1>Internal Server Error</H1>
+Invalid use of http2https for $REDIRECT_STATUS.
+<P><HR>
+<ADDRESS>$SERVER_SIGNATURE</ADDRESS>
+</BODY></HTML>
+EOF
+    exit
+fi
+
+
+# if things seem to be happening with SSL, pretend we're not here
+if test "$REDIRECT_HTTPS" = "on"; then
+    if test "$REDIRECT_ERROR_NOTES" = ""; then
+	REDIRECT_ERROR_NOTES="You don't have permission to access $REQUEST_URI on this server."
+    fi
+    cat <<EOF
+Status: 403 Forbidden
+Content-type: text/html
+
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<HTML><HEAD>
+<TITLE>403 Forbidden</TITLE>
+</HEAD><BODY>
+<H1>Forbidden</H1>
+$REDIRECT_ERROR_NOTES
+<P><HR>
+<ADDRESS>$SERVER_SIGNATURE</ADDRESS>
+</BODY></HTML>
+EOF
+    exit
+fi
+
+
+cat <<EOF
+Status: 301 Moved Permanently
+Location: https://$SERVER_NAME$REQUEST_URI
+Content-type: text/html
+
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<HTML><HEAD>
+<TITLE>HTTPS Required</TITLE>
+</HEAD><BODY>
+<H1>HTTPS Required</H1>
+You must use HTTPS to access $REQUEST_URI on this server.
+<P><HR>
+<ADDRESS>$SERVER_SIGNATURE</ADDRESS>
+</BODY></HTML>
+EOF
diff -r 000000000000 -r c7f6b056b673 cgi-bin/list-log.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/list-log.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,201 @@
+#! @PERL@ -wT
+
+# List the messages in a user's log directory
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.25 $Revision$
+#	@configure_input@
+
+# This file must protected with an equivalent to httpd.conf lines
+#   in the README file.
+
+
+use strict 'subs';
+
+use POSIX qw(strftime);
+
+
+# get DCC parameters
+local($user_dir,
+      $list_msg_link,
+      $msg_day_first, $msg_day_last,
+      $msg_first, $msg_last, $msg_newer,
+      $msg_part_num, @msgs_num,
+      %msgs_date, %msgs_from,
+      %msgs_result, %msgs_subject);
+do('@cgibin@/common') || die("could not get DCC configuration: $!\n");
+
+get_log_msgs($query{msg} ? $query{msg} : undef, 20, 1);
+
+html_head("Messages Logged for $user at $hostname");
+
+print "<H3>";
+print $#msgs_num >= 0 ? ($#msgs_num+1) : "No";
+print " messages logged for <EM>$user</EM> at $hostname at ";
+print strftime "%x %X", localtime;
+print "</H3>\n<P>\n<P class=warn><STRONG>\n";
+print $query{result} ? html_str_encode($query{result}) : "&nbsp;";
+print "</STRONG>\n<P>\n";
+common_buttons();
+
+print "</TABLE>\n";
+
+if ($#msgs_num >= 0) {
+    my($msg_num, $msg, $next_date, $last_date);
+
+    print <<EOF;
+<P>
+<TABLE   border=1 cellpadding="2%" frame=void rules=rows
+    summary="messages logged for $user at $hostname">
+EOF
+
+    print_links();
+
+    for ($msg_num = $msg_first; $msg_num <= $msg_last; ++$msg_num) {
+	my($msg) = $msgs_num[$msg_num];
+
+	next if (!$msg);
+	$next_date = $msgs_date{$msg};
+	if ($next_date =~ s/(.*) .*/$1/) {
+	    if (! $last_date) {
+		print "<TR><TH>\n";
+		print "    <TH>$next_date";
+		if ($msg_part_num) {
+		    print " part $msg_part_num";
+		    $msg_part_num= 0;
+		}
+		print "\n    <TH>From\n    <TH>&nbsp;\n    <TH>Subject\n";
+	    } elsif ($last_date ne $next_date) {
+		print "<TR><TH>&nbsp\n";
+		print "    <TH>$next_date";
+		print "\n    <TH>&nbsp;\n    <TH>&nbsp;\n    <TH>&nbsp;\n";
+	    }
+	    $last_date = $next_date;
+	}
+
+	print "<TR><TD class=\"mono small\">$list_msg_link${url_ques}msg=$msg\">";
+	print $msg_num+1;
+	print <<EOF;
+</A>
+    <TD class=mono>$msgs_date{$msg}
+    <TD>$msgs_from{$msg}
+    <TD class=small>$msgs_result{$msg}
+    <TD>$msgs_subject{$msg}
+EOF
+    }
+
+    if ($msg_num > $#msgs_num) {
+	print <<EOF;
+<TR><TH>&nbsp;
+    <TH>Next Day
+    <TH>&nbsp;
+    <TH>&nbsp;
+    <TH>&nbsp;
+EOF
+    }
+
+    print_links();
+
+
+    print <<EOF;
+</TABLE>
+
+<P>
+<TABLE class=small border=0 cellspacing=0 cellpadding=0>
+<TR><TD class=strong>Grey
+    <TD>greylist embargo
+    <TD class=strong>OK-Grey
+    <TD>greylist embargo ended
+<TR><TD class=strong>DCC
+    <TD>bulk according to DCC network&nbsp;&nbsp;&nbsp;
+    <TD class=strong>OK-DCC
+    <TD>not bulk according to DCC network
+<TR><TD class=strong>Rep
+    <TD>bad DCC Reputation
+<TR><TD class=strong>MTA
+    <TD>blacklisted by mail system
+    <TD class=strong>MTA-OK
+    <TD>whitelisted by mail system
+<TR><TD class=strong>BL
+    <TD>blacklisted in whiteclnt files
+    <TD class=strong>WL
+    <TD>whitelisted in whiteclnt files
+<TR><TD class=strong>DNSBL
+    <TD>DNS blacklisted URL
+<TR><TD><I>italic</I>
+    <TD>checks off
+</TABLE>
+
+EOF
+}
+
+html_footer();
+print "</BODY>\n</HTML>\n";
+
+# re-enable mail notifications
+unlink("$user_dir/notify.marker", "$user_dir/notify.pending",
+       "$user_dir/notify.block");
+close(MARK)
+    if (open(MARK, "> $user_dir/notify.marker"));
+
+
+
+sub print_links {
+    my($msg_num);
+
+    print "<TR><TD colspan=4>\n";
+    print "    $list_log_link\">Newest</A>\n";
+    print_bounded_link($msg_day_last+1, "Next Day");
+    print_bounded_link($msg_newer,	"Newer");
+    print_bounded_link($msg_first-1,	"Older");
+    print_bounded_link($msg_day_first-1, "Previous Day");
+    print_bounded_link(0,		"Oldest");
+}
+
+
+
+sub print_bounded_link {
+    my($msg_num, $name) = @_;
+    my($val);
+
+    if ($msg_num > $#msgs_num) {
+	$val = "";
+    } elsif ($msg_num < 0) {
+	$val = "${url_ques}msg=$msgs_num[0]";
+    } else {
+	$val = "${url_ques}msg=$msgs_num[$msg_num]";
+    }
+    print "    $list_log_link$val\">$name</A>\n";
+}
diff -r 000000000000 -r c7f6b056b673 cgi-bin/list-msg.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/list-msg.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,288 @@
+#! @PERL@ -wT
+
+# Display a message in a user's log directory
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.33 $Revision$
+#	@configure_input@
+
+# This file must protected with an equivalent to httpd.conf lines
+#   in the README file.
+
+
+use strict 'subs';
+
+
+# get DCC parameters
+local(%msgs_cache, @msgs_num, $msg_first);
+do('@cgibin@/common') || die("could not get DCC configuration: $!\n");
+
+
+
+# display the message literally if asked
+if ($query{literal}) {
+    my($buf, $msg, $path);
+
+    # punt to directory listing if no message specified
+    punt2("no message specified for listing", $list_log_url)
+	if (!$query{msg});
+    punt2("bad message $query{msg} specified for listing", $list_log_url)
+	if ($query{msg} !~ /^([a-zA-Z\d][a-zA-Z\d]{5}[a-zA-Z\d.]{0,4})$/);
+    $msg = $1;
+
+    $path = msg2path($msg);
+    if (!open(MSG, $path)) {
+	html_head("Message $msg for $user");
+	print "<P class=warn>Cannot open $path: $!\n";
+	html_footer();
+	print "</BODY>\n</HTML>";
+	exit;
+    }
+    print "Content-type: text/plain\n";
+    print "Expires: Thu, 01 Dec 1994 16:00:00 GMT\n";
+    print "pragma: no-cache\n\n";
+    print $buf
+	while (read(MSG, $buf, 4*1024));
+    exit;
+}
+
+
+# find the previous and next messages
+my($msg) = $query{msg};
+get_log_msgs($msg, 0, 1);
+$msg = $msgs_num[$#msgs_num] if (!$msg && $#msgs_num >= 0);
+my($path) = msg2path($msg, "");
+
+if (!$msg || !$msgs_cache{$msg}) {
+    punt2("message $msg has disappeared", $list_log_url)
+} else {
+    my($msg_num, $older_msg, $oldest_msg, $newer_msg, $newest_msg);
+
+    $newest_msg = "";
+    if ($#msgs_num < 0) {
+	$oldest_msg = "";
+    } else {
+	$oldest_msg = $msgs_num[0];
+    }
+    if (!defined($msg_first) || $msgs_num[$msg_first] ne $msg) {
+	$msg_num = "";
+	$newer_msg = $newest_msg;
+	$older_msg = $oldest_msg;
+    } else {
+	$msg_num = "#" . ($msg_first+1);
+	if ($msg_first < $#msgs_num) {
+	    $newer_msg = $msgs_num[$msg_first+1]
+	} else {
+	    $newer_msg = $newest_msg;
+	}
+	if ($msg_first > 0) {
+	    $older_msg = $msgs_num[$msg_first-1];
+	} else {
+	    $older_msg = $oldest_msg;
+	}
+    }
+
+    $oldest_link = "$list_msg_link${url_ques}msg=$oldest_msg\">Oldest</A>";
+    $older_link = "$list_msg_link${url_ques}msg=$older_msg\">Older</A>";
+    $newer_link = "$list_msg_link${url_ques}msg=$newer_msg\">Newer</A>";
+    $newest_link = "$list_msg_link${url_ques}msg=$newest_msg\">Newest</A>";
+    html_head("Message $msg_num for $user");
+    common_buttons();
+    print <<EOF;
+<TR><TD>$newest_link
+    <TD>$newer_link
+    <TD>$older_link
+    <TD>$oldest_link
+</TABLE>
+
+<H3>Message $msg_num for $user</H3>
+<P>
+$list_msg_link${url_ques}msg=$msg&amp;literal=yes"
+    TARGET="DCC literal log file">Literal log file</A> $path
+contents for reporting or analysis.
+EOF
+}
+
+# pass a greylist triple
+if ($query{greywhite}) {
+    my($cksum, $sight);
+
+    html_whine("bogus greywhite checksum \"$query{greywhite}\"")
+	if ($query{greywhite} !~ /^([a-z0-9 ]+)$/);    
+    $cksum = $1;
+    $sight = `@libexecdir@/dccsight -G '$cksum' 2>&1`;
+    if ($sight =~ /^(Pass|Embargo Ended)\s*$/i){
+	print "\n<P>\ngreylist embargo ended";
+    } else {
+	print "\n<P class=warn>\n@libexecdir@/dccsight -G '$cksum': $sight";
+    }
+}
+
+
+
+local($msg_date, $msg_helo, $msg_env_from, @msg_env_to,
+      $msg_client_name, $msg_ip, $msg_mail_host,
+      $msg_hdrs, $msg_body, $msg_cksums);
+@error = parse_log_msg($msg);
+if (defined $error[0]) {
+    print "<P class=warn><STRONG>$error[0]:</STRONG> $error[1]\n";
+    html_footer();
+    print "</BODY>\n</HTML>\n";
+    exit;
+}
+
+
+print <<EOF;
+<P>
+<HR>
+<H4>Envelope</H4>
+<TABLE border=0 cellspacing=0 cellpadding=0>
+EOF
+
+print "<TR><TD>&nbsp;\n    <TD>$msg_date\n";
+print_envelope_link(1, "IP address", "IP", $msg_client_name, $msg_ip);
+print_envelope_link(scalar "helo" =~ /^($sub_hdrs)$/i,
+		    "HELO", "substitute HELO", "", $msg_helo);
+print_envelope_link(1, "env_From", "env_From", "", $msg_env_from);
+print_envelope_link(scalar "mail_host" =~ /^($sub_hdrs)$/i,
+		    "mail_host", "substitute mail_host", "", $msg_mail_host);
+map print_envelope_link(1, "env_To", "env_To", "", html_str_encode($_)),
+	@msg_env_to;
+
+
+# make links for whitelisting the body checksusm
+$msg_cksums =~ s/(Body|Fuz1|Fuz2):\s+(\S{8}\s\S{8}\s\S{8}\s\S{8}\s)/make_hex_link($1,$2)/eig;
+
+# make whitelisting links for greylist checksums with active embargos
+while ($msg_cksums =~ /^\s+([a-z0-9]{8}(?: [a-z0-9]{8}){3}) Embargo[\s#0-9]*$/mi) {
+    my($sum, $sight, $link, $text);
+
+    $sum = $1;
+    $sight = `@libexecdir@/dccsight -QG "$sum" 2>&1`;
+    if ($sight =~ /Embargo.*/) {
+	$link = make_greywhite_link($sum);
+	$text = "    <STRONG>select checksum to end greylist embargo of sender</STRONG>\n";
+    } else {
+	$link = "$sum<!--inactive-->";
+	$text = "    current value: " . html_str_encode($sight);
+    }
+    $msg_cksums =~ s/(^\s+)$sum(.*$)\n/$1$link$2\n$1$text/m;
+}
+
+print <<EOF;
+</TABLE>
+<P>
+<HR>
+<H4>Headers</H4>
+<PRE>
+$msg_hdrs
+</PRE>
+
+<HR>
+<H4>Body</H4>
+$msg_body
+
+<HR>
+<H4>Checksums</H4>
+<PRE>$msg_cksums</PRE>
+
+EOF
+html_footer();
+print "</BODY>\n</HTML>\n";
+
+exit;
+
+#############################################################################
+
+
+sub print_envelope_link {
+    my($enable, $label, $type, $text, $value) = @_;
+
+    # skip the row if the value is absent
+    return if (! $value);
+
+    print "<TR><TD>";
+    print html_str_encode($label) if ($label);
+    print ":&nbsp;&nbsp;\n    <TD>";
+    if (! $enable) {
+	print html_str_encode($text . $value);
+	print "\n";
+	return;
+    }
+
+    if ($text) {
+	    print $text;
+	    print "&nbsp;";
+    }
+    print $edit_link;
+    print "${url_ques}type=";
+    print url_encode($type);
+    print "&amp;val=";
+    print url_encode($value);
+    print "&amp;msg=$query{msg}" if ($query{msg});
+    print "&amp;auto=1#cur_key\">";
+    print html_str_encode($value);
+    print "</A>\n";
+}
+
+
+
+sub make_hex_link {
+    my($type, $sum) = @_;
+    my($str);
+
+    # cannot whitelist missing fuz2 place keepers
+    return "$type: $sum" if ($sum =~ /^[ 0]+$/);
+
+    $str = "$edit_link${url_ques}type=";
+    $str .= url_encode("hex $type");
+    $str .= "&amp;val=";
+    $str .= url_encode($sum);
+    $str .= "&amp;msg=$msg&amp;auto=1#cur_key\">$type: $sum</A>";
+
+    return $str;
+}
+
+
+sub make_greywhite_link {
+    my($sum) = @_;
+    my($str);
+
+    $str = "$list_msg_link${url_ques}msg=$msg&amp;greywhite=";
+    $str .= url_encode($sum);
+    $str .= "\">$sum</A>";
+
+    return $str;
+}
diff -r 000000000000 -r c7f6b056b673 cgi-bin/webuser-notify.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/webuser-notify.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,188 @@
+#! /bin/sh
+
+# send mail messages about new per-user DCC log files
+
+# This script can be run by the daily DCC cron job after it has been
+#	1. changed to contain the URL of the local DCC CGI scripts,
+#	2. changed to use a locally suitable command to send mail
+#	3. copied to the DCC libexec directory,
+#	4. and made executable.
+#   It is remotely possible that the default values in this script are
+#	suitable and that you could use this script directly by putting
+#	the following in the libexec directory:
+#	    #! /bin/sh
+#	    sh /var/dcc/cgi-bin/webuser-notify -d $*
+#
+# This script expects to be called by the standard DCC cron job to read a
+#   series of user names on stdin, each of which has a new log file.
+#   Each user name must be prefixed with the appropriate subdirectory such
+#   as "local/".  The single argument to this script must be the
+#   DCCM_USERDIRS or DCCIFD_USERDIRS_directory made into an absolute path.
+
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.11 $Revision$
+
+
+# http or https; prefer https.
+HTTPS="https"
+
+# URL of the CGI scripts
+#   you will probably need to change this
+BASE=`hostname`
+BASE=`expr "$BASE" : '.*\.\([^.]*\.[^.]*$\)' \| "$BASE"`
+BASE="www.$BASE/DCC-cgi-bin"
+
+
+SWITCH='#webuser mail-notify'
+
+USAGE="`basename $0`: [-x] -d userdirs"
+USERDIRS=
+while getopts "xd:" c; do
+    case $c in
+	x) set -x;;
+	d) USERDIRS=$OPTARG;;
+	*) ;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0 -o "$USERDIRS" = ""; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+if test ! -d "$USERDIRS"; then
+    echo "userdir $1 is not a directory" 1>&2
+    exit 1
+fi
+
+
+while read NM; do
+    # ignore users that are not configured to use the machinery
+    if test ! -f "$USERDIRS/$NM/whiteclnt"; then
+	continue;
+    fi
+
+    DIR="$USERDIRS/$NM"
+
+    UNAME=`expr "$NM" : 'local/\(.*\)' \| "$NM"`
+    OK=no
+    BOX="$UNAME"
+    eval `sed -n -e '/^[^#]/q'						\
+	-e "s/^$SWITCH=on mailbox=$/OK=yes;/p"				\
+	-e "s/^$SWITCH=on mailbox=\(.*\)/BOX=\1;OK=yes;/p"		\
+	-e "s/^$SWITCH=off.*/OK=no;/p"	$USERDIRS/$NM/whiteclnt`
+    if test "$OK" != yes; then
+	# forget it if notifications are turned off for this user
+	continue
+    fi
+
+    # Don't send a notification of messages older than this file,
+    #	 because the user has already check them through the CGI scripts.
+    MARKER=notify.marker
+
+    # Postpone notifications until this file is current to avoid sending
+    #	daily notes that the user is ignoring.
+    BLOCK=notify.block
+
+    # Send a notification when this file is current
+    PENDING=notify.pending
+
+    FND="find $DIR -follow"
+
+    if test -f $DIR/$PENDING; then
+	# Because `find -mtime -0` sometimes means  `find -mtime -1`
+	# or vice versa, use this to see if $DIR/$PENDING is finally ready.
+	rm -f $DIR/$MARKER
+	touch $DIR/$MARKER
+	if test "`$FND -name $PENDING -newer $DIR/$MARKER`" != ""; then
+	    continue;
+	fi
+	rm $DIR/$PENDING
+
+    else
+	if test -f $DIR/$MARKER; then
+	    if test "`$FND -name 'msg.*' -newer $DIR/$MARKER		\
+			| head -1`" = ""; then
+		# there are no messages the user has not seen
+		continue
+	    fi
+	fi
+	# We have at least one new message.  If we are blocked by having
+	# sent a notification within the past week, make a pending message.
+	rm -f $DIR/$MARKER
+	touch $DIR/$MARKER
+	if test "`$FND -name $BLOCK -newer $DIR/$MARKER`" != ""; then
+	    mv $DIR/$BLOCK $DIR/$PENDING
+	    continue;
+	fi
+    fi
+
+    # browsers don't tolerate '/' or '@' in usernames and passwords in URLs
+    URL_UNAME="`expr "$UNAME@" : '\([^/@]*@$\)' || true`$BASE"
+
+
+
+    ####################################################################
+    # Modify the following message to taste.
+
+    MAIL_SUBJECT="bulk mail notification for $UNAME"
+    if test "`basename @NOTIFYMAILER@`" = sendmail; then
+	HEADERS="Subject: $MAIL_SUBJECT
+Precedence: bulk
+"
+	CMD="@NOTIFYMAILER@ $BOX"
+    else
+	HEADERS=
+	CMD="@NOTIFYMAILER@ -s '$MAIL_SUBJECT' $BOX"
+    fi 
+eval $CMD <<EOF
+$HEADERS
+Recently logged bulk messages for $UNAME can be viewed at
+$HTTPS://$URL_UNAME/list-log
+Edit your whitelist at
+$HTTPS://$URL_UNAME/edit-whiteclnt
+if any of those are solicited bulk messages instead of spam
+or to stop or redirect these messages.
+
+The user name for both web pages is $UNAME
+EOF
+
+    # no more of this mail for a week
+    NWEEK=`@PERL@ -e 'use POSIX; 
+	    print strftime("%y%m%d%H%M", localtime(time()+7*24*60*60))'`
+    rm -f $DIR/$BLOCK
+    touch -t "$NWEEK" $DIR/$BLOCK
+
+done
diff -r 000000000000 -r c7f6b056b673 configure
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configure	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,4117 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+
+# --S-LICENSE--
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_default_prefix=/var/dcc
+if test -n "  --with-updatedcc_pfile=FILE of updatedcc parameters"; then
+    ac_help="$ac_help  --with-updatedcc_pfile=FILE of updatedcc parameters
+"
+fi
+if test -n "  --with-installroot=DIR    prefix DIR to installation directory paths"; then
+    ac_help="$ac_help  --with-installroot=DIR    prefix DIR to installation directory paths
+"
+fi
+if test -n "  --with-configsuffix=STR   append STR to installed configuration file names"; then
+    ac_help="$ac_help  --with-configsuffix=STR   append STR to installed configuration file names
+"
+fi
+if test "  --enable-64-bits	    compile for 64-bits on Solaris and Linux PowerPC"; then
+    ac_help="$ac_help  --enable-64-bits	    compile for 64-bits on Solaris and Linux PowerPC
+"
+fi
+if test -n ""; then
+    ac_help="$ac_help
+"
+fi
+if test -n "  --with-DCC-MD5	    use MD5 code in the DCC source"; then
+    ac_help="$ac_help  --with-DCC-MD5	    use MD5 code in the DCC source
+"
+fi
+if test -n "  --with-uid=UID	    set-UID user name for cdcc, dccproc, & dccsight"; then
+    ac_help="$ac_help  --with-uid=UID	    set-UID user name for cdcc, dccproc, & dccsight
+"
+fi
+if test "  --disable-sys-inst	    install in private directories and no set-UID"; then
+    ac_help="$ac_help  --disable-sys-inst	    install in private directories and no set-UID
+"
+fi
+if test "  --disable-server	    do not build DCC server, dccd"; then
+    ac_help="$ac_help  --disable-server	    do not build DCC server, dccd
+"
+fi
+if test "  --disable-dccifd	    do not build DCC program interface, dccifd"; then
+    ac_help="$ac_help  --disable-dccifd	    do not build DCC program interface, dccifd
+"
+fi
+if test "  --disable-dccm	    do not build DCC sendmail interface, dccm"; then
+    ac_help="$ac_help  --disable-dccm	    do not build DCC sendmail interface, dccm
+"
+fi
+if test -n "  --with-sendmail=DIR	    build dccm with sendmail milter interface in dir"; then
+    ac_help="$ac_help  --with-sendmail=DIR	    build dccm with sendmail milter interface in dir
+"
+fi
+if test -n "  --with-cgibin=DIR	    for whitelist CGI scripts [HOMEDIR/cgibin]"; then
+    ac_help="$ac_help  --with-cgibin=DIR	    for whitelist CGI scripts [HOMEDIR/cgibin]
+"
+fi
+if test -n "  --with-rundir=DIR	    for PID files and milter socket [/var/run/dcc]"; then
+    ac_help="$ac_help  --with-rundir=DIR	    for PID files and milter socket [/var/run/dcc]
+"
+fi
+if test -n "  --with-db-memory=MB	    minimum server database buffer size 32-49152 MBytes"; then
+    ac_help="$ac_help  --with-db-memory=MB	    minimum server database buffer size 32-49152 MBytes
+"
+fi
+if test -n "  --with-max-db-mem=MB	    maximum server database buffer size 32-49152 MBytes"; then
+    ac_help="$ac_help  --with-max-db-mem=MB	    maximum server database buffer size 32-49152 MBytes
+"
+fi
+if test -n "  --with-max-log-size=KB    maximum log file size; 0=no limit [32]"; then
+    ac_help="$ac_help  --with-max-log-size=KB    maximum log file size; 0=no limit [32]
+"
+fi
+if test -n ""; then
+    ac_help="$ac_help
+"
+fi
+if test "  --disable-IPv6	    no IPv6 support"; then
+    ac_help="$ac_help  --disable-IPv6	    no IPv6 support
+"
+fi
+if test -n "  --with-kludge=FILE	    include header FILE"; then
+    ac_help="$ac_help  --with-kludge=FILE	    include header FILE
+"
+fi
+if test -n "  --with-socks[=lib]	    build DCC clients with SOCKS support"; then
+    ac_help="$ac_help  --with-socks[=lib]	    build DCC clients with SOCKS support
+"
+fi
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+sitefile=
+srcdir=
+target=NONE
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+libexecdir='${exec_prefix}/libexec'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --help		    print this message
+  --no-create		    do not create output files
+  --quiet		    do not print "checking..." messages
+  --homedir=HOMEDIR	    DCC home directory [/var/dcc]
+  --bindir=DIR		    user executables [/usr/local/bin]
+  --libexecdir=DIR	    program executables [HOMEDIR/libexec]
+  --mandir=DIR		    documentation [/usr/local/man]
+EOF
+    if test -n "$ac_help"; then
+      echo "$ac_help"
+    fi
+    exit 0 ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  --homedir | -homedir | --home | -home)
+    ac_prev=homedir ;;
+  --homedir=* | -homedir=* | --home=* | -home=*)
+    # make homedir absolute to prevent problems with updatedcc
+    cwd=`pwd`
+    prefix=`echo "$ac_optarg" | sed -e "s@^[^/]@$cwd/&@"`
+    if expr "$prefix" : '/.*' >/dev/null; then :
+    else
+	{ echo "error: --could not make --homedir=$ac_optarg absolute" 1>&2; exit 1; }    fi
+    ;;
+    
+
+  -prefix | --prefix | -prefix=* | --prefix=*)
+    { echo "error: --prefix is not what you think it is.
+    Use --homedir, --bindir, --libexecdir, --mandir, --with-installroot,
+    and perhaps --disable-sys-inst" 1>&2; exit 1; }    ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=dccproc/dccproc.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "error: cannot find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "error: cannot find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$sitefile"; then
+  if test -z "$CONFIG_SITE"; then
+    if test "x$prefix" != xNONE; then
+      CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+    else
+      CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+    fi
+  fi
+else
+  CONFIG_SITE="$sitefile"
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+# pay no attention to old autoconf cache files in case the target system
+# has been upgraded
+if test -f "$cache_file"; then
+    old_cache_file=`find "$cache_file" -mtime +1 -type f -size +1c -print`
+    if test "$cache_file" = "$old_cache_file"; then
+	echo "ignore old cache $cache_file"
+	rm $cache_file
+    else
+	if grep '^ac_cv_DCC_VERSION.*1.3.103' $cache_file 2>&1 1>/dev/null; then : ;
+	else
+	    echo "ignore stray $cache_file"
+	    rm -f $cache_file
+	fi
+    fi
+fi
+ac_cv_DCC_VERSION=1.3.103
+if test -r "$cache_file"; then
+    if test "$silent" != yes; then
+	echo "loading cache $cache_file"
+    fi
+  . $cache_file
+else
+    if test "$silent" != yes; then
+	echo "creating cache $cache_file"
+    fi
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='	'
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+ac_aux_dir=
+for ac_dir in autoconf $srcdir/autoconf; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { echo "error: cannot find install-sh or install.sh in autoconf $srcdir/autoconf" 1>&2; exit 1; }
+fi
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+
+RCS_REVISION="`echo 'Rhyolite Software DCC 1.3.103-1.204' | sed -e 's/-.*//'`"
+echo "$ac_t"""$RCS_REVISION"" 1>&6
+
+
+TARGET_SYS="`uname`"
+
+
+# How were we started?
+#   Mangle $ac_configure_args to prevent duplicates from updatedcc -cwhatever
+DCC_PARMS='
+PARM_CACHE		PARM_HOMEDIR		PARM_BINDIR
+PARM_LIBEXECDIR		PARM_MANDIR		PARM_MD5
+PARM_SYS_INST		PARM_UID		PARM_SERVER
+PARM_DCCIFD		PARM_DCCM		PARM_SENDMAIL
+PARM_CGIBIN		PARM_RUNDIR		PARM_DB_MEMORY
+PARM_MAX_DB_MEM		PARM_MAX_LOG		PARM_RL_MIN_MAX
+PARM_IPV6		PARM_64BITS		PARM_KLUDGE
+PARM_SOCKS		PARM_WITH_C_WARNINGS	PARM_UPDATEDCC_PFILE
+PARM_FETCH_CMD'
+for var in $DCC_PARMS; do
+    eval $var=
+done
+var=
+for c in $ac_configure_args; do
+    if test -z "$var"; then
+	var="$c"
+	case $var in
+	-[[a-z]]*) var="-$var";;
+	esac
+    else
+	var="$var=$c"
+    fi
+
+    case $var in
+    # allow blank instead of '='
+    --homedir|--home|--bindir|--libexecdir|--mandir)
+				continue;;
+
+    # allow these but do not record them in updatedcc
+    --with-installroot*)	continue;;
+    --with-configsuffix*)	continue;;
+
+
+    # remember these for updatedcc
+    --cache-file=*)	PARM_CACHE=" $var";;
+    --homedir=*|--home=*)
+		PARM_HOMEDIR=" --homedir=`expr X$var : '.*=\(.*\)'`";;
+    --quiet|--silent|--no-create)	;;
+    --bindir=*)			PARM_BINDIR=" $var";;
+    --libexecdir=*)		PARM_LIBEXECDIR=" $var";;
+    --mandir=*)			PARM_MANDIR=" $var";;
+    --with-DCC[-_]MD5)		PARM_MD5=" $var";;
+    --without-DCC[-_]MD5)	PARM_MD5=;;
+    --disable-sys[-_]inst)	PARM_SYS_INST=" $var";;
+    --enable-sys[-_]inst)	PARM_SYS_INST=;;
+    --with-uid=*)		PARM_UID=" $var";;
+    --without-uid)		PARM_UID=;;
+    --disable-server)		PARM_SERVER=" $var";;
+    --enable-server)		PARM_SERVER=;;
+    --disable-dccifd)		PARM_DCCIFD=" $var";;
+    --enable-dccifd)		PARM_DCCIFD=;;
+    --disable-dccm)		PARM_DCCM=" $var";;
+    --enable-dccm)		PARM_DCCM=;;
+    --with*-sendmail*)		PARM_SENDMAIL=" $var";;
+    --with-cgibin=*)		PARM_CGIBIN=" $var";;
+    --without-cgibin)		PARM_CGIBIN=;;
+    --with-rundir=*)		PARM_RUNDIR=" $var";;
+    --without-rundir)		PARM_RUNDIR=;;
+    --enable-big[-_]db);;				# obsolete with 1.2.31
+    --with-db[-_]memory=*)	PARM_DB_MEMORY=" $var";;
+    --without-db[-_]memory)	PARM_DB_MEMORY=;;
+    --with-max[-_]db[-_]mem=*)	PARM_MAX_DB_MEM=" $var";;
+    --without-max[-_]db[-_]mem)	PARM_MAX_DB_MEM=;;
+    --with-max[-_]log[-_]size=*) PARM_MAX_LOG=" $var";;
+    --without-max[-_]log[-_]size) PARM_MAX_LOG=;;
+    --with-rl[-_]min[-_]max=*)	PARM_RL_MIN_MAX=" $var";;
+    --without-rl[-_]min[-_]max=*) PARM_RL_MIN_MAX=;;
+    --with-bad[-_]locks);;				# obsolete with 1.3.81
+    --without-bad[-_]locks);;				# obsolete with 1.3.81
+    --*able-IPv6)		PARM_IPV6=" $var";;
+    --*able-64[-_]bits)		PARM_64BITS=" $var";;
+    --without-socks)		PARM_SOCKS=;;
+    --with*-socks*)		PARM_SOCKS=" $var";;
+    --with-kludge=*)		PARM_KLUDGE=" $var";;
+    --without-kludge)		PARM_KLUDGE=;;
+    --with-c[-_]warnings*)	PARM_WITH_C_WARNINGS=" $var";;
+    --without-c[-_]warnings)	PARM_WITH_C_WARNINGS=;;
+    --with-updatedcc[-_]pfile=*) PARM_UPDATEDCC_PFILE=" $var";;
+    --without-updatedcc[-_]pfile) PARM_UPDATEDCC_PFILE=;;
+    --with-fetch[-_]cmd=*)	PARM_FETCH_CMD=" $var";;
+    --without-fetch[-_]cmd=*)	PARM_FETCH_CMD=;;
+    *)			echo "unofficial configure parameter: $var";;
+    esac
+    var=
+done
+eval UPDATEDCC_PARMS=\"`echo $DCC_PARMS | sed -e 's/ *PARM_/$PARM_/g'`\"
+
+
+# We cannot
+#	- use `env -` because that clears $PATH.
+#	- just set variables because CC= breaks the C compiler on Solaris.
+#	- just unset variables, because unsetting a variable that is not
+#	    set generates errors in some versions of sh or bash..
+#	- export unset variables because that sets them to null on NetBSD.
+UPDATEDCC_ENV='NOMAN DCC_OWN DCC_GRP DCC_MODE MANOWN MANGRP CFLAGS DCC_CFLAGS PTHREAD_CFLAGS LDFLAGS DCC_LDFLAGS PTHREAD_LDFLAGS LIBS PTHREAD_LIBS CC INSTALL DCCD_MAX_FLOODS DBGFLAGS DCC_UPDATEDCC_FAST'
+# This assumes that a null value any dcc ./configure environment variable
+#   is the same as not setting it.  That is made true by updatedcc even for
+#   the BSD /usr/share/mk NOMAN switch.
+if test -z "`set | grep '^NOMAN='`"; then
+    unset NOMAN
+else
+    NOMAN=no
+fi
+UPDATEDCC_ENV_SET=
+UPDATEDCC_ENV_EXPORT=
+for var in $UPDATEDCC_ENV; do
+    # escape characters that might confuse the shell
+    val=`eval echo '$'$var | sed -e 's/\([^-_/:=+,.a-zA-Z0-9]\)/\\\\\1/g'`
+    if test -n "$val"; then
+	UPDATEDCC_ENV_SET="$UPDATEDCC_ENV_SET$var=$val "
+	UPDATEDCC_ENV_EXPORT="$UPDATEDCC_ENV_EXPORT $var"
+    fi
+done
+if test -n "$UPDATEDCC_ENV_EXPORT"; then
+    UPDATEDCC_ENV_EXPORT="export$UPDATEDCC_ENV_EXPORT"
+fi
+
+# Check whether --with-updatedcc-pfile or --without-updatedcc-pfile was given.
+if test "${with_updatedcc_pfile+set}" = set; then
+  withval="$with_updatedcc_pfile"
+  :
+fi
+if test -n "$with_updatedcc_pfile" -a "$with_updatedcc_pfile" != no	\
+	-a "$with_updatedcc_pfile" != yes; then
+    UPDATEDCC_PFILE="$with_updatedcc_pfile"
+else
+    UPDATEDCC_PFILE=""
+fi
+
+
+# Check whether --with-installroot or --without-installroot was given.
+if test "${with_installroot+set}" = set; then
+  withval="$with_installroot"
+  :
+fi
+if test -n "$with_installroot" -a "$with_installroot" != no; then
+   installroot=$with_installroot
+fi
+
+# Check whether --with-configsuffix or --without-configsuffix was given.
+if test "${with_configsuffix+set}" = set; then
+  withval="$with_configsuffix"
+  :
+fi
+if test -n "$with_configsuffix" -a "$with_configsuffix" != no; then
+   configsuffix=$with_configsuffix
+fi
+
+
+# add to a shell variable without duplication
+appendvar () {
+    nm=$1
+    eval cur="\$$nm"
+    shift
+
+    # echo -n "appending '$*' to $nm='$cur' ... "
+    for v in "$*"; do
+	pat=`echo "$v" | sed -e 's/[./\]/&/g'`
+	if expr " $cur " : ".* $pat " >/dev/null; then 
+	    continue
+	else
+	    cur="$cur $v"
+	fi
+    done
+    cur=`echo "$cur" | sed -e 's/^  *//' -e 's/  *$//'`
+    eval "$nm='$cur'"
+    # eval echo "$nm=\$$nm"
+}
+
+
+# add to the start of a shell variable without duplication
+prependvar () {
+    nm=$1
+    eval cur="\$$nm"
+    shift
+
+    # echo -n "prepending '$*' to $nm='$cur' ... "
+    for v in "$*"; do
+	pat=`echo "$v" | sed -e 's/[./\]/&/g'`
+	cur=`echo " $cur " | sed -e "s/  *$pat  */ /g" -e 's/^  *//'`
+	cur="$v $cur"
+    done
+    cur=`echo "$cur" | sed -e 's/^  *//' -e 's/  *$//'`
+    eval "$nm='$cur'"
+    # eval echo "$nm=\$$nm"
+}
+
+
+saveflags () {
+    OLD_CFLAGS="$CFLAGS"
+    OLD_LDFLAGS="$LDFLAGS"
+    OLD_LIBS="$LIBS"
+}
+
+
+restoreflags () {
+    CFLAGS="$OLD_CFLAGS"
+    LDFLAGS="$OLD_LDFLAGS"
+    LIBS="$OLD_LIBS"
+}
+
+SUBDIR_DISABLED=
+
+
+# Look for the C compiler only if it is not set explicitly because the
+# autoconf system prefers gcc if gcc is present even if cc is also present
+# and is not gcc.
+DCC_CC="#CC="
+if test "$TARGET_SYS" = SunOS -a -z "$CC"; then
+    # detect the free, non-compilers shipped by Sun
+    CC_INST="`${CC-cc} -v 2>&1 | head -1`"
+    if expr X"$CC_INST" : '.*package not installed.*' >/dev/null; then
+	echo "$ac_t""$CC_INST; setting CC=gcc" 1>&6
+	CC=gcc
+	DCC_CC="CC=gcc"
+    fi
+fi
+# the cache conflicts with users alternating `CC=gcc ./configure`
+#	with `./configure`, so remove the definition of CC from the cache
+unset ac_cv_prog_CC
+OLD_CC="$CC"
+if test -z "$CC"; then
+    # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_CC" && ac_cv_prog_CC=""""
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+fi
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_prog_rejected=no
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+	ac_prog_rejected=yes
+	continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  if test -z "$CC"; then
+    case "`uname -s`" in
+    *win32* | *WIN32*)
+      # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="cl"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+ ;;
+    esac
+  fi
+  test -z "$CC" && { echo "error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+ if test "$OLD_CC" != "$CC" -a -n "$OLD_CC"; then
+    echo "$ac_t""	*** Setting CC=$CC instead of $OLD_CC ***" 1>&6
+    DCC_CC="CC=$CC"
+fi
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    # This must be in double quotes, not single quotes, because CPP may get
+  # substituted into the Makefile and "${CC-cc}" will confuse make.
+  CPP="${CC-cc} -E"
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp.
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -E -traditional-cpp"
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -nologo -E"
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+  ac_cv_prog_CPP="$CPP"
+fi
+  CPP="$ac_cv_prog_CPP"
+else
+  ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+
+# how do we run the C compiler?
+
+# Check whether --enable-64-bits or --disable-64-bits was given.
+if test "${enable_64_bits+set}" = set; then
+  enableval="$enable_64_bits"
+  :
+fi
+
+HAVE_CC_M='#do not have cc -M'
+appendvar LIBS -lm
+RANLIB="ranlib"
+HAVE_PTHREADS=
+case "$TARGET_SYS" in
+    BSD/OS)
+	appendvar PTHREAD_LDFLAGS -pthread
+	;;
+    FreeBSD)
+	if test 0`/sbin/sysctl -n kern.osreldate 2>/dev/null` -ge 602000; then
+	    # use new POSIX threads starting with 6.2
+	    # http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html
+	    appendvar PTHREAD_LIBS -lpthread
+	else
+	    appendvar PTHREAD_LDFLAGS -pthread
+	    # use libc_r on ancient versions
+	    if test -s /usr/lib/libc_r.a; then
+		appendvar PTHREAD_LIBS -lc_r
+	    fi
+	fi
+	;;
+    DragonFly)
+	appendvar PTHREAD_LDFLAGS -pthread
+	if test -s /usr/lib/libc_r.a; then
+	    # use libc_r on ancient versions
+	    appendvar PTHREAD_LIBS -lc_r
+	fi
+	;;
+    Darwin)
+	RANLIB="ranlib -c"
+	appendvar LIBS -lresolv
+	;;
+    SunOS)
+	appendvar PTHREAD_LIBS -lpthread
+	prependvar DCC_CFLAGS -D_REENTRANT -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+	prependvar CFLAGS -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+	appendvar LIBS -lsocket -lnsl -lresolv
+	# manual 64-bit versions on Solaris, because it seems to be impossible
+	# to figure out whether a 32-bit version of the milter library is used
+	if test "$enable_64_bits" != yes				\
+		    -o "`isainfo -b 2>/dev/null`" != 64; then
+	    ac_cv_have_64bits=no
+	else
+	    # see if we have a 64-bit system with a 64-bit compiler
+	    echo $ac_n "checking compiling 64-bit void*""... $ac_c" 1>&6
+echo "configure: checking compiling 64-bit void*" >&5
+if eval "test \"`echo '$''{'ac_cv_have_64bits'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  		saveflags
+		ac_cv_have_64bits=no
+		for OPT in -m64 -xarch=generic64 -xarch=v9; do
+		    CFLAGS="$OPT $OLD_CFLAGS"
+		    if test "$cross_compiling" = yes; then
+  continue
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+main()
+				{exit(sizeof(void*)==8 ? 0 : sizeof(void*));}
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_have_64bits="$OPT"; break
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  continue
+fi
+rm -fr conftest*
+fi
+
+		done
+		restoreflags
+fi
+
+echo "$ac_t""$ac_cv_have_64bits" 1>&6
+	    # if we have a 64-bit system with a 64-bit compiler, adjust
+	    # the flags and libraries so that we will later find that
+	    # sizeof(long) and sizeof(void*)=8
+	    if test -n "$ac_cv_have_64bits" -a "$ac_cv_have_64bits" != no; then
+		prependvar CFLAGS $ac_cv_have_64bits
+		prependvar DCC_CFLAGS $ac_cv_have_64bits
+		prependvar LIBS $ac_cv_have_64bits
+	    fi
+	fi
+	;;
+    HP-UX)
+	prependvar DCC_CFLAGS-D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS
+	appendvar PTHREAD_LIBS -lpthread
+	;;
+    AIX)
+	cat >> confdefs.h <<\EOF
+#define _ALL_SOURCE 1
+EOF
+	prependvar DCC_CFLAGS -D_THREAD_SAFE
+	appendvar PTHREAD_LDFLAGS -pthread
+	appendvar PTHREAD_LIBS -lpthreads
+	appendvar LIBS -lbsd -lc_r
+	;;
+    IRIX*)
+	appendvar PTHREAD_LIBS -lpthread
+	prependvar DCC_CFLAGS -woff 1185
+	prependvar CFLAGS -woff 1185
+	prependvar DCC_LDFLAGS -LD_MSG:OFF=84
+	RANLIB="@#ranlib"
+	HAVE_CC_M='HAVE_CC_M=yes'
+	;;
+    Linux)
+	appendvar LIBS -lresolv
+	appendvar PTHREAD_LDFLAGS -pthread
+	prependvar DCC_CFLAGS -D_FILE_OFFSET_BITS=64
+	prependvar CFLAGS -D_FILE_OFFSET_BITS=64
+	# generate 64-bit code for 64-bit PowerPC
+	if test "$enable_64_bits" = yes					\
+		-a -n "`uname -a | grep 'ppc64 GNU/Linux'`"; then
+	    appendvar DCC_CFLAGS -m64
+	    appendvar CFLAGS -m64
+	    appendvar DCC_LDFLAGS -m64
+	    appendvar LDFLAGS -m64
+	fi
+	;;
+    OpenBSD)
+	appendvar PTHREAD_LDFLAGS -pthread
+	;;
+    NetBSD)
+	appendvar PTHREAD_LDFLAGS -lpthread
+	appendvar PTHREAD_CFLAGS -I/usr/pkg/include
+	appendvar CPPFLAGS -I/usr/pkg/include
+	appendvar PTHREAD_LDFLAGS -L/usr/pkg/lib
+	;;
+    OSF1)
+	# OSF1 is too messy for AC_CHECK_FUNC(pthread_mutex_lock...)
+	HAVE_PTHREADS=yes
+	appendvar PTHREAD_CFLAGS -pthread -D_REENTRANT
+	appendvar PTHREAD_LDFLAGS -pthread
+	appendvar PTHREAD_LIBS -lpthread -lexc
+	HAVE_CC_M='HAVE_CC_M=yes'
+	;;
+    OpenUNIX)
+	prependvar DCC_CFLAGS -Kthread
+	appendvar PTHREAD_LIBS -Kthread
+	appendvar LIBS -lsocket -lnsl
+	RANLIB="@#ranlib"
+	;;
+    *)
+	echo "warning: 	*** unrecognized system $TARGET_SYS ***" 1>&2
+	# Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_RANLIB="ranlib"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+  echo "$ac_t""$RANLIB" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+	if test "$RANLIB" = ':'; then
+	    RANLIB="@#ranlib"
+	fi
+	appendvar PTHREAD_LDFLAGS -pthread
+	;;
+esac
+
+# decide if we have POSIX threads
+if test "$HAVE_PTHREADS" != yes; then
+    saveflags
+    CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+    LDFLAGS="$LDFLAGS $PTHREAD_LDFLAGS"
+    LIBS="$LIBS $PTHREAD_LIBS"
+    echo $ac_n "checking for pthread_mutex_lock""... $ac_c" 1>&6
+echo "configure: checking for pthread_mutex_lock" >&5
+if eval "test \"`echo '$''{'ac_cv_func_pthread_mutex_lock'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char pthread_mutex_lock(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char pthread_mutex_lock();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_pthread_mutex_lock) || defined (__stub___pthread_mutex_lock)
+choke me
+#else
+pthread_mutex_lock();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_pthread_mutex_lock=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_pthread_mutex_lock=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'pthread_mutex_lock`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  HAVE_PTHREADS=yes
+else
+  echo "$ac_t""no" 1>&6
+fi
+    restoreflags
+fi
+cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <sys/mman.h>
+MS_ASYNC
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "^MS_ASYNC$" >/dev/null 2>&1; then
+  rm -rf conftest*
+  cat >> confdefs.h <<\EOF
+#define HAVE_OLD_MSYNC 
+EOF
+
+fi
+rm -f conftest*
+if test "$HAVE_PTHREADS" = yes; then
+    cat >> confdefs.h <<\EOF
+#define HAVE_PTHREADS 1
+EOF
+
+fi
+
+echo $ac_n "checking for gcc __attribute__""... $ac_c" 1>&6
+echo "configure: checking for gcc __attribute__" >&5
+if eval "test \"`echo '$''{'ac_cv_have_gcc_attributes'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+
+int main() {
+extern void __attribute((noreturn)) ck(); ck();
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_have_gcc_attributes=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_have_gcc_attributes=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_have_gcc_attributes" 1>&6
+if test "$ac_cv_have_gcc_attributes" = yes; then
+    cat >> confdefs.h <<\EOF
+#define HAVE_GCC_ATTRIBUTES 1
+EOF
+    cat >> confdefs.h <<\EOF
+#define HAVE_GCC_INLINE 1
+EOF
+    # if we have gcc attributes, see if we have the real gcc instead of
+    #	Sun Studio compilers that use -xM for gcc -M
+    if test -n "`${CC-cc} -v 2>&1 | grep '^gcc version'`"; then
+	HAVE_CC_M='HAVE_CC_M=yes'
+    fi
+fi
+
+
+# Check whether --with-c-warnings or --without-c-warnings was given.
+if test "${with_c_warnings+set}" = set; then
+  withval="$with_c_warnings"
+  :
+fi
+CWRN1="#CWARN"
+CWRN2="#CWARN"
+CWRN3="#CWARN"
+if test "$with_c_warnings" = yes; then
+    with_c_warnings=1
+fi
+if test -z "$with_c_warnings" -o "$with_c_warnings" = no; then
+    with_c_warnings=0
+fi
+if test "$with_c_warnings" -ge 1; then
+    CWRN1="CWARN"
+fi
+if test "$with_c_warnings" -ge 2; then
+    CWRN2="CWARN"
+fi
+if test "$with_c_warnings" -ge 3; then
+    CWRN3="CWARN"
+fi
+
+# does this system have 64 bit longs, pointers and off_t?
+echo $ac_n "checking sizeof(long)""... $ac_c" 1>&6
+echo "configure: checking sizeof(long)" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_sizeof_long=0
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "ac_cv_sizeof_long=%d\n", sizeof(long));
+  fprintf(f, "ac_cv_sizeof_ptr=%d\n", sizeof((void *)0));
+  fprintf(f, "ac_cv_sizeof_off_t=%d\n", sizeof(off_t));
+  exit(0);
+}
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  . ./conftestval
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  echo "test type size program failed"
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$ac_cv_sizeof_long" 1>&6
+if test 0$ac_cv_sizeof_long -eq 8; then
+    cat >> confdefs.h <<\EOF
+#define HAVE_64BIT_LONG 1
+EOF
+fi
+if test 0$ac_cv_sizeof_ptr -eq 8; then
+    cat >> confdefs.h <<\EOF
+#define HAVE_64BIT_PTR 1
+EOF
+fi
+if test 0$ac_cv_sizeof_off_t -eq 8; then
+    cat >> confdefs.h <<\EOF
+#define HAVE_BIG_FILES 1
+EOF
+fi
+
+
+# set switches to generate the meat of a makefile
+echo $ac_n "checking for gmake""... $ac_c" 1>&6
+echo "configure: checking for gmake" >&5
+if make -v nosuchtarget 2>&1 | grep '^GNU Make' >/dev/null; then
+    echo "$ac_t""yes, make is gmake but called make" 1>&6
+    MAKE_TYPE=gmake
+    DCCMAKE=make
+else
+    if test -f /usr/share/mk/bsd.prog.mk; then
+	echo "$ac_t""no, assume 4.4BSD make" 1>&6
+	MAKE_TYPE=make
+	DCCMAKE=make
+    else
+	echo "warning: 	*** You must install and use gmake on $TARGET_SYS ***" 1>&2
+	MAKE_TYPE=gmake
+	DCCMAKE=gmake
+    fi
+fi
+DEPTH=.
+if test "$MAKE_TYPE" = gmake; then
+    MAKE_DOT=
+    MAKE_INC='#include "$(DEPTH)/Makefile.inc"'
+    MAKE_INC2='include $(DEPTH)/Makefile.inc2'
+    MAKE_PROG='include $(DEPTH)/gmake.inc'
+    MAKE_SUBDIR='include $(DEPTH)/gmake.inc'
+    MAKE_LIB='include $(DEPTH)/gmake.inc\
+include $(DEPTH)/Makefile.inc2'
+else
+    MAKE_DOT="."
+    MAKE_INC='.include "$(DEPTH)/Makefile.inc"'
+    MAKE_INC2='.include "$(DEPTH)/Makefile.inc2"'
+    MAKE_PROG='.include <bsd.prog.mk>'
+    if test "$TARGET_SYS" = 'NetBSD'; then
+	MAKE_SUBDIR='.include <bsd.subdir.mk>'
+    else
+	MAKE_SUBDIR='.include <bsd.prog.mk>'
+    fi
+    MAKE_LIB='.include <bsd.lib.mk>\
+.include "$(DEPTH)/Makefile.inc2"\
+LDADD	=			# BSD/OS 4.2 gives $(LDADD) to ar'
+fi
+
+
+# On some systems `logger` does not have -s
+case "$TARGET_SYS" in
+    SunOS|AIX|OSF1)
+	DCC_LOGGER="\$DCC_LIBEXEC/logger -p \${DCC_ERROR_LOG_FACILITY-mail.err} -t \${LOGGER_TAG-DCC}"
+		;;
+    BSD/OS|FreeBSD|DragonFly|Darwin|Linux|OpenBSD|NetBSD)
+	DCC_LOGGER="logger -s -p \${DCC_ERROR_LOG_FACILITY-mail.err} -t \${LOGGER_TAG-DCC}"
+		;;
+    *)
+	# Extract the first word of "logger", so it can be a program name with args.
+set dummy logger; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_DCC_LOGGER'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$DCC_LOGGER"; then
+  ac_cv_prog_DCC_LOGGER="$DCC_LOGGER" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_DCC_LOGGER="logger -s -p \${DCC_ERROR_LOG_FACILITY-mail.err} -t \${LOGGER_TAG-DCC}"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_DCC_LOGGER" && ac_cv_prog_DCC_LOGGER="echo 1>&2"
+fi
+fi
+DCC_LOGGER="$ac_cv_prog_DCC_LOGGER"
+if test -n "$DCC_LOGGER"; then
+  echo "$ac_t""$DCC_LOGGER" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    ;;
+esac
+
+# Deal xargs that run even if there is nothing to do.
+#   GNU's version has -r but does the wrong thing with only -n
+#   Solaris does not have -r but does nothing with xargs -n1000 </dev/null
+# Get the absolute path of the command to prevent confusion on Solaris systems
+#   with confused paths.
+# Extract the first word of "xargs", so it can be a program name with args.
+set dummy xargs; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_DCC_XARGS'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$DCC_XARGS" in
+  /*)
+  ac_cv_path_DCC_XARGS="$DCC_XARGS" # Let the user override the test with a path.
+  ;;
+  ?:/*)
+  ac_cv_path_DCC_XARGS="$DCC_XARGS" # Let the user override the test with a dos path.
+  ;;
+  *)
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_DCC_XARGS="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_path_DCC_XARGS" && ac_cv_path_DCC_XARGS="xargs"
+  ;;
+esac
+fi
+DCC_XARGS="$ac_cv_path_DCC_XARGS"
+if test -n "$DCC_XARGS"; then
+  echo "$ac_t""$DCC_XARGS" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+if test -n "`$DCC_XARGS echo bad </dev/null 2>&1`"; then
+    if test -z "`$DCC_XARGS -r echo bad </dev/null 2>&1`"; then
+	DCC_XARGS="$DCC_XARGS -r"
+    else
+	if test -z "`$DCC_XARGS -n1000 echo bad </dev/null 2>&1`"; then
+	    DCC_XARGS="$DCC_XARGS -n1000"
+	fi
+    fi
+fi
+
+# How can we fetch a file specified by URL?
+#   Fall back to ftp and just hope it understands URL.
+if test -n "$with_fetch_cmd" -a "$with_fetch_cmd" != yes; then
+   FETCH_CMD="$with_fetch_cmd"
+else
+    for ac_prog in wget fetch curl
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_FETCH_CMD'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$FETCH_CMD" in
+  /*)
+  ac_cv_path_FETCH_CMD="$FETCH_CMD" # Let the user override the test with a path.
+  ;;
+  ?:/*)
+  ac_cv_path_FETCH_CMD="$FETCH_CMD" # Let the user override the test with a dos path.
+  ;;
+  *)
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_FETCH_CMD="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac
+fi
+FETCH_CMD="$ac_cv_path_FETCH_CMD"
+if test -n "$FETCH_CMD"; then
+  echo "$ac_t""$FETCH_CMD" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+test -n "$FETCH_CMD" && break
+done
+test -n "$FETCH_CMD" || FETCH_CMD="ftp"
+
+fi
+FETCH_WGET_OPTS=
+if expr "$FETCH_CMD" : '.*wget' >/dev/null 2>&1; then
+    if test -n "`wget --help </dev/null 2>&1 | grep 'referer.*URL'`"; then
+	FETCH_WGET_OPTS="$FETCH_WGET_OPTS --referer=\$HTTP_REFERER"
+    fi
+    #default 20 retries is far, far too many
+    if test -n "`wget --help </dev/null 2>&1 | grep 'tries=NUMBER'`"; then
+	FETCH_WGET_OPTS="$FETCH_WGET_OPTS --tries=1"
+    fi
+fi
+if expr "$FETCH_CMD" : '.*curl' >/dev/null 2>&1; then
+    if test -n "`curl --help </dev/null 2>&1 | grep 'referer.*URL'`"; then
+	FETCH_CURL_OPTS="--referer \$HTTP_REFERER"
+    fi
+fi
+
+# Extract the first word of "perl", so it can be a program name with args.
+set dummy perl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_PERL'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$PERL" in
+  /*)
+  ac_cv_path_PERL="$PERL" # Let the user override the test with a path.
+  ;;
+  ?:/*)
+  ac_cv_path_PERL="$PERL" # Let the user override the test with a dos path.
+  ;;
+  *)
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_PERL="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_path_PERL" && ac_cv_path_PERL="/usr/local/bin/perl"
+  ;;
+esac
+fi
+PERL="$ac_cv_path_PERL"
+if test -n "$PERL"; then
+  echo "$ac_t""$PERL" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+# for cgi-bin/webuser-notify
+# Extract the first word of "sendmail", so it can be a program name with args.
+set dummy sendmail; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_NOTIFYMAILER'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$NOTIFYMAILER" in
+  /*)
+  ac_cv_path_NOTIFYMAILER="$NOTIFYMAILER" # Let the user override the test with a path.
+  ;;
+  ?:/*)
+  ac_cv_path_NOTIFYMAILER="$NOTIFYMAILER" # Let the user override the test with a dos path.
+  ;;
+  *)
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH:/usr/sbin:/usr/lib"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_NOTIFYMAILER="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_path_NOTIFYMAILER" && ac_cv_path_NOTIFYMAILER="mail"
+  ;;
+esac
+fi
+NOTIFYMAILER="$ac_cv_path_NOTIFYMAILER"
+if test -n "$NOTIFYMAILER"; then
+  echo "$ac_t""$NOTIFYMAILER" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+# for the CGI-bin scripts
+# Extract the first word of "htpasswd", so it can be a program name with args.
+set dummy htpasswd; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_HTPASSWD'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$HTPASSWD" in
+  /*)
+  ac_cv_path_HTPASSWD="$HTPASSWD" # Let the user override the test with a path.
+  ;;
+  ?:/*)
+  ac_cv_path_HTPASSWD="$HTPASSWD" # Let the user override the test with a dos path.
+  ;;
+  *)
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_HTPASSWD="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_path_HTPASSWD" && ac_cv_path_HTPASSWD="/usr/local/bin/htpasswd"
+  ;;
+esac
+fi
+HTPASSWD="$ac_cv_path_HTPASSWD"
+if test -n "$HTPASSWD"; then
+  echo "$ac_t""$HTPASSWD" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+# for the graphs
+# Extract the first word of "rrdtool", so it can be a program name with args.
+set dummy rrdtool; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_RRDTOOL'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$RRDTOOL" in
+  /*)
+  ac_cv_path_RRDTOOL="$RRDTOOL" # Let the user override the test with a path.
+  ;;
+  ?:/*)
+  ac_cv_path_RRDTOOL="$RRDTOOL" # Let the user override the test with a dos path.
+  ;;
+  *)
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_RRDTOOL="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_path_RRDTOOL" && ac_cv_path_RRDTOOL="/usr/local/bin/rrdtool"
+  ;;
+esac
+fi
+RRDTOOL="$ac_cv_path_RRDTOOL"
+if test -n "$RRDTOOL"; then
+  echo "$ac_t""$RRDTOOL" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+# choose between `su` and `su -`
+case "$TARGET_SYS" in
+    Linux|BSD/OS|FreeBSD|NetBSD|DragonFly)
+	# `su -` is required on Linux and desirable where it works
+	# clear $TERM because some too smart by half systems spew /etc/motd
+	DCC_SU="TERM= su -";;
+    SunOS|Darwin)
+	# `su -` is known to not work or has bad side effects on some systems
+	DCC_SU="su";;
+    *)
+	# be considervative the rest of the time
+	DCC_SU="su";;
+esac
+
+# check for 4.4BSD sockets
+# look for sockets with lengths
+echo $ac_n "checking for sa_len""... $ac_c" 1>&6
+echo "configure: checking for sa_len" >&5
+if eval "test \"`echo '$''{'ac_cv_have_sa_len'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+      cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <sys/socket.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "[^_]sa_len[^_a-z0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  	ac_cv_have_sa_len=yes
+else
+  rm -rf conftest*
+  ac_cv_have_sa_len=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_have_sa_len" 1>&6
+if test "$ac_cv_have_sa_len" = yes; then
+    cat >> confdefs.h <<\EOF
+#define HAVE_SA_LEN 1
+EOF
+fi
+# many systems want recvfrom() to use a pointer to a socklen_t
+echo $ac_n "checking for socklen_t""... $ac_c" 1>&6
+echo "configure: checking for socklen_t" >&5
+if eval "test \"`echo '$''{'ac_cv_have_socklen_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+      cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <sys/socket.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "typedef.*[^_]socklen_t[^_a-z0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  	ac_cv_have_socklen_t=yes
+else
+  rm -rf conftest*
+  ac_cv_have_socklen_t=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_have_socklen_t" 1>&6
+if test "$ac_cv_have_socklen_t" = yes; then
+    cat >> confdefs.h <<\EOF
+#define HAVE_SOCKLEN_T 1
+EOF
+fi
+
+echo $ac_n "checking for AF_LOCAL""... $ac_c" 1>&6
+echo "configure: checking for AF_LOCAL" >&5
+if eval "test \"`echo '$''{'ac_cv_have_af_local'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+      cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <sys/socket.h>
+#ifdef AF_LOCAL
+ have_af_local
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "have_af_local" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_have_af_local=yes
+else
+  rm -rf conftest*
+  ac_cv_have_af_local=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_have_af_local" 1>&6
+if test "$ac_cv_have_af_local" = yes; then
+    cat >> confdefs.h <<\EOF
+#define HAVE_AF_LOCAL 1
+EOF
+fi
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  ac_cv_header_stdc=yes
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "memchr" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "free" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+  :
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  :
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+  cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for pid_t""... $ac_c" 1>&6
+echo "configure: checking for pid_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_pid_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_pid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_pid_t" 1>&6
+if test $ac_cv_type_pid_t = no; then
+  cat >> confdefs.h <<\EOF
+#define pid_t int
+EOF
+
+fi
+
+if test "$ac_cv_type_pid_t" != no; then
+    cat >> confdefs.h <<\EOF
+#define HAVE_PID_T 1
+EOF
+fi
+
+# Some systems have uint32_t, others have u_int32_t, and some have both.
+#   Then there is OpenUNIX but in sys/types.h and */bitypes.h which
+#   is included by network header files.
+echo $ac_n "checking for u_int32_t""... $ac_c" 1>&6
+echo "configure: checking for u_int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_have_u_int32_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+      cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "u_int32_t" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_have_u_int32_t=yes
+else
+  rm -rf conftest*
+  ac_cv_have_u_int32_t=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_have_u_int32_t" 1>&6
+if test "$ac_cv_have_u_int32_t" = yes; then
+    cat >> confdefs.h <<\EOF
+#define DCC_HAVE_U_INT32_T 1
+EOF
+fi
+# and then there is u_*int64_t
+echo $ac_n "checking for u_int64_t""... $ac_c" 1>&6
+echo "configure: checking for u_int64_t" >&5
+if eval "test \"`echo '$''{'ac_cv_have_u_int64_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+      cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <sys/types.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "u_int64_t" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_have_u_int64_t=yes
+else
+  rm -rf conftest*
+  ac_cv_have_u_int64_t=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_have_u_int64_t" 1>&6
+if test "$ac_cv_have_u_int64_t" = yes; then
+    cat >> confdefs.h <<\EOF
+#define DCC_HAVE_U_INT64_T 1
+EOF
+fi
+
+echo $ac_n "checking need for string.h""... $ac_c" 1>&6
+echo "configure: checking need for string.h" >&5
+if eval "test \"`echo '$''{'ac_cv_need_strings_h'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "strcasecmp" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_need_strings_h=no
+else
+  rm -rf conftest*
+  ac_cv_need_strings_h=yes
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_need_strings_h" 1>&6
+if test "$ac_cv_need_strings_h" = yes; then
+    cat >> confdefs.h <<\EOF
+#define NEED_STRINGS_H 1
+EOF
+fi
+
+# Checks for library functions and headers.
+echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6
+echo "configure: checking whether time.h and sys/time.h may both be included" >&5
+if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+int main() {
+struct tm *tp;
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_header_time=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_time=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_time" 1>&6
+if test $ac_cv_header_time = yes; then
+  cat >> confdefs.h <<\EOF
+#define TIME_WITH_SYS_TIME 1
+EOF
+
+fi
+
+for ac_hdr in strings.h paths.h sys/cdefs.h pthread.h arpa/nameser.h 	resolv.h utime.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+ 
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in vsyslog getipnodebyname getipnodebyaddr freehostent		getaddrinfo getnameinfo freeaddrinfo gai_strerror hstrerror		_res res_init res_query dn_expand					inet_ntop inet_aton gethostid						localtime_r gmtime_r timegm altzone futimes setpgid poll		strlcpy strlcat getprogname daemon siginterrupt eaccess
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+ 
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+# deal with Linux "improvements" to the BIND resolver library
+#   They #define the standard BIND names in resolv.h
+if test "$ac_cv_func__res" = no -a "$ac_cv_header_resolv_h" = yes \
+	-a "$TARGET_SYS" = Linux; then
+    for ac_func in __res_state __res_init __res_query __dn_expand
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+ 
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+    if test "$ac_cv_func___res_state" = yes; then
+	cat >> confdefs.h <<\EOF
+#define HAVE__RES 1
+EOF
+
+    fi
+    if test "$ac_cv_func___res_init" = yes; then
+	cat >> confdefs.h <<\EOF
+#define HAVE_RES_INIT 1
+EOF
+
+    fi
+    if test "$ac_cv_func___res_query" = yes; then
+	cat >> confdefs.h <<\EOF
+#define HAVE_RES_QUERY 1
+EOF
+
+    fi
+    if test "$ac_cv_func___dn_expand" = yes; then
+	cat >> confdefs.h <<\EOF
+#define HAVE_DN_EXPAND 1
+EOF
+
+    fi
+fi
+
+for ac_func in getifaddrs
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+ 
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+if test "$ac_cv_func_getifaddrs" = yes; then
+    for ac_func in freeifaddrs
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+ 
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+else
+    echo $ac_n "checking getifaddrs replacement""... $ac_c" 1>&6
+echo "configure: checking getifaddrs replacement" >&5
+if eval "test \"`echo '$''{'ac_cv_use_dcc_getifaddrs'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+      saveflags
+    CFLAGS="$CFLAGS -I include"
+    cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#define TEST_DCC_GETIFADDRS
+#include "dcclib/getifaddrs.c"
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_use_dcc_getifaddrs=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_use_dcc_getifaddrs=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_use_dcc_getifaddrs" 1>&6
+    restoreflags
+    if test "$ac_cv_use_dcc_getifaddrs" = yes; then
+	cat >> confdefs.h <<\EOF
+#define USE_DCC_GETIFADDRS 1
+EOF
+    fi
+fi
+
+# Try to use a local md5.h and md5.c.  Some systems such as RedHat 7.3
+#   have versions of /usr/include/md5.h that are not directly usable.
+#   Then there are the odd versions that are installed on systems.
+# Check whether --with-DCC-MD5 or --without-DCC-MD5 was given.
+if test "${with_DCC_MD5+set}" = set; then
+  withval="$with_DCC_MD5"
+  :
+fi
+if test "$with_DCC_MD5" = yes; then
+    unset ac_cv_header_md5_h
+    unset ac_cv_search_MD5Init
+else
+    ac_safe=`echo "md5.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for md5.h""... $ac_c" 1>&6
+echo "configure: checking for md5.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <md5.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/param.h>
+#include <md5.h>
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  echo $ac_n "checking for library containing MD5Init""... $ac_c" 1>&6
+echo "configure: checking for library containing MD5Init" >&5
+if eval "test \"`echo '$''{'ac_cv_search_MD5Init'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_func_search_save_LIBS="$LIBS"
+ac_cv_search_MD5Init="no"
+cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char MD5Init();
+
+int main() {
+MD5Init()
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  ac_cv_search_MD5Init="none required"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+test "$ac_cv_search_MD5Init" = "no" && for i in md md5; do
+LIBS="-l$i  $ac_func_search_save_LIBS"
+cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char MD5Init();
+
+int main() {
+MD5Init()
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  ac_cv_search_MD5Init="-l$i"
+break
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+done
+LIBS="$ac_func_search_save_LIBS"
+fi
+
+echo "$ac_t""$ac_cv_search_MD5Init" 1>&6
+if test "$ac_cv_search_MD5Init" != "no"; then
+  test "$ac_cv_search_MD5Init" = "none required" || LIBS="$ac_cv_search_MD5Init $LIBS"
+  cat >> confdefs.h <<\EOF
+#define HAVE_MD5 1
+EOF
+
+else :
+  ac_cv_header_md5_h=no
+fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  echo "$ac_t"" but that md5.h does not compile" 1>&6; ac_cv_header_md5_h=no
+fi
+rm -f conftest*
+else
+  echo "$ac_t""no" 1>&6
+fi
+fi
+
+# How can the database code discover the size of physical memory?
+GOT_PHYSMEM=
+# Solaris sysconf()
+if test -z "$GOT_PHYSMEM"; then
+    echo $ac_n "checking for sysconf(_SC_PHYS_PAGES)+sysconf(_SC_PAGESIZE)""... $ac_c" 1>&6
+echo "configure: checking for sysconf(_SC_PHYS_PAGES)+sysconf(_SC_PAGESIZE)" >&5
+if eval "test \"`echo '$''{'ac_cv_have_sysconf_pages'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+      cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <unistd.h>
+int main() {
+sysconf(_SC_PHYS_PAGES)+sysconf(_SC_PAGESIZE)
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_have_sysconf_pages=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_have_sysconf_pages=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_have_sysconf_pages" 1>&6
+    if test "$ac_cv_have_sysconf_pages" = yes; then
+	GOT_PHYSMEM=1
+	cat >> confdefs.h <<\EOF
+#define HAVE__SC_PHYS_PAGES 1
+EOF
+    fi
+fi
+# BSD sysctl()
+if test -z "$GOT_PHYSMEM"; then
+    echo $ac_n "checking for sysctl(HW_PHYSMEM)""... $ac_c" 1>&6
+echo "configure: checking for sysctl(HW_PHYSMEM)" >&5
+if eval "test \"`echo '$''{'ac_cv_have_hw_physmem'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+      cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <sys/types.h>
+	#include <sys/param.h>
+	#include <sys/sysctl.h>
+int main() {
+sysctl(0,CTL_HW+HW_PHYSMEM,0,0,0,0)
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_have_hw_physmem=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_have_hw_physmem=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_have_hw_physmem" 1>&6
+    if test "$ac_cv_have_hw_physmem" = yes; then
+	GOT_PHYSMEM=1
+	cat >> confdefs.h <<\EOF
+#define HAVE_HW_PHYSMEM 1
+EOF
+    fi
+fi
+# HP-UX pstat_getstatic()
+if test -z "$GOT_PHYSMEM"; then
+    echo $ac_n "checking for pstat_getstatic()""... $ac_c" 1>&6
+echo "configure: checking for pstat_getstatic()" >&5
+if eval "test \"`echo '$''{'ac_cv_have_pstat_getstatic'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+      cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <sys/types.h>
+	#include <sys/pstat.h>
+int main() {
+struct pst_static pss; pstat_getstatic(&pss, sizeof(pss), 1, 0)
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_have_pstat_getstatic=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_have_pstat_getstatic=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_have_pstat_getstatic" 1>&6
+    if test "$ac_cv_have_pstat_getstatic" = yes; then
+	GOT_PHYSMEM=1
+	cat >> confdefs.h <<\EOF
+#define HAVE_PSTAT_GETSTATIC 1
+EOF
+    fi
+fi
+if test -n "$GOT_PHYSMEM"; then
+    cat >> confdefs.h <<\EOF
+#define GOT_PHYSMEM 1
+EOF
+fi
+
+echo $ac_n "checking for __progname""... $ac_c" 1>&6
+echo "configure: checking for __progname" >&5
+if eval "test \"`echo '$''{'ac_cv_have___progname'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+
+int main() {
+	extern char *__progname;
+	return __progname[0];
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  ac_cv_have___progname=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_have___progname=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_have___progname" 1>&6
+if test "$ac_cv_have___progname" = yes; then
+    cat >> confdefs.h <<\EOF
+#define HAVE___PROGNAME 1
+EOF
+fi
+
+
+# how big is NUM_CWFS
+echo $ac_n "checking FD_SETSIZE to compute NUM_CWFS""... $ac_c" 1>&6
+echo "configure: checking FD_SETSIZE to compute NUM_CWFS" >&5
+if eval "test \"`echo '$''{'ac_cv_num_cwfs'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_num_cwfs=32
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#undef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#undef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", max(2, min(20, FD_SETSIZE-32)));
+  exit(0);
+}
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_num_cwfs=`cat conftestval`
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_num_cwfs=32
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$ac_cv_num_cwfs" 1>&6
+cat >> confdefs.h <<EOF
+#define NUM_CWFS $ac_cv_num_cwfs
+EOF
+
+
+# Check whether --with-uid or --without-uid was given.
+if test "${with_uid+set}" = set; then
+  withval="$with_uid"
+  :
+fi
+
+# Check whether --enable-chown or --disable-chown was given.
+if test "${enable_chown+set}" = set; then
+  enableval="$enable_chown"
+  :
+fi
+
+case "$TARGET_SYS" in
+    Darwin)
+	DCCSUID=root
+	DCC_OWN=daemon
+	DCC_GRP=daemon
+	;;
+    FreeBSD|NetBSD)
+	DCCSUID=root
+	DCC_OWN=root
+	DCC_GRP=wheel
+	;;
+    *)
+	DCCSUID=root
+	DCC_OWN=bin
+	DCC_GRP=bin
+	;;
+esac
+
+if test -n "$with_uid" -a "$with_uid" != no; then
+    DCCSUID="$with_uid"
+else
+    with_uid=
+    PARM_UID=
+fi
+
+if test "$enable_sys_install" = no; then
+    #correct common misspelling
+    enable_sys_inst=no
+    PARM_SYS_INST="--disable-sys-inst"
+fi
+if test "$enable_sys_inst" != no; then
+    NO_SYS_INSTALL="#install man pages"
+    NO_SUID="#cdcc, dccproc, ... set-UID $DCCSUID"
+else
+    # Do not install the man pages if not installing on the system.
+    NO_SYS_INSTALL="NO_SYS_INSTALL=not-system-install"
+
+    # avoid chown and chgrp if we are not root
+    # not all systems have `id -n`
+    uid=`id | sed -n \
+		-e 's/.*uid=[0-9][0-9]*(\([^)][^)]*\)).*/\1/p' \
+		-e 's/.*uid=\([0-9][0-9]*\).*/\1/p'`
+    if test -n "$uid"; then
+	DCC_OWN="$uid"
+    fi
+    gid=`id | sed -n \
+		-e 's/.*gid=[0-9][0-9]*(\([^)][^)]*\)).*/\1/p' \
+		-e 's/.*gid=\([0-9][0-9]*\).*/\1/p'`
+    if test -n "$gid"; then
+	DCC_GRP="$gid"
+    fi
+
+    # set-UID even with disable-sys-inst if we been given a UID
+    if test -n "$with_uid"; then
+	NO_SUID="#./configure run as root; cdcc, dccproc, ... set-UID $DCCSUID"
+    else
+	NO_SUID="NO_SUID=no-set-UID"
+	if test -n "$uid"; then
+	    DCCSUID="$uid"
+	fi
+    fi
+fi
+
+if test -s dccd/dccd.c; then
+# Check whether --enable-server or --disable-server was given.
+if test "${enable_server+set}" = set; then
+  enableval="$enable_server"
+  :
+fi
+fi
+# Check whether --enable-dccifd or --disable-dccifd was given.
+if test "${enable_dccifd+set}" = set; then
+  enableval="$enable_dccifd"
+  :
+fi
+# Check whether --enable-dccm or --disable-dccm was given.
+if test "${enable_dccm+set}" = set; then
+  enableval="$enable_dccm"
+  :
+fi
+
+# Look for sendmail
+#	dccm requires the sendmail milter header file and library.
+#	Without an explicit hint in the SENDMAIL environment variable,
+#	look for them first in a sendmail directory parallel to the DCC
+#	source directory containing a standard sendmail source and build tree
+#	from sendmail.org
+# Check whether --with-sendmail or --without-sendmail was given.
+if test "${with_sendmail+set}" = set; then
+  withval="$with_sendmail"
+  :
+fi
+if test "$enable_dccm" = no -o "$with_sendmail" = no -o ! -d dccm; then
+    with_sendmail=no
+    enable_dccm=no
+    SENDMAIL=no
+    SENDMAIL_OBJ=/dev/null
+    SENDMAIL_LIB=/usr/lib
+else
+    SENDMAIL_OBJ=
+    SENDMAIL_LIB=
+fi
+if test -n "$with_sendmail" -a "$with_sendmail" != yes -a "$with_sendmail" != no; then
+    SENDMAIL="$with_sendmail"
+    SENDMAIL_EVAL="$SENDMAIL"
+    with_sendmail=yes
+fi
+if test -z "$SENDMAIL"; then
+    # prefer a parallel ../sendmail directory
+    DEPTH=.
+    SENDMAIL='${DEPTH}/../sendmail'
+    eval SENDMAIL_EVAL=$SENDMAIL
+    # notice native sendmail installation on FreeBSD 4.6 and elsewhere
+    #	prefer a shared library, without depending on test getting the
+    #	precedent of -a and -o right.  One might hope that a FreeBSD system
+    #	with native sendmail milter would not have an old broken test command,
+    #	but who knows?
+    if test ! -d $SENDMAIL_EVAL						\
+	    -a -f /usr/include/libmilter/mfapi.h; then
+	if test -f /usr/lib/libmilter.so; then
+	    SENDMAIL=/usr/include
+	    SENDMAIL_EVAL=$SENDMAIL
+	    SENDMAIL_OBJ=/usr/lib
+	    SENDMAIL_OBJ_EVAL=$SENDMAIL_OBJ
+	    SENDMAIL_LIB=$SENDMAIL_OBJ/libmilter.so
+	fi
+	if test ! -d $SENDMAIL_EVAL					\
+		-a -f /usr/lib/libmilter.a; then
+	    SENDMAIL=/usr/include
+	    SENDMAIL_EVAL=$SENDMAIL
+	    SENDMAIL_OBJ=/usr/lib
+	    SENDMAIL_OBJ_EVAL=$SENDMAIL_OBJ
+	    SENDMAIL_LIB=$SENDMAIL_OBJ/libmilter.a
+	    # RedHat 7.3 put old libsmutil.a in /usr/lib
+	    if test -f /usr/lib/libsmutil.a; then
+		SENDMAIL_LIB="$SENDMAIL_LIB /usr/lib/libsmutil.a"
+	    fi
+	fi
+    fi
+    # notice a FreeBSD sendmail package if there is only one
+    if test ! -d $SENDMAIL_EVAL; then
+	SENDMAIL_PKG=`echo /usr/ports/mail/sendmail/work/sendmail*`
+	if test -d "$SENDMAIL_PKG"; then
+	    SENDMAIL="$SENDMAIL_PKG"
+	    SENDMAIL_EVAL="$SENDMAIL"
+	fi
+    fi
+    # notice native installation on some versions of NetBSD
+    if test ! -d $SENDMAIL_EVAL						\
+	    -a -f /usr/pkg/include/libmilter/mfapi.h			\
+	    -a -f /usr/pkg/lib/libmilter.a; then
+	SENDMAIL=/usr/pkg/include
+	SENDMAIL_EVAL=$SENDMAIL
+	SENDMAIL_OBJ=/usr/pkg/lib
+	SENDMAIL_OBJ_EVAL=$SENDMAIL_OBJ
+	SENDMAIL_LIB=$SENDMAIL_OBJ/libmilter.a
+    fi
+    # notice native installation on some flavors of Linux
+    if test ! -d $SENDMAIL_EVAL						\
+	    -a -f /usr/include/libmilter/mfapi.h			\
+	    -a -f /usr/lib/libmilter/libmilter.a; then
+	SENDMAIL=/usr/include
+	SENDMAIL_EVAL=$SENDMAIL
+	SENDMAIL_OBJ=/usr/lib/libmilter
+	SENDMAIL_OBJ_EVAL=$SENDMAIL_OBJ
+	SENDMAIL_LIB=$SENDMAIL_OBJ/libmilter.a
+	# RedHat 7.3 put old libsmutil.a in /usr/lib
+	if test -f /usr/lib/libsmutil.a; then
+	    SENDMAIL_LIB="$SENDMAIL_LIB /usr/lib/libsmutil.a"
+	fi
+    fi
+fi
+# Look for an object directory in a standard sendmail.org distribution tree
+#   if we still lack libmilter.a
+if test -z "$SENDMAIL_LIB"; then
+    case "$TARGET_SYS" in
+    AIX)
+	# sendmail thinks all AIX systems are "PPC"
+	PLATFORM=`uname -vs | tr ' /' '.\-'`.`uname -r`.PPC;;
+    SunOS)
+	# sendmail does not like the trailing 'u' in sun4u
+	PLATFORM=`uname -rsm | tr ' /' '.\-'				\
+		| sed -e 's/sun4u$/sun4/'`;;
+    Darwin)
+	# sendmail does not like the period in the middle of Power.Macintosh
+	PLATFORM=`uname -rsm | tr ' /' '.\-'				\
+	    | sed -e 's/Power.Macintosh/PowerMacintosh/'`;;
+    *)
+	PLATFORM=`uname -rsm | tr ' /' '.\-'`;;
+    esac
+    SENDMAIL_OBJ="$SENDMAIL/obj.$PLATFORM"
+    SENDMAIL_LIB="$SENDMAIL_OBJ/libmilter/libmilter.a"
+
+    # If there is no libmilter.a in the sendmail.org standard place,
+    #	then maybe we are being run by a FreeBSD port
+    if test ! -f $SENDMAIL_LIB -a -f $SENDMAIL/include/libmilter/mfapi.h    \
+	    -a -f $SENDMAIL/lib/libmilter.a; then
+	SENDMAIL_OBJ=$SENDMAIL/lib
+	SENDMAIL_OBJ_EVAL=$SENDMAIL_OBJ
+	SENDMAIL_LIB=$SENDMAIL_OBJ/libmilter.a
+    fi
+
+    eval SENDMAIL_OBJ_EVAL="\"$SENDMAIL_OBJ\""
+
+    # add the odd old sendmail 8.11 milter extra library
+    if test -f "$SENDMAIL_OBJ_EVAL/libsmutil/libsmutil.a"; then
+	SENDMAIL_LIB="$SENDMAIL_LIB $SENDMAIL_OBJ/libsmutil/libsmutil.a"
+    fi
+    # and the odd libsm.a that 8.12.1 needs for sm_printf()
+    if test -f "$SENDMAIL_OBJ_EVAL/libsm/libsm.a"; then
+	SENDMAIL_LIB="$SENDMAIL_LIB $SENDMAIL_OBJ/libsm/libsm.a"
+    fi
+fi
+
+# figure out -I for dccm/Makefile
+#   If we are using a sendmail source directory, then cc must look in both
+#   the sendmail and include directories.
+SENDMAIL_INC=
+SENDMAIL_INC_DIRS=
+if test -n "$SENDMAIL"; then
+    for DIR in $SENDMAIL $SENDMAIL/sendmail $SENDMAIL/include; do
+	eval DIR_EVAL="$DIR"
+	if test -d "$DIR_EVAL" -a "$DIR" != /usr/include -a "$DIR" != /usr; then
+	    appendvar SENDMAIL_INC -I$DIR
+	    appendvar SENDMAIL_INC_DIRS $DIR_EVAL
+	fi
+    done
+fi
+
+
+# Check whether --with-cgibin or --without-cgibin was given.
+if test "${with_cgibin+set}" = set; then
+  withval="$with_cgibin"
+  :
+fi
+if test -n "$with_cgibin" -a "$with_cgi_bin" != no; then
+    cgibin="$with_cgibin"
+fi
+
+
+# where is a good place to put the dccm and dccifd sockets and PID files?
+# Check whether --with-rundir or --without-rundir was given.
+if test "${with_rundir+set}" = set; then
+  withval="$with_rundir"
+  :
+fi
+dcc_rundir=/var/run/dcc
+if test -n "$with_rundir" -a "$with_rundir" != no; then
+    unset ac_cv_path_varrun
+    dcc_rundir="$with_rundir"
+else
+    echo $ac_n "checking run directory""... $ac_c" 1>&6
+echo "configure: checking run directory" >&5
+if eval "test \"`echo '$''{'ac_cv_path_varrun'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+      if test "$cross_compiling" = yes; then
+  ac_cv_path_varrun=/var/run
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <stdio.h>
+#include "include/dcc_paths.h"
+main()
+{
+    FILE *f=fopen("conftestval", "w");
+    if (!f) exit(1);
+    fprintf(f, _PATH_VARRUN"\n");
+    exit(0);
+}
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  x=`cat conftestval`; ac_cv_path_varrun=`expr "$x" \: '\(.*\)/$' \| "$x"`
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_path_varrun=/var/run
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$ac_cv_path_varrun" 1>&6
+    dcc_rundir=$ac_cv_path_varrun/dcc
+fi
+cat >> confdefs.h <<EOF
+#define DCC_RUNDIR "$dcc_rundir"
+EOF
+
+
+
+# Check whether --with-db-memory or --without-db-memory was given.
+if test "${with_db_memory+set}" = set; then
+  withval="$with_db_memory"
+  :
+fi
+if test -z "$with_db_memory" -o "$with_db_memory" = no; then
+    with_db_memory=0
+fi
+if test -n "`expr "$with_db_memory" : '[0-9]*\(.*\)'`"; then
+    echo "warning: --with-db-memory=X requires a number" 1>&2
+    with_db_memory=0
+fi
+if test "$with_db_memory" -ne 0; then
+    # related to MAX_MAX_DB_MBYTE in db.c, help string here, and INSTALL.html
+    if test "$with_db_memory" -lt 32 -o $with_db_memory -gt 49152; then
+	echo "warning: --with-db-memory=X must be between 32 MBytes and 49152 MBytes" 1>&2
+	with_db_memory=0
+    fi
+fi
+cat >> confdefs.h <<EOF
+#define DB_MIN_MBYTE $with_db_memory
+EOF
+
+# Check whether --with-max-db-mem or --without-max-db-mem was given.
+if test "${with_max_db_mem+set}" = set; then
+  withval="$with_max_db_mem"
+  :
+fi
+if test -z "$with_max_db_mem" -o "$with_max_db_mem" = no; then
+    with_max_db_mem=0
+fi
+if test -n "`expr "$with_max_db_mem" : '[0-9]*\(.*\)'`"; then
+    echo "warning: --with-max-db_mem=X requires a number" 1>&2
+    with_max_db_mem=0
+fi
+if test "$with_max_db_mem" -ne 0; then
+    if test "$with_max_db_mem" -lt 32 -o $with_max_db_mem -gt 49152; then
+	echo "warning: --with-max-db_mem=X must be between 32 MBytes and 49152 MBytes" 1>&2
+	with_max_db_mem=0
+    fi
+    if test "$with_db_memory" -ne 0	\
+		-a "$with_max_db_mem" -lt $with_db_memory; then
+	echo "warning: --with-max-db_mem=$with_max_db_mem less than --with-db-memory=$with_db_memory" 1>&2
+	with_max_db_mem=0
+    fi
+fi
+
+cat >> confdefs.h <<EOF
+#define DB_MAX_MBYTE $with_max_db_mem
+EOF
+
+
+# Check whether --with-max-log-size or --without-max-log-size was given.
+if test "${with_max_log_size+set}" = set; then
+  withval="$with_max_log_size"
+  :
+fi
+if test "$with_max_log_size" = no; then
+    with_max_log_size=
+fi
+if test -n "$with_max_log_size"	\
+	-a -n "`expr 0$with_max_log_size : '[0-9]*\(.*\)'`"; then
+    echo "warning: --with-max-log-size=X requires a number" 1>&2
+    with_max_log_size=
+fi
+if test -z "$with_max_log_size"; then
+    with_max_log_size=32
+fi
+# assume it is in bytes instead of KBytes if it is more than 2*1024*1024, 
+if test 0$with_max_log_size -ge 2097152; then
+    with_max_log_size=`expr \( 0$with_max_log_size + 1023 \) / 1024`
+fi
+# "unlimited is really 2 GBbyte
+if test $with_max_log_size -eq 0; then
+    with_max_log_size=2097152
+fi
+cat >> confdefs.h <<EOF
+#define MAX_LOG_KBYTE $with_max_log_size
+EOF
+
+
+# Check whether --with-rl-min-max or --without-rl-min-max was given.
+if test "${with_rl_min_max+set}" = set; then
+  withval="$with_rl_min_max"
+  :
+fi
+if test "$with_rl_min_max" = no; then
+    with_rl_min_max=
+fi
+if test -n "$with_rl_min_max"	\
+	-a -n "`expr 0$with_rl_min_max : '[0-9]*\(.*\)'`"; then
+    echo "warning: --with-rl-min-max=X requires a number" 1>&2
+    with_rl_min_max=
+fi
+if test -n "$with_rl_min_max"; then
+    cat >> confdefs.h <<EOF
+#define RL_MIN_MAX $with_rl_min_max
+EOF
+fi
+
+
+echo $ac_n "checking for sysctl(kern.boottime)""... $ac_c" 1>&6
+echo "configure: checking for sysctl(kern.boottime)" >&5
+if eval "test \"`echo '$''{'ac_cv_have_boottime'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+      cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <sys/types.h>
+	#include <sys/param.h>
+	#include <sys/sysctl.h>
+int main() {
+sysctl(0,CTL_KERN+KERN_BOOTTIME,0,0,0,0)
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_have_boottime=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_have_boottime=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_have_boottime" 1>&6
+if test "$ac_cv_have_boottime" = yes; then
+    cat >> confdefs.h <<\EOF
+#define HAVE_BOOTTIME 1
+EOF
+fi
+
+
+# Decide whether we should try to build IPv6 support.  Be conservative
+#	and give up at the first sign of trouble.
+# Also check for some other network related system oddities.
+# Check whether --enable-IPv6 or --disable-IPv6 was given.
+if test "${enable_IPv6+set}" = set; then
+  enableval="$enable_IPv6"
+  :
+fi
+# some systems think they have IPv6 but don't or have odd definitions
+#   for the structure tags
+case "$TARGET_SYS" in
+    SunOS)
+	cat >> confdefs.h <<\EOF
+#define CONF_S6_ADDR32 _S6_un._S6_u32
+EOF
+	# use poll() instead of select() on Solaris because socket() can yield
+	# file descriptors larger than FD_SETSIZE
+	if test "$ac_cv_func_poll" = yes; then
+	    cat >> confdefs.h <<\EOF
+#define USE_POLL 1
+EOF
+	fi
+	# use `dbclean -F` on Solaris to force less unneeded disk I/O
+	cat >> confdefs.h <<\EOF
+#define USE_DBCLEAN_F 1
+EOF
+	cat >> confdefs.h <<\EOF
+#define HAVE_COHERENT_MMAP 1
+EOF
+	;;
+    OSF1)
+	cat >> confdefs.h <<\EOF
+#define CONF_S6_ADDR32 s6_laddr
+EOF
+	;;
+    OpenUNIX)
+	enable_IPv6=no
+	cat >> confdefs.h <<\EOF
+#define CONF_S6_ADDR32 S6_un.S6_l
+EOF
+	;;
+    Linux)
+	# allow file descriptors larger than FD_SETSIZE
+	if test "$ac_cv_func_poll" = yes; then
+	    cat >> confdefs.h <<\EOF
+#define USE_POLL 1
+EOF
+	fi
+	;;
+    FreeBSD|DragonFly)
+	# allow file descriptors larger than FD_SETSIZE
+	if test "$ac_cv_func_poll" = yes; then
+	    cat >> confdefs.h <<\EOF
+#define USE_POLL 1
+EOF
+	fi
+	cat >> confdefs.h <<\EOF
+#define CONF_S6_ADDR32 __u6_addr.__u6_addr32
+EOF
+	cat >> confdefs.h <<\EOF
+#define HAVE_COHERENT_MMAP 1
+EOF
+	;;
+    HP-UX)
+	# allow file descriptors larger than FD_SETSIZE
+	if test "$ac_cv_func_poll" = yes; then
+	    cat >> confdefs.h <<\EOF
+#define USE_POLL 1
+EOF
+	fi
+	# at least some versions of HP-UX do not have working select()
+	#   poll(), and shutdown() on AF_UNIX sockets.
+	if test `uname -r | sed -e 's/^[A-Z.]*//' -e 's/\.//g'` -le 1100; then
+	    cat >> confdefs.h <<\EOF
+#define HP_UX_BAD_AF_UNIX 1
+EOF
+	fi
+	;;
+    *)
+	cat >> confdefs.h <<\EOF
+#define CONF_S6_ADDR32 __u6_addr.__u6_addr32
+EOF
+	;;
+esac
+if test "$enable_IPv6"SET = noSET -o "$enable_IPV6"SET = noSET	\
+	-o "$enable_ipv6"SET = noSET; then
+    NO_IPV6="disabled"
+else
+    if test "$ac_cv_func_gai_strerror" != yes; then
+	no_getaddrinfo="no gai_strerror()"
+    fi
+    if test "$ac_cv_func_freeaddrinfo" != yes; then
+	no_getaddrinfo="no freeaddrinfo()"
+    fi
+    if test "$ac_cv_func_getaddrinfo" != yes; then
+	no_getaddrinfo="no getaddrinfo()"
+    fi
+    if test "$ac_cv_func_freehostent" != yes; then
+	no_getipnodebyname="no freehostent()"
+    fi
+    if test "$ac_cv_func_getipnodebyaddr" != yes; then
+	no_getipnodebyname="no getipnodebyaddr()"
+    fi
+    if test "$ac_cv_func_getipnodebyname" != yes; then
+	no_getipnodebyname="no getipnodebyname()"
+    fi
+    if test -n "$no_getipnodebyname" -a -n "$no_getaddrinfo"; then
+	NO_IPV6="$no_getipnodebyname and $no_getaddrinfo"
+    fi
+fi
+echo $ac_n "checking for IPv6""... $ac_c" 1>&6
+echo "configure: checking for IPv6" >&5
+cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <netinet/in.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "sockaddr_in6" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  NO_IPV6="no sockaddr_in6"
+fi
+rm -f conftest*
+cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include <netinet/in.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "in6_addr" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  NO_IPV6="no in6_addr"
+fi
+rm -f conftest*
+cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+#include<sys/socket.h>
+AF_INET6
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "^AF_INET6$" >/dev/null 2>&1; then
+  rm -rf conftest*
+  NO_IPV6="no AF_INET6"
+    cat >> confdefs.h <<\EOF
+#define NO_AF_INET6 1
+EOF
+
+fi
+rm -f conftest*
+
+if test -z "$NO_IPV6"; then
+    echo "$ac_t""yes" 1>&6
+else
+    echo "$ac_t""$NO_IPV6" 1>&6
+    cat >> confdefs.h <<EOF
+#define NO_IPV6 "$NO_IPV6"
+EOF
+fi
+
+
+
+# Decide about man pages and what to build
+if test "$prefix" = NONE; then
+    if test -z "$DCC_HOMEDIR"; then
+	DCC_HOMEDIR=$ac_default_prefix
+    fi
+    prefix=$DCC_HOMEDIR
+fi
+cat >> confdefs.h <<EOF
+#define DCC_HOMEDIR "$prefix"
+EOF
+if test "$libexecdir" = '${exec_prefix}/libexec'; then
+    libexecdir=$prefix/libexec
+fi
+cat >> confdefs.h <<EOF
+#define DCC_LIBEXECDIR "$libexecdir"
+EOF
+if test "$bindir" = '${exec_prefix}/bin'; then
+    if test "$enable_sys_inst" = no -a ! -w /usr/local/bin -a -d $HOME/bin; then
+	bindir=$HOME/bin
+	echo "$ac_t""install binaries in $bindir" 1>&6
+    else
+	bindir=/usr/local/bin
+    fi
+fi
+
+# default cgibin after $prefix has been figured out
+if test -z "$cgibin"; then
+    cgibin="$prefix/cgi-bin"
+fi
+
+# Pick the default target location of the man pages and
+#	decide whether to install .8 or .0 files.
+case "$TARGET_SYS" in
+    Linux)
+	# default to /usr/local/man/man8 and use roff files
+	mancat=man
+	MAN8='dcc.8 $(SUBDIR_MAN8)'
+	MAN8INST=''
+	USE_DCCMANINSTALL='maninstall:dccmaninstall'
+	;;
+    OpenBSD)
+	# default to /usr/local/man/cat8 and whatever.0 names
+	mancat=cat
+	MAN8='dcc.0 $(SUBDIR_MAN0)'
+	MAN8INST=''
+	USE_DCCMANINSTALL='maninstall:dccmaninstall'
+	;;
+    HP-UX)
+	# default to /usr/local/man/cat8 and use cleartext files and
+	#   whatever.8 names
+	mancat=cat
+	MAN8='dcc.0 $(SUBDIR_MAN0)'
+	MAN8INST='/`expr $$NM : "\(.*\)".0`.8'
+	USE_DCCMANINSTALL='maninstall:dccmaninstall'
+	;;
+    IRIX*)
+	# default to /usr/local/man/man8 and use cleartext files and
+	#   whatever.8 names
+	mancat=man
+	MAN8='dcc.0 $(SUBDIR_MAN0)'
+	MAN8INST='/`expr $$NM : "\(.*\)".0`.8'
+	USE_DCCMANINSTALL='maninstall:dccmaninstall'
+	;;
+    SunOS)
+	# default to /usr/local/man/cat8 and use cleartext files,
+	#   use our installation rule, and whatever.8 names
+	mancat=cat
+	MAN8='dcc.0 $(SUBDIR_MAN0)'
+	MAN8INST='/`expr $$NM : "\(.*\)".0`.8'
+	USE_DCCMANINSTALL='maninstall:dccmaninstall'
+	;;
+    FreeBSD)
+	# default to /usr/local/man/man8 and use nroff files, possibly
+	#   compressed via /usr/share/mk
+	mancat=man
+	MAN8='dcc.8 $(SUBDIR_MAN8)'
+	MAN8INST=''
+	USE_DCCMANINSTALL='# do not use dccmaninstall; instead use the built in maninstall'
+	;;
+    DragonFly)
+	# default to /usr/local/man/man8 and use nroff files, possibly
+	#   compressed via /usr/share/mk
+	mancat=man
+	MAN8='dcc.8 $(SUBDIR_MAN8)'
+	MAN8INST=''
+	USE_DCCMANINSTALL='# do not use dccmaninstall; instead use the built in maninstall'
+	;;
+    Darwin)
+	# default to /usr/local/man/man8 and use nroff files
+	mancat=man
+	MAN8='dcc.8 $(SUBDIR_MAN8)'
+	MAN8INST=''
+	USE_DCCMANINSTALL='maninstall:dccmaninstall'
+	;;
+    NetBSD)
+	# default to /usr/local/man/man8 and use nroff files, possibly
+	#   compressed via /usr/share/mk on versions well after 1.4.2.
+	mancat=cat
+	MAN8='dcc.0 $(SUBDIR_MAN0)'
+	MAN8INST=''
+	# force NetBSD to install the man pages
+	USE_DCCMANINSTALL='install maninstall: dccmaninstall'
+	;;
+    BSD/OS)
+	# default to /usr/local/man/cat8 and use cleartext files, possibly
+	#   compressed via /usr/share/mk, and named whatever.0
+	mancat=cat
+	MAN8='dcc.0 $(SUBDIR_MAN0)'
+	MAN8INST=''
+	USE_DCCMANINSTALL='# do not use dccmaninstall; instead use the built in maninstall'
+	;;
+    AIX)
+	# default to /usr/local/man/cat8, use our installation rule,
+	#   and use cleartext files named whatever.8
+	mancat=cat
+	MAN8='dcc.0 $(SUBDIR_MAN0)'
+	MAN8INST='/`expr $$NM : "\(.*\)".0`.8'
+	USE_DCCMANINSTALL='maninstall:dccmaninstall'
+	;;
+    OpenUNIX)
+	# default to /usr/local/man/cat8 and use cleartext files named
+	#   whatever.8
+	mancat=cat
+	MAN8='dcc.0 $(SUBDIR_MAN0)'
+	MAN8INST='/`expr $$NM : "\(.*\)".0`.8'
+	USE_DCCMANINSTALL='maninstall:dccmaninstall'
+	;;
+    *)
+	echo "warning: 	*** Cannot install man pages on unfamiliar systems ***" 1>&2
+	mancat=fixme
+	MAN8=
+	MAN8INST=
+	USE_DCCMANINSTALL='maninstall:dccmaninstall'
+	;;
+esac
+if test "$mandir" = '${prefix}/man'; then
+    if test "$DCC_MANDIR"SET != SET; then
+	mandir="$DCC_MANDIR"
+    else
+	mandir=/usr/local/man
+    fi
+fi
+mandir="$mandir/$mancat"
+USE_GROFF='USE_GROFF=no'
+if expr "$MAN8" : 'dcc\.0' >/dev/null; then
+   if groff -Tascii -mtty-char -mdoc </dev/null >/dev/null; then
+	USE_GROFF='USE_GROFF=yes'
+	echo "$ac_t""use groff to customize .0 man pages" 1>&6
+    else
+	echo "$ac_t""need to but cannot use groff to customize .0 man pages" 1>&6
+    fi
+else
+    echo "$ac_t""ignore .0 man pages" 1>&6    
+fi
+
+# Enough systems have idiosyncratic native install programs and we install
+#   few enough files that it is usually best to use the autoconf script.
+#   A few systems cannot tolerate the autoconf script.
+case "$TARGET_SYS" in
+    CYGWIN*)
+	DCCINSTALL=install
+	;;
+    *)
+	DCCINSTALL='$(DEPTH)/autoconf/install-sh -c'
+	# some systems do not have UNIX file permissions
+	cat >> confdefs.h <<\EOF
+#define HAVE_PRIVATE_FILES 1
+EOF
+	;;
+esac
+
+SUBDIR_DCCIFD=
+if test "$enable_dccifd" != no; then
+    if test "$HAVE_PTHREADS" = yes; then
+	SUBDIR_DCCIFD=dccifd
+    else
+	echo "warning: 	*** cannot build dccifd without threads ***" 1>&2
+    fi
+fi
+if test -z "$SUBDIR_DCCIFD"; then
+    SUBDIR_DISABLED="$SUBDIR_DISABLED dccifd"
+fi
+
+# disable server for DCC Reputation trials
+if test ! -s dccd/dccd.c; then
+    enable_server=no
+fi
+# allow varient --disable-dccd
+if test "$enable_server" = no -o "$enable_dccd" = no; then
+    SUBDIR_DCCD=
+    SUBDIR_SRVRLIB=
+    SUBDIR_DISABLED="$SUBDIR_DISABLED dccd dbclean dblist srvrlib"
+else
+    SUBDIR_DCCD="dbclean dccd dblist"
+    SUBDIR_SRVRLIB="srvrlib"
+fi
+
+# We have already chosen the sendmail source directory.  Here we only
+#	check that the milter library is present and turn dccm on or off.
+SUBDIR_DCCM=
+if test "$with_sendmail"SET != noSET -a "$enable_dccm"SET != noSET; then
+    SUBDIR_DCCM=dccm
+    if test -n "$SENDMAIL_INC"; then
+	echo "$ac_t""look for milter headers in $SENDMAIL_INC_DIRS" 1>&6
+    else
+	echo "$ac_t""look for milter headers only in common directory" 1>&6
+    fi
+    MFAPI=`find $SENDMAIL_INC_DIRS /usr/include				\
+		-type d -name libmilter 2>/dev/null`
+    if test -n "$MFAPI"; then
+	MFAPI="`find $MFAPI -name mfapi.h`"
+    fi
+    if test -z "$MFAPI"; then
+	echo "warning: 	*** cannot build dccm without mfapi.h ***" 1>&2
+	SUBDIR_DCCM=
+    fi
+    echo "$ac_t""look for milter library in $SENDMAIL_OBJ_EVAL" 1>&6
+    if test ! -d $SENDMAIL_OBJ_EVAL; then
+	echo "warning: 	*** cannot build dccm without sendmail libraries in $SENDMAIL_OBJ_EVAL ***" 1>&2
+	SUBDIR_DCCM=
+    fi
+    if test "$HAVE_PTHREADS" != yes; then
+	echo "warning: 	*** cannot build dccm without threads ***" 1>&2
+	SUBDIR_DCCM=
+    fi
+# If dccm has been installed but there is no sendmail library or headers,
+# then assume something is recently broken and try to build dccm regardless.
+# This prevents bad things like losing a sendmail directory from letting
+# a DCC build appear to succeed.
+    if test -x "$libexecdir/dccm"; then
+	if test -z "$SUBDIR_DCCM"; then
+	    echo "$ac_t""	*** try to rebuild dccm because it is already installed ***" 1>&6
+	fi
+	SUBDIR_DCCM=dccm
+    fi
+fi
+if test -z "$SUBDIR_DCCM"; then
+    SUBDIR_DISABLED="$SUBDIR_DISABLED dccm"
+fi
+if test -z "$SUBDIR_DCCM$SUBDIR_DCCIFD"; then
+    SUBDIR_DISABLED="$SUBDIR_DISABLED thrlib"
+    SUBDIR_THRLIB=
+else
+    SUBDIR_THRLIB="dns-helper thrlib"
+fi
+
+
+# Check whether --with-kludge or --without-kludge was given.
+if test "${with_kludge+set}" = set; then
+  withval="$with_kludge"
+  :
+fi
+if test -n "$with_kludge" -a "$with_kludge" != no; then
+    kludge_h="$with_kludge"
+    cat >> confdefs.h <<\EOF
+#define NEED_KLUDGE_H 1
+EOF
+else
+    kludge_h="@kludge_h@"
+fi
+
+
+# Check whether --with-socks or --without-socks was given.
+if test "${with_socks+set}" = set; then
+  withval="$with_socks"
+  :
+fi
+if test "$with_socks" = no; then
+    with_socks=
+fi
+if test "$ac_cv_lib_socks" != "$with_socks"; then
+    unset ac_cv_func_Rsendto
+fi
+ac_cv_lib_socks="$with_socks"
+if test "$with_socks" != yes -a -n "$with_socks"; then
+    if expr "$with_socks" : '.*/.*' >/dev/null; then
+	LIBS="$LIBS $with_socks"
+    else
+	LIBS="$LIBS -l$with_socks"
+    fi
+fi
+for ac_func in Rsendto
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+ 
+else
+  echo "$ac_t""no" 1>&6
+if test "$with_socks" != yes -a -n "$with_socks"; then
+	echo "$ac_t""	warning: SOCKS functions including Rsendto() not found in -l$with_socks" 1>&6
+	unset ac_cv_func_Rsendto
+    fi
+    if test "$with_socks" = yes; then
+	echo "$ac_t""	warning: SOCKS functions including Rsendto() not found" 1>&6
+	unset ac_cv_func_Rsendto
+    fi
+fi
+done
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set | grep ac_space) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    if test "$silent" != yes; then
+	echo "updating cache $cache_file"
+    fi
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[ 	]*VPATH[ 	]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+if test "$silent" != yes; then
+    echo creating $CONFIG_STATUS
+fi
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+
+trap 'rm -fr `echo "./Makefile ./Makefile.inc ./Makefile.inc2 ./cdcc/Makefile ./cgi-bin/Makefile ./cgi-bin/chgpasswd ./cgi-bin/common ./cgi-bin/edit-whiteclnt ./cgi-bin/list-log ./cgi-bin/list-msg ./cgi-bin/webuser-notify ./dbclean/Makefile ./dblist/Makefile ./dccd/Makefile ./dccd/dump-clients/Makefile ./dccifd/Makefile ./dccifd/dccif-test/Makefile ./dccifd/dccif.pl ./dcclib/Makefile ./dccm/Makefile ./dccproc/Makefile ./dccsight/Makefile ./dns-helper/Makefile ./gmake.inc ./homedir/Makefile ./homedir/dcc_conf ./include/kludge.h ./misc/Makefile ./misc/cron-dccd ./misc/crontab ./misc/dcc-stats-collect ./misc/dcc-stats-graph ./misc/dcc-stats-init ./misc/dcc.m4 ./misc/fetch-testmsg-whitelist ./misc/fetchblack ./misc/fetchids ./misc/list-clients ./misc/newwebuser ./misc/rcDCC ./misc/start-dccd ./misc/start-dccifd ./misc/start-dccm ./misc/start-grey ./misc/stats-get ./misc/stop-dccd ./misc/uninstalldcc ./misc/updatedcc ./rrd-combine/Makefile ./srvrlib/Makefile ./thrlib/Makefile ./cdcc.8 ./dbclean.8 ./dblist.8 ./dcc.8 ./dccd.8 ./dccifd.8 ./dccm.8 ./dccproc.8 ./dccsight.8 ./FAQ.html ./INSTALL.html ./cdcc.html ./dbclean.html ./dblist.html ./dcc.html ./dccd.html ./dccifd.html ./dccm.html ./dccproc.html ./dccsight.html  include/dcc_config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@libexecdir@%$libexecdir%g
+s%@mandir@%$mandir%g
+s%@UPDATEDCC_PARMS@%$UPDATEDCC_PARMS%g
+s%@UPDATEDCC_ENV@%$UPDATEDCC_ENV%g
+s%@UPDATEDCC_ENV_SET@%$UPDATEDCC_ENV_SET%g
+s%@UPDATEDCC_ENV_EXPORT@%$UPDATEDCC_ENV_EXPORT%g
+s%@UPDATEDCC_PFILE@%$UPDATEDCC_PFILE%g
+s%@installroot@%$installroot%g
+s%@configsuffix@%$configsuffix%g
+s%@CC@%$CC%g
+s%@DCC_CC@%$DCC_CC%g
+s%@RANLIB@%$RANLIB%g
+s%@CPP@%$CPP%g
+s%@DCC_CFLAGS@%$DCC_CFLAGS%g
+s%@DCC_LDFLAGS@%$DCC_LDFLAGS%g
+s%@PTHREAD_CFLAGS@%$PTHREAD_CFLAGS%g
+s%@PTHREAD_LDFLAGS@%$PTHREAD_LDFLAGS%g
+s%@PTHREAD_LIBS@%$PTHREAD_LIBS%g
+s%@HAVE_CC_M@%$HAVE_CC_M%g
+s%@CWRN1@%$CWRN1%g
+s%@CWRN2@%$CWRN2%g
+s%@CWRN3@%$CWRN3%g
+s%@DCCMAKE@%$DCCMAKE%g
+s%@MAKE_INC@%$MAKE_INC%g
+s%@MAKE_INC2@%$MAKE_INC2%g
+s%@MAKE_PROG@%$MAKE_PROG%g
+s%@MAKE_SUBDIR@%$MAKE_SUBDIR%g
+s%@MAKE_LIB@%$MAKE_LIB%g
+s%@MAKE_DOT@%$MAKE_DOT%g
+s%@DCC_LOGGER@%$DCC_LOGGER%g
+s%@DCC_XARGS@%$DCC_XARGS%g
+s%@FETCH_CMD@%$FETCH_CMD%g
+s%@FETCH_WGET_OPTS@%$FETCH_WGET_OPTS%g
+s%@FETCH_CURL_OPTS@%$FETCH_CURL_OPTS%g
+s%@PERL@%$PERL%g
+s%@NOTIFYMAILER@%$NOTIFYMAILER%g
+s%@HTPASSWD@%$HTPASSWD%g
+s%@RRDTOOL@%$RRDTOOL%g
+s%@DCC_SU@%$DCC_SU%g
+s%@NO_SYS_INSTALL@%$NO_SYS_INSTALL%g
+s%@NO_SUID@%$NO_SUID%g
+s%@DCCSUID@%$DCCSUID%g
+s%@DCC_OWN@%$DCC_OWN%g
+s%@DCC_GRP@%$DCC_GRP%g
+s%@SENDMAIL_LIB@%$SENDMAIL_LIB%g
+s%@SENDMAIL_INC@%$SENDMAIL_INC%g
+s%@cgibin@%$cgibin%g
+s%@dcc_rundir@%$dcc_rundir%g
+s%@MAN8@%$MAN8%g
+s%@MAN8INST@%$MAN8INST%g
+s%@USE_DCCMANINSTALL@%$USE_DCCMANINSTALL%g
+s%@USE_GROFF@%$USE_GROFF%g
+s%@DCCINSTALL@%$DCCINSTALL%g
+s%@SUBDIR_DCCIFD@%$SUBDIR_DCCIFD%g
+s%@SUBDIR_DCCD@%$SUBDIR_DCCD%g
+s%@SUBDIR_SRVRLIB@%$SUBDIR_SRVRLIB%g
+s%@SUBDIR_DCCM@%$SUBDIR_DCCM%g
+s%@SUBDIR_THRLIB@%$SUBDIR_THRLIB%g
+s%@SUBDIR_DISABLED@%$SUBDIR_DISABLED%g
+s%@kludge_h@%$kludge_h%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"./Makefile ./Makefile.inc ./Makefile.inc2 ./cdcc/Makefile ./cgi-bin/Makefile ./cgi-bin/chgpasswd ./cgi-bin/common ./cgi-bin/edit-whiteclnt ./cgi-bin/list-log ./cgi-bin/list-msg ./cgi-bin/webuser-notify ./dbclean/Makefile ./dblist/Makefile ./dccd/Makefile ./dccd/dump-clients/Makefile ./dccifd/Makefile ./dccifd/dccif-test/Makefile ./dccifd/dccif.pl ./dcclib/Makefile ./dccm/Makefile ./dccproc/Makefile ./dccsight/Makefile ./dns-helper/Makefile ./gmake.inc ./homedir/Makefile ./homedir/dcc_conf ./include/kludge.h ./misc/Makefile ./misc/cron-dccd ./misc/crontab ./misc/dcc-stats-collect ./misc/dcc-stats-graph ./misc/dcc-stats-init ./misc/dcc.m4 ./misc/fetch-testmsg-whitelist ./misc/fetchblack ./misc/fetchids ./misc/list-clients ./misc/newwebuser ./misc/rcDCC ./misc/start-dccd ./misc/start-dccifd ./misc/start-dccm ./misc/start-grey ./misc/stats-get ./misc/stop-dccd ./misc/uninstalldcc ./misc/updatedcc ./rrd-combine/Makefile ./srvrlib/Makefile ./thrlib/Makefile ./cdcc.8 ./dbclean.8 ./dblist.8 ./dcc.8 ./dccd.8 ./dccifd.8 ./dccm.8 ./dccproc.8 ./dccsight.8 ./FAQ.html ./INSTALL.html ./cdcc.html ./dbclean.html ./dblist.html ./dcc.html ./dccd.html ./dccifd.html ./dccm.html ./dccproc.html ./dccsight.html "}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+
+  echo . | tr -d '\n'
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ 	]*\)#\([ 	]*define[ 	][ 	]*\)'
+ac_dB='\([ 	][ 	]*\)[^ 	]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ 	]*\)#\([ 	]*\)undef\([ 	][ 	]*\)'
+ac_uB='\([ 	]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ 	]*\)#\([ 	]*\)undef\([ 	][ 	]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+  CONFIG_HEADERS="include/dcc_config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  echo creating $ac_file
+
+  rm -f conftest.frag conftest.in conftest.out
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h.  And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+  ac_lines=`grep -c . conftest.vals`
+  # grep -c gives empty output for an empty file on some AIX systems.
+  if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+  # Write a limited-size here document to conftest.frag.
+  echo '  cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+  echo 'CEOF
+  sed -f conftest.frag conftest.in > conftest.out
+  rm -f conftest.in
+  mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+  rm -f conftest.vals
+  mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+  rm -f conftest.frag conftest.h
+  sed -e "s%@configure_input@%Generated automatically by configure%g" conftest.in >conftest.h
+  rm -f conftest.in
+  if cmp -s $ac_file conftest.h 2>/dev/null; then
+    echo "$ac_file is unchanged"
+    rm -f conftest.h
+  else
+    # Remove last slash and all that follows it.  Not all systems have dirname.
+      ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+      if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+      # The file is in a subdirectory.
+      test ! -d "$ac_dir" && mkdir "$ac_dir"
+    fi
+    rm -f $ac_file
+    mv conftest.h $ac_file
+  fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
+
+
+
+
+
+
+sed -e 's@\(HREF="[^"]*\)\.in">@\1">@' INSTALL.html > INSTALL.html$$
+/bin/rm -f INSTALL.html; mv INSTALL.html$$ INSTALL.html
+
+
+sed -e 's@\(HREF="[^"]*\)\.in">@\1">@' FAQ.html > FAQ.html$$
+/bin/rm -f FAQ.html; mv FAQ.html$$ FAQ.html
+
+
+
diff -r 000000000000 -r c7f6b056b673 dbclean.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbclean.0	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,145 @@
+dbclean(8)            Distributed Checksum Clearinghouse            dbclean(8)
+
+NNAAMMEE
+     ddbbcclleeaann -- Clean Distributed Checksum Clearinghouse Database
+
+SSYYNNOOPPSSIISS
+     ddbbcclleeaann [--ddffFFNNPPSSVVqq] [--ii _i_d] [--aa [_s_e_r_v_e_r_-_a_d_d_r][_,_p_o_r_t]] [--hh _h_o_m_e_d_i_r]
+             [--GG _o_n] [--RR _m_o_d_e] [--ss _h_a_s_h_-_s_i_z_e] [--ee _s_e_c_o_n_d_s] [--EE _s_p_a_m_s_e_c_s]
+             [--LL _l_t_y_p_e_,_f_a_c_i_l_i_t_y_._l_e_v_e_l]
+
+DDEESSCCRRIIPPTTIIOONN
+     DDbbcclleeaann creates empty, rebuilds corrupted, and deletes or expires old
+     reports of checksums from DCC databases.  It should be installed where it
+     will be found with the path given the DCC server daemon when the daemon
+     needs to expand the hash table.  See dccd(8).  It should also be run by
+     cron(8) approximately daily.
+
+     If the hash table in the database has been damaged, ddbbcclleeaann tries to
+     repair the database.
+
+     The contents of the _w_h_i_t_e_l_i_s_t file are built into the DCC server's data-
+     base.  Changes to the whitelist are not effective until dbclean is run to
+     expire reports.  White or blacklists can also be used by DCC clients, and
+     generally work better there.
+
+   OOPPTTIIOONNSS
+     The following options are available:
+
+     --dd   enables debugging output.  Additional --dd options increase the number
+          of messages.
+
+     --FF   uses write() instead of mmap() in some cases to modify the DCC data-
+          base.  This works better on some versions of Solaris provided the
+          entire DCC database fits in RAM and provided the file system has not
+          been tuned for the large, random accesses of a DCC database.  It is
+          the default on Solaris.
+
+     --ff   turns off --FF.
+
+     --NN   creates a new, empty database.  There must not be an existing data-
+          base and the DCC server, dccd(8), must not be running.
+
+     --PP   expires old checksums from a database using the --ee --aanndd --EE values
+          from the preceding use of ddbbcclleeaann.  --PP cannot be used with --ee or --EE.
+          Note also that using --PP differs from not using --ee or --EE, because in
+          the absence of all four, their default values are used.
+
+     --SS   says that the DCC server, dccd(8), is not running and so ddbbcclleeaann
+          should run stand-alone and not try to tell the DCC server about
+          changes to the database.  --ii is not needed when --SS is present.
+
+     --VV   displays the version of the DCC database cleaner.
+
+     --qq   quiets the announcement of results at the end.
+
+     --ii _i_d
+          specifies the DCC ID recognized by the local DCC server as its own.
+          This ID allows the DCC server to recognize commands from ddbbcclleeaann to
+          stop using the database while it is being cleaned.
+
+     --aa [_s_e_r_v_e_r_-_a_d_d_r][_,_p_o_r_t]
+          is commonly used to specify a UDP port or IP address of the local
+          server other than the default.
+
+     --hh _h_o_m_e_d_i_r
+          overrides the default DCC home directory, _/_v_a_r_/_d_c_c.
+
+     --GG _o_n
+          cleans a greylist database.
+
+     --RR _m_o_d_e
+          repairs a database or does a quick cleaning.  _M_o_d_e must be one of
+          the following:
+          _b_a_d    to repair a broken database.
+          _q_u_i_c_k  for a quick, superficial cleaning during the day.
+          _h_a_s_h   to rebuild a hash not sent to disk before the system was
+                 recently restarted.
+          _c_r_o_n   for the nightly cleaning by the cron(8) job
+                 _/_v_a_r_/_d_c_c_/_l_i_b_e_x_e_c_/_c_r_o_n_-_d_c_c_d
+          _d_e_l    to finish processing a delete command received by dccd(8).
+
+     --ss _h_a_s_h_-_s_i_z_e
+          specifies a size for the hash table.  By default the hash table is
+          rebuilt to be approximately 80% full based on an estimate of the
+          number of distinct checksums in the main file.
+
+     --ee _s_e_c_o_n_d_s
+          specifies that reports older than _s_e_c_o_n_d_s and with totals below 10
+          targets should be deleted.  Reports older than _s_e_c_o_n_d_s of checksums
+          that have been reported more recently are summarized in the data-
+          base.  The default value is 1DAY or the value of --EE, whichever is
+          smaller.  The 1 day default is reduced if the system does not appear
+          to have enough RAM to hold the database.  The minimum is 1 hour.
+          _S_e_c_o_n_d_s can also be _N_E_V_E_R or a number of hours, days, or weeks fol-
+          lowed by _H_O_U_R_S, _H, _D_A_Y_S, _D, _W_E_E_K_S or _W.
+
+          DCC servers that are not very busy and are isolated or do not
+          receive "floods" of checksums from busy servers should use longer
+          values to increase their chances of recognizing bulk mail.
+
+     --EE _s_p_a_m_s_e_c_s
+          changes the expiration of checksums with more than 10 targets from
+          the default of 30DAYS or the explicit value of --ee, whichever is
+          larger.  The default is reduced if the system does not have enough
+          RAM to hold the database.  _S_p_a_m_s_e_c_s can also be _N_E_V_E_R or a number of
+          hours, days, or weeks followed by _H_O_U_R_S, _H, _D_A_Y_S, _D, _W_E_E_K_S or _W.
+
+     --LL _l_t_y_p_e_,_f_a_c_i_l_i_t_y_._l_e_v_e_l
+          specifies how messages should be logged.  _L_t_y_p_e must be _e_r_r_o_r, _i_n_f_o,
+          or _o_f_f to indicate which of the two types of messages are being con-
+          trolled or to turn off all syslog(3) messages from ddbbcclleeaann.  _L_e_v_e_l
+          must be a syslog(3) level among _E_M_E_R_G, _A_L_E_R_T, _C_R_I_T, _E_R_R, _W_A_R_N_I_N_G,
+          _N_O_T_I_C_E, _I_N_F_O, and _D_E_B_U_G.  _F_a_c_i_l_i_t_y must be among _A_U_T_H, _A_U_T_H_P_R_I_V,
+          _C_R_O_N, _D_A_E_M_O_N, _F_T_P, _K_E_R_N, _L_P_R, _M_A_I_L, _N_E_W_S, _U_S_E_R, _U_U_C_P, and _L_O_C_A_L_0
+          through _L_O_C_A_L_7.  The default is equivalent to
+                --LL _i_n_f_o_,_M_A_I_L_._N_O_T_I_C_E --LL _e_r_r_o_r_,_M_A_I_L_._E_R_R
+
+     ddbbcclleeaann exits 0 on success, and > 0 if an error occurs.
+
+FFIILLEESS
+     /var/dcc      is the DCC home directory containing data and control
+                   files.
+     dcc_db        is the main file containing mail checksums.
+     dcc_db.hash   mail checksum database hash table.
+     grey_db       is the database of greylist checksums.
+     grey_db.hash  is the greylist database hash table.
+     dcc_db-new, dcc_db-new.hash, grey_db-new, grey_db-new.hash
+                   new database and hash files until they are renamed.
+     dcc_db-old, grey_db-old
+                   previous database files.
+     ids           list of IDs and passwords, as described in dccd(8).
+     whitelist     contains the DCC server whitelist in the format described
+                   in dcc(8).
+     grey_whitelist
+                   contains the greylist server whitelist.
+
+SSEEEE AALLSSOO
+     cdcc(8), cron(8), dcc(8), dccd(8), dblist(8), dccifd(8), dccm(8),
+     dccproc(8).
+
+HHIISSTTOORRYY
+     Implementation of ddbbcclleeaann was started at Rhyolite Software, in 2000.
+     This document describes version 1.3.103.
+
+                               February 26, 2009
diff -r 000000000000 -r c7f6b056b673 dbclean.8.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbclean.8.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,325 @@
+.\" Copyright (c) 2008 by Rhyolite Software, LLC
+.\"
+.\" This agreement is not applicable to any entity which sells anti-spam
+.\" solutions to others or provides an anti-spam solution as part of a
+.\" security solution sold to other entities, or to a private network
+.\" which employs the DCC or uses data provided by operation of the DCC
+.\" but does not provide corresponding data to other users.
+.\"
+.\" Permission to use, copy, modify, and distribute this software without
+.\" changes for any purpose with or without fee is hereby granted, provided
+.\" that the above copyright notice and this permission notice appear in all
+.\" copies and any distributed versions or copies are either unchanged
+.\" or not called anything similar to "DCC" or "Distributed Checksum
+.\" Clearinghouse".
+.\"
+.\" Parties not eligible to receive a license under this agreement can
+.\" obtain a commercial license to use DCC by contacting Rhyolite Software
+.\" at sales@rhyolite.com.
+.\"
+.\" A commercial license would be for Distributed Checksum and Reputation
+.\" Clearinghouse software.  That software includes additional features.  This
+.\" free license for Distributed ChecksumClearinghouse Software does not in any
+.\" way grant permision to use Distributed Checksum and Reputation Clearinghouse
+.\" software
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+.\" 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.
+.\"
+.\" Rhyolite Software DCC 1.3.103-1.58 $Revision$
+.\"
+.Dd February 26, 2009
+.ds volume-ds-DCC Distributed Checksum Clearinghouse
+.Dt dbclean 8 DCC
+.Os " "
+.Sh NAME
+.Nm dbclean
+.Nd Clean Distributed Checksum Clearinghouse Database
+.Sh SYNOPSIS
+.Bk -words
+.Nm dbclean
+.Op Fl dfFNPSVq
+.Op Fl i Ar id
+.Oo
+.Fl a Xo
+.Sm off
+.Op Ar server-addr
+.Op Ar ,port
+.Xc
+.Sm on
+.Oc
+.Op Fl h Ar homedir
+.br
+.Op Fl G Ar on
+.Op Fl R Ar mode
+.Op Fl s Ar hash-size
+.Op Fl e Ar seconds
+.Op Fl E Ar spamsecs
+.br
+.Op Fl L Ar ltype,facility.level
+.Ek
+.Sh DESCRIPTION
+.Nm Dbclean
+creates empty, rebuilds corrupted, and deletes or expires old reports
+of checksums from DCC databases.
+It should be installed where it will be found with the path
+given the DCC server daemon when the daemon needs to expand the hash table.
+See
+.Xr dccd 8 .
+It should also be run by
+.Xr cron 8
+approximately daily.
+.Pp
+If the hash table in the database has been damaged,
+.Nm
+tries to repair the database.
+.Pp
+The contents of the
+.Pa whitelist
+file are built into the DCC server's database.
+Changes to the whitelist are not effective until dbclean is run
+to expire reports.
+White or blacklists can also be used by DCC clients,
+and generally work better there.
+.Ss OPTIONS
+The following options are available:
+.Bl -tag -width xxx
+.It Fl d
+enables debugging output.
+Additional
+.Fl d
+options increase the number of messages.
+.It Fl F
+uses write() instead of mmap() in some cases to modify the DCC
+database.
+This works better on some versions of Solaris provided the entire DCC
+database fits in RAM and provided the file system has not been tuned
+for the large, random accesses of a DCC database.
+It is the default on Solaris.
+.It Fl f
+turns off
+.Fl F .
+.It Fl N
+creates a new, empty database.
+There must not be an existing database and the DCC server,
+.Xr dccd 8 ,
+must not be running.
+.It Fl P
+expires old checksums from a database using the
+.Fl e and
+.Fl E
+values from the preceding use of
+.Nm dbclean .
+.Fl P
+cannot be used with
+.Fl e
+or
+.Fl E .
+Note also that using
+.Fl P
+differs from not using
+.Fl e
+or
+.Fl E ,
+because in the absence of all four, their default values are used.
+.It Fl S
+says that
+the DCC server,
+.Xr dccd 8 ,
+is not running and so
+.Nm
+should run stand-alone and not try to tell the DCC server about
+changes to the database.
+.Fl i
+is not needed when
+.Fl S
+is present.
+.It Fl V
+displays the version of the DCC database cleaner.
+.It Fl q
+quiets the announcement of results at the end.
+.It Fl i Ar id
+specifies the DCC ID recognized by the local DCC server as its own.
+This ID allows the DCC server to recognize commands from
+.Nm
+to stop using the database while it is being cleaned.
+.It Fl a Xo
+.Sm off
+.Op Ar server-addr
+.Op Ar ,port
+.Xc
+.Sm on
+is commonly used to specify a UDP port or IP address of the local
+server other than the default.
+.It Fl h Ar homedir
+overrides the default DCC home directory,
+.Pa @prefix@ .
+.It Fl G Ar on
+cleans a greylist database.
+.It Fl R Ar mode
+repairs a database or does a quick cleaning.
+.Ar Mode
+must be one of the following:
+.Bl -tag -width quick -compact
+.It Em bad
+to repair a broken database.
+.It Em quick
+for a quick, superficial cleaning during the day.
+.It Em hash
+to rebuild a hash not sent to disk before the system was recently restarted.
+.It Em cron
+for the nightly cleaning by the
+.Xr cron 8
+job
+.Pa @libexecdir@/cron-dccd
+.It Em del
+to finish processing a delete command received by
+.Xr dccd 8 .
+.El
+.It Fl s Ar hash-size
+specifies a size for the hash table.
+By default the hash table is rebuilt to be approximately 80% full based
+on an estimate of the number of distinct checksums in the main file.
+.It Fl e Ar seconds
+specifies that reports older than
+.Ar seconds
+and with totals below 10 targets should be deleted.
+Reports older than
+.Ar seconds
+of checksums that have been reported more recently
+are summarized in the database.
+The default value is 1DAY or the value of
+.Fl E ,
+whichever is smaller.
+The 1 day default is reduced if the system does not appear to have
+enough RAM to hold the database.
+The minimum is 1 hour.
+.Ar Seconds
+can also be
+.Ar NEVER
+or a number of hours, days, or weeks followed by
+.Ar HOURS ,
+.Ar H ,
+.Ar DAYS ,
+.Ar D ,
+.Ar WEEKS
+or
+.Ar W .
+.Pp
+DCC servers that are not very busy and are
+isolated or do not receive "floods" of checksums
+from busy servers should use longer values
+to increase their chances of recognizing bulk mail.
+.It Fl E Ar spamsecs
+changes the expiration of checksums with more than 10 targets
+from the default of 30DAYS or the explicit value of
+.Fl e ,
+whichever is larger.
+The default is reduced if the system does not have enough RAM
+to hold the database.
+.Ar Spamsecs
+can also be
+.Ar NEVER
+or a number of hours, days, or weeks followed by
+.Ar HOURS ,
+.Ar H ,
+.Ar DAYS ,
+.Ar D ,
+.Ar WEEKS
+or
+.Ar W .
+.It Fl L Ar ltype,facility.level
+specifies how messages should be logged.
+.Ar Ltype
+must be
+.Ar error ,
+.Ar info ,
+or
+.Ar off
+to indicate which of the two types of messages are being controlled or
+to turn off all
+.Xr syslog 3
+messages from
+.Nm .
+.Ar Level
+must be a
+.Xr syslog 3
+level among
+.Ar EMERG ,
+.Ar ALERT ,
+.Ar CRIT , ERR ,
+.Ar WARNING ,
+.Ar NOTICE ,
+.Ar INFO ,
+and
+.Ar DEBUG .
+.Ar Facility
+must be among
+.Ar AUTH ,
+.Ar AUTHPRIV ,
+.Ar CRON ,
+.Ar DAEMON ,
+.Ar FTP ,
+.Ar KERN ,
+.Ar LPR ,
+.Ar MAIL ,
+.Ar NEWS ,
+.Ar USER ,
+.Ar UUCP ,
+and
+.Ar LOCAL0
+through
+.Ar LOCAL7 .
+The default is equivalent to
+.Dl Fl L Ar info,MAIL.NOTICE  Fl L Ar error,MAIL.ERR
+.El
+.Pp
+.Nm
+exits 0 on success,
+and > 0 if an error occurs.
+.Sh FILES
+.Bl -tag -width grey_db.hash -compact
+.It Pa @prefix@
+is the DCC home directory containing data and control files.
+.It Pa dcc_db
+is the main file containing mail checksums.
+.It Pa dcc_db.hash
+mail checksum database hash table.
+.It Pa grey_db
+is the database of greylist checksums.
+.It Pa grey_db.hash
+is the greylist database hash table.
+.It Pa dcc_db-new , dcc_db-new.hash , grey_db-new , grey_db-new.hash
+new database and hash files until they are renamed.
+.It Pa dcc_db-old , grey_db-old
+previous database files.
+.It Pa ids
+list of IDs and passwords, as described in
+.Xr dccd 8 .
+.It Pa whitelist
+contains the DCC server whitelist in
+the format described in
+.Xr dcc 8 .
+.It Pa grey_whitelist
+contains the greylist server whitelist.
+.El
+.Sh SEE ALSO
+.Xr cdcc 8 ,
+.Xr cron 8 ,
+.Xr dcc 8 ,
+.Xr dccd 8 ,
+.Xr dblist 8 ,
+.Xr dccifd 8 ,
+.Xr dccm 8 ,
+.Xr dccproc 8 .
+.Sh HISTORY
+Implementation of
+.Nm
+was started at Rhyolite Software, in 2000.
+This document describes version 1.3.103.
diff -r 000000000000 -r c7f6b056b673 dbclean.html.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbclean.html.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,190 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<HTML>
+<HEAD>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <TITLE>dbclean.0.8</TITLE>
+    <META http-equiv="Content-Style-Type" content="text/css">
+    <STYLE type="text/css">
+	BODY {background-color:white; color:black}
+	ADDRESS {font-size:smaller}
+        IMG.logo {width:6em; vertical-align:middle}
+    </STYLE>
+</HEAD>
+<BODY>
+<PRE>
+<!-- Manpage converted by man2html 3.0.1 -->
+<B><A HREF="dbclean.html">dbclean(8)</A></B>            Distributed Checksum Clearinghouse            <B><A HREF="dbclean.html">dbclean(8)</A></B>
+
+
+</PRE>
+<H2><A NAME="NAME">NAME</A></H2><PRE>
+     <B>dbclean</B> -- Clean Distributed Checksum Clearinghouse Database
+
+
+</PRE>
+<H2><A NAME="SYNOPSIS">SYNOPSIS</A></H2><PRE>
+     <B>dbclean</B> [<B>-dfFNPSVq</B>] [<B>-i</B> <I>id</I>] [<B>-a</B> [<I>server-addr</I>][<I>,port</I>]] [<B>-h</B> <I>homedir</I>]
+             [<B>-G</B> <I>on</I>] [<B>-R</B> <I>mode</I>] [<B>-s</B> <I>hash-size</I>] [<B>-e</B> <I>seconds</I>] [<B>-E</B> <I>spamsecs</I>]
+             [<B>-L</B> <I>ltype,facility.level</I>]
+
+
+</PRE>
+<H2><A NAME="DESCRIPTION">DESCRIPTION</A></H2><PRE>
+     <B>Dbclean</B> creates empty, rebuilds corrupted, and deletes or expires old
+     reports of checksums from DCC databases.  It should be installed where it
+     will be found with the path given the DCC server daemon when the daemon
+     needs to expand the hash table.  See <B><A HREF="dccd.html">dccd(8)</A></B>.  It should also be run by
+     <B>cron(8)</B> approximately daily.
+
+     If the hash table in the database has been damaged, <B>dbclean</B> tries to
+     repair the database.
+
+     The contents of the <I>whitelist</I> file are built into the DCC server's data-
+     base.  Changes to the whitelist are not effective until dbclean is run to
+     expire reports.  White or blacklists can also be used by DCC clients, and
+     generally work better there.
+
+   <A NAME="OPTIONS"><B>OPTIONS</B></A>
+     The following options are available:
+
+     <A NAME="OPTION-d"><B>-d</B></A>   enables debugging output.  Additional <B>-d</B> options increase the number
+          of messages.
+
+     <A NAME="OPTION-F"><B>-F</B></A>   uses write() instead of mmap() in some cases to modify the DCC data-
+          base.  This works better on some versions of Solaris provided the
+          entire DCC database fits in RAM and provided the file system has not
+          been tuned for the large, random accesses of a DCC database.  It is
+          the default on Solaris.
+
+     <A NAME="OPTION-f"><B>-f</B></A>   turns off <B>-F</B>.
+
+     <A NAME="OPTION-N"><B>-N</B></A>   creates a new, empty database.  There must not be an existing data-
+          base and the DCC server, <B><A HREF="dccd.html">dccd(8)</A></B>, must not be running.
+
+     <A NAME="OPTION-P"><B>-P</B></A>   expires old checksums from a database using the <B>-e -and -E</B> values
+          from the preceding use of <B>dbclean</B>.  <B>-P</B> cannot be used with <B>-e</B> or <B>-E</B>.
+          Note also that using <B>-P</B> differs from not using <B>-e</B> or <B>-E</B>, because in
+          the absence of all four, their default values are used.
+
+     <A NAME="OPTION-S"><B>-S</B></A>   says that the DCC server, <B><A HREF="dccd.html">dccd(8)</A></B>, is not running and so <B>dbclean</B>
+          should run stand-alone and not try to tell the DCC server about
+          changes to the database.  <B>-i</B> is not needed when <B>-S</B> is present.
+
+     <A NAME="OPTION-V"><B>-V</B></A>   displays the version of the DCC database cleaner.
+
+     <A NAME="OPTION-q"><B>-q</B></A>   quiets the announcement of results at the end.
+
+     <A NAME="OPTION-i"><B>-i</B></A> <I>id</I>
+          specifies the DCC ID recognized by the local DCC server as its own.
+          This ID allows the DCC server to recognize commands from <B>dbclean</B> to
+          stop using the database while it is being cleaned.
+
+     <A NAME="OPTION-a"><B>-a</B></A> [<I>server-addr</I>][<I>,port</I>]
+          is commonly used to specify a UDP port or IP address of the local
+          server other than the default.
+
+     <A NAME="OPTION-h"><B>-h</B></A> <I>homedir</I>
+          overrides the default DCC home directory, <I>@prefix@</I>.
+
+     <A NAME="OPTION-G"><B>-G</B></A> <I>on</I>
+          cleans a greylist database.
+
+     <A NAME="OPTION-R"><B>-R</B></A> <I>mode</I>
+          repairs a database or does a quick cleaning.  <I>Mode</I> must be one of
+          the following:
+          <I>bad</I>    to repair a broken database.
+          <I>quick</I>  for a quick, superficial cleaning during the day.
+          <I>hash</I>   to rebuild a hash not sent to disk before the system was
+                 recently restarted.
+          <I>cron</I>   for the nightly cleaning by the <B>cron(8)</B> job
+                 <I>@libexecdir@/cron-dccd</I>
+          <I>del</I>    to finish processing a delete command received by <B><A HREF="dccd.html">dccd(8)</A></B>.
+
+     <A NAME="OPTION-s"><B>-s</B></A> <I>hash-size</I>
+          specifies a size for the hash table.  By default the hash table is
+          rebuilt to be approximately 80% full based on an estimate of the
+          number of distinct checksums in the main file.
+
+     <A NAME="OPTION-e"><B>-e</B></A> <I>seconds</I>
+          specifies that reports older than <I>seconds</I> and with totals below 10
+          targets should be deleted.  Reports older than <I>seconds</I> of checksums
+          that have been reported more recently are summarized in the data-
+          base.  The default value is 1DAY or the value of <B>-E</B>, whichever is
+          smaller.  The 1 day default is reduced if the system does not appear
+          to have enough RAM to hold the database.  The minimum is 1 hour.
+          <I>Seconds</I> can also be <I>NEVER</I> or a number of hours, days, or weeks fol-
+          lowed by <I>HOURS</I>, <I>H</I>, <I>DAYS</I>, <I>D</I>, <I>WEEKS</I> or <I>W</I>.
+
+          DCC servers that are not very busy and are isolated or do not
+          receive "floods" of checksums from busy servers should use longer
+          values to increase their chances of recognizing bulk mail.
+
+     <A NAME="OPTION-E"><B>-E</B></A> <I>spamsecs</I>
+          changes the expiration of checksums with more than 10 targets from
+          the default of 30DAYS or the explicit value of <B>-e</B>, whichever is
+          larger.  The default is reduced if the system does not have enough
+          RAM to hold the database.  <I>Spamsecs</I> can also be <I>NEVER</I> or a number of
+          hours, days, or weeks followed by <I>HOURS</I>, <I>H</I>, <I>DAYS</I>, <I>D</I>, <I>WEEKS</I> or <I>W</I>.
+
+     <A NAME="OPTION-L"><B>-L</B></A> <I>ltype,facility.level</I>
+          specifies how messages should be logged.  <I>Ltype</I> must be <I>error</I>, <I>info</I>,
+          or <I>off</I> to indicate which of the two types of messages are being con-
+          trolled or to turn off all <B>syslog(3)</B> messages from <B>dbclean</B>.  <I>Level</I>
+          must be a <B>syslog(3)</B> level among <I>EMERG</I>, <I>ALERT</I>, <I>CRIT</I>, <I>ERR</I>, <I>WARNING</I>,
+          <I>NOTICE</I>, <I>INFO</I>, and <I>DEBUG</I>.  <I>Facility</I> must be among <I>AUTH</I>, <I>AUTHPRIV</I>,
+          <I>CRON</I>, <I>DAEMON</I>, <I>FTP</I>, <I>KERN</I>, <I>LPR</I>, <I>MAIL</I>, <I>NEWS</I>, <I>USER</I>, <I>UUCP</I>, and <I>LOCAL0</I>
+          through <I>LOCAL7</I>.  The default is equivalent to
+                <B>-L</B> <I>info,MAIL.NOTICE</I> <B>-L</B> <I>error,MAIL.ERR</I>
+
+     <B>dbclean</B> exits 0 on success, and &gt; 0 if an error occurs.
+
+
+</PRE>
+<H2><A NAME="FILES">FILES</A></H2><PRE>
+     <A NAME="FILE-@prefix@">@prefix@</A>      is the DCC home directory containing data and control
+                   files.
+     <A NAME="FILE-dcc_db">dcc_db</A>        is the main file containing mail checksums.
+     <A NAME="FILE-dcc_db.hash">dcc_db.hash</A>   mail checksum database hash table.
+     <A NAME="FILE-grey_db">grey_db</A>       is the database of greylist checksums.
+     <A NAME="FILE-grey_db.hash">grey_db.hash</A>  is the greylist database hash table.
+     <A NAME="FILE-dcc_db">dcc_db</A>-new, dcc_db-new.hash, grey_db-new, grey_db-new.hash
+                   new database and hash files until they are renamed.
+     <A NAME="FILE-dcc_db">dcc_db</A>-old, grey_db-old
+                   previous database files.
+     <A NAME="FILE-ids">ids</A>           list of IDs and passwords, as described in <B><A HREF="dccd.html">dccd(8)</A></B>.
+     <A NAME="FILE-whitelist">whitelist</A>     contains the DCC server whitelist in the format described
+                   in <B><A HREF="dcc.html">dcc(8)</A></B>.
+     <A NAME="FILE-grey_whitelist">grey_whitelist</A>
+                   contains the greylist server whitelist.
+
+
+</PRE>
+<H2><A NAME="SEE-ALSO">SEE ALSO</A></H2><PRE>
+     <B><A HREF="cdcc.html">cdcc(8)</A></B>, <B>cron(8)</B>, <B><A HREF="dcc.html">dcc(8)</A></B>, <B><A HREF="dccd.html">dccd(8)</A></B>, <B><A HREF="dblist.html">dblist(8)</A></B>, <B><A HREF="dccifd.html">dccifd(8)</A></B>, <B><A HREF="dccm.html">dccm(8)</A></B>,
+     <B><A HREF="dccproc.html">dccproc(8)</A></B>.
+
+
+</PRE>
+<H2><A NAME="HISTORY">HISTORY</A></H2><PRE>
+     Implementation of <B>dbclean</B> was started at Rhyolite Software, in 2000.
+     This document describes version 1.3.103.
+
+                               February 26, 2009
+</PRE>
+<HR>
+<ADDRESS>
+Man(1) output converted with
+<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
+modified for the DCC $Date 2001/04/29 03:22:18 $
+<BR>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG SRC="http://logos.dcc-servers.net/border.png"
+            class=logo ALT="DCC logo">
+    </A>
+<A HREF="http://validator.w3.org/check?uri=referer">
+    <IMG class=logo ALT="Valid HTML 4.01 Strict"
+        SRC="http://www.w3.org/Icons/valid-html401">
+    </A>
+</ADDRESS>
+</BODY>
+</HTML>
diff -r 000000000000 -r c7f6b056b673 dbclean/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbclean/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,3 @@
+Makefile.in
+dbclean.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 dbclean/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbclean/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,50 @@
+# make the Distributed Checksum Clearinghouse database cleaner
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.13 $Revision$
+# @configure_input@
+
+DEPTH	=..
+PROG	=dbclean
+SRCS	=$(PROG).c
+
+CFLAGS	+=$(SRVRINC)
+LDADD	+=$(SRVRLIBS)
+DPADD	+=$(SRVRLIBS)
+
+DCC_BINDIR=@installroot@@libexecdir@
+@MAKE_PROG@
+@MAKE_INC2@
diff -r 000000000000 -r c7f6b056b673 dbclean/dbclean.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbclean/dbclean.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,2789 @@
+/* Distributed Clearinghouse Checksum database cleaner
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.254 $Revision$
+ */
+
+#include "srvr_defs.h"
+#include "dcc_ck.h"
+#include <signal.h>
+
+static DCC_EMSG dcc_emsg;
+
+static DCC_WF dbclean_wf;
+static DCC_WHITE_TBL dbclean_white_tbl;
+static DCC_CLNT_CTXT *ctxt;
+static DCC_OP_RESP aop_resp;
+static int flods_off;
+static int dccd_unlocked;		/* dccd has been told to unlock	*/
+
+static DCC_SRVR_NM srvr = DCC_SRVR_NM_DEF;
+static DCC_CLNT_ID srvr_clnt_id = DCC_ID_INVALID;
+static const ID_TBL *srvr_clnt_tbl;
+static u_char info_flags = 0;
+#ifdef USE_DBCLEAN_F
+static u_char db_mode = DB_OPEN_MMAP_WRITE;
+#else
+static u_char db_mode = DB_OPEN_MMAP_WRITE_NOSYNC;
+#endif
+
+static u_char cleardb;			/* 1=clear the database */
+static enum {
+    NORMAL_MODE,
+    REPAIR_MODE,			/* database broken */
+    QUICK_MODE,				/* too big for window */
+    HASH_MODE,				/* hash table full */
+    NO_CRON_MODE,			/* work around missing cron job */
+    DEL_MODE				/* after deletion */
+} clean_mode = NORMAL_MODE;
+static u_char standalone;		/* 1=don't talk to dccd */
+static u_char keep_white;		/* 1=do not rebuild whitelist */
+
+static int exit_value = -1;
+
+static const char *homedir;
+static u_char cur_db_created;
+static const char *cur_db_nm_str = DB_DCC_NAME;
+static DCC_PATH cur_db_nm;
+static DCC_PATH cur_hash_nm;
+static int old_db_fd = -1;
+static DB_HADDR old_db_hash_used;
+static DB_PARMS old_db_parms;
+static DB_PARMS new_db_parms;
+static DB_PTR old_db_pos,  new_db_csize;
+static off_t new_db_fsize;
+static u_int new_db_pagesize;
+static FLOD_MMAPS new_flod_mmaps;
+static u_char adj_delay_pos;
+static u_char new_db_created;
+static DCC_PATH new_db_nm;
+static int new_db_fd = -1;
+static u_char new_hash_created;
+static DCC_PATH new_hash_nm;
+static DCC_PATH old_db_nm;
+
+static int expire_secs = -1;
+static int def_expire_secs = DB_EXPIRE_SECS_DEF;
+static int expire_spamsecs = -1;
+static int def_expire_spamsecs = DB_EXPIRE_SPAMSECS_DEF;
+static int have_expire_parms = 0;
+static double def_exp_ratio = 0.0;
+static DB_EX_SECS new_ex_secs;
+static DB_EX_TS new_ex_ts;
+
+static DB_HADDR new_hash_len;
+
+static int expired_rcds, comp_rcds, obs_rcds, expired_cks;
+static int white_cks, kept_cks;
+
+static DCC_TS future_ts;
+
+#define RESTART_DELAY	(60*5)
+#define SHORT_DELAY	30
+
+static struct timeval clean_start;
+
+static struct timeval progress_rpt_last;    /* when previous progress report */
+static struct timeval progress_rpt_checked; /* when last checked */
+static struct timeval progress_rpt_start;   /* start of progress reporting */
+#define REPORT_INTERVAL_SECS	    (5*60)
+#define REPORT_INTERVAL_FAST_SECS   10
+#define	UNLOCK_INTERVAL_USECS	    (DCC_US/2)
+static int progress_rpt_cnt;		/* operations until next check */
+static int progress_rpt_base;
+static u_char progress_rpt_started;	/* 1=have started reporting progress */
+static int progress_rpt_percent;
+
+static u_char write_new_flush(u_char);
+static u_char write_new_rcd(const void *, int);
+static void write_new_hdr(u_char);
+static void unlink_whine(const char *, u_char);
+static void rename_bail(const char *, const char *);
+static u_char expire(DB_PTR);
+static u_char copy_db(void);
+static u_char catchup(DCC_EMSG);
+static void parse_white(void);
+static void build_hash(void);
+static u_char persist_aop(DCC_AOPS, u_int32_t, int);
+static void dccd_new_db(const char *);
+static void finish(void);
+static void exit_dbclean(int) NRATTRIB;
+static void sigterm(int);
+
+
+static void
+usage(u_char die)
+{
+	const char str[] = {
+		"usage: [-64dfFNPSVq] [-i id]"
+		" [-a [server-addr][,server-port]] [-h homedir]\n"
+		"   [-G on] [-R mode] [-s hash-size] [-e seconds]"
+		" [-E spamsecs]\n"
+		"   [-L ltype,facility.level]"};
+	static u_char complained;
+
+	/* its important to try to run, so don't give up unless necessary */
+	if (die) {
+		dcc_logbad(EX_USAGE, complained ? "giving up" : str);
+	} else if (!complained) {
+		dcc_error_msg("%s\ncontinuing", str);
+		complained = 1;
+	}
+}
+
+
+int NRATTRIB
+main(int argc, char **argv)
+{
+	char hostname[DCC_MAXDOMAINLEN];
+	u_char print_version = 0;
+	struct stat cur_db_sb;
+	u_int tgt_db_pagesize;
+	const char *cp;
+	char *p;
+	u_long l;
+	int i;
+
+	gettimeofday(&db_time, 0);
+	clean_start = db_time;
+
+	dcc_timeval2ts(&future_ts, &clean_start, 24*60*60);
+
+	dcc_syslog_init(1, argv[0], 0);
+
+	/* this must match DBCLEAN_GETOPTS in cron-dccd.in */
+	while ((i = getopt(argc, argv, "64dfFNPSVqi:a:h:G:R:s:e:E:L:")) != -1) {
+		switch (i) {
+		case '6':
+#ifndef NO_IPV6
+			info_flags = DCC_INFO_FG_IPV6;
+#endif
+			break;
+		case '4':
+			info_flags = 0;
+			break;
+
+		case 'd':
+			if (db_debug++)
+				++dcc_clnt_debug;
+			break;
+
+		case 'f':
+			db_mode &= ~DB_OPEN_MMAP_WRITE;
+			break;
+
+
+		case 'F':
+			db_mode |= DB_OPEN_MMAP_WRITE;
+			break;
+
+		case 'N':		/* make a new, clear database */
+			cleardb = 1;
+			standalone = 1;
+			break;
+
+		case 'P':
+			if (have_expire_parms > 0)
+				dcc_logbad(EX_USAGE,
+					   "do not use -P with -e or -E");
+			have_expire_parms = -1;
+			break;
+
+		case 'S':
+			standalone = 1;
+			break;
+
+		case 'V':
+			fprintf(stderr, DCC_VERSION"\n");
+			print_version = 1;
+			break;
+
+		case 'q':
+			trace_quiet = 1;
+			break;
+
+		case 'i':
+			l = strtoul(optarg, &p, 10);
+			if (*p != '\0'
+			    || l < DCC_SRVR_ID_MIN
+			    || l > DCC_SRVR_ID_MAX)
+				dcc_logbad(EX_USAGE, "invalid DCC ID \"-i %s\"",
+					   optarg);
+			srvr_clnt_id = l;
+			break;
+
+		case 'a':
+			cp = dcc_parse_nm_port(dcc_emsg, optarg, srvr.port,
+					       hostname, sizeof(hostname),
+					       &srvr.port, 0, 0, 0, 0);
+			if (!cp) {
+				dcc_error_msg("%s", dcc_emsg);
+				break;
+			}
+			cp += strspn(cp, DCC_WHITESPACE);
+			if (*cp != '\0') {
+				dcc_error_msg("unrecognized port number in"
+					      "\"-a %s\"", optarg);
+				break;
+			}
+			if (hostname[0] == '\0')
+				strcpy(srvr.hostname, DCC_SRVR_NM_DEF_HOST);
+			else
+				BUFCPY(srvr.hostname, hostname);
+			break;
+
+		case 'h':
+			homedir = optarg;
+			break;
+
+		case 'G':
+			dcc_syslog_init(1, argv[0], " grey");
+			if (have_expire_parms > 0)
+				dcc_logbad(EX_USAGE,
+					   "do not use -G with -e or -E");
+			if (strcasecmp(optarg, "on"))
+				usage(0);   /* be generous and allow -Gasdf */
+			grey_on = 1;
+			have_expire_parms = -1;
+			cur_db_nm_str = DB_GREY_NAME;
+			break;
+
+		case 'R':
+			if (!strcasecmp(optarg, "bad"))
+				clean_mode = REPAIR_MODE;
+			else if (!strcasecmp(optarg, "quick"))
+				clean_mode = QUICK_MODE;
+			else if (!strcasecmp(optarg, "hash"))
+				clean_mode = HASH_MODE;
+			else if (!strcasecmp(optarg, "cron"))
+				clean_mode = NO_CRON_MODE;
+			else if (!strcasecmp(optarg, "del"))
+				clean_mode = DEL_MODE;
+			else
+				dcc_logbad(EX_USAGE,
+					   "unrecognized repair mode -R %s",
+					   optarg);
+			break;
+
+		case 's':		/* hash table size in entries */
+			new_hash_len = strtoul(optarg, &p, 0);
+			if (*p != '\0'
+			    || new_hash_len < MIN_HASH_ENTRIES
+			    || new_hash_len > MAX_HASH_ENTRIES)
+				dcc_logbad(EX_USAGE,
+					   "invalid database size \"%s\"",
+					   optarg);
+			break;
+
+		case 'e':		/* expiration for non-bulk checksums */
+			if (grey_on)
+				dcc_logbad(EX_USAGE,
+					   "do not use -e with -G");
+			if (have_expire_parms < 0)
+				dcc_logbad(EX_USAGE,
+					   "-e cannot be used with -P");
+			have_expire_parms = 1;
+			expire_secs = dcc_get_secs(optarg, 0,
+						   DB_EXPIRE_SECS_MIN,
+						   DB_EXPIRE_SECS_MAX, -1);
+			if (expire_secs < 0)
+				dcc_logbad(EX_USAGE,
+					   "invalid expiration seconds"
+					   " \"-e %s\"",
+					   optarg);
+			break;
+
+		case 'E':		/* expiration for bulk checksums */
+			if (grey_on)
+				dcc_logbad(EX_USAGE,
+					   "do not use -E with -G");
+			if (have_expire_parms < 0)
+				dcc_logbad(EX_USAGE,
+					   "do not use -E with -P");
+			have_expire_parms = 1;
+			expire_spamsecs = dcc_get_secs(optarg, 0,
+						       DB_EXPIRE_SECS_MIN,
+						       DB_EXPIRE_SECS_MAX, -1);
+			if (expire_spamsecs < 0)
+				dcc_logbad(EX_USAGE,
+					   "invalid spam expiration seconds"
+					   " \"-E %s\"",
+					   optarg);
+			break;
+
+		case 'L':
+			dcc_parse_log_opt(optarg);
+			break;
+
+		default:
+			usage(0);
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	if (argc != 0)
+		usage(1);
+
+	if (srvr_clnt_id == DCC_ID_INVALID && !standalone) {
+		if (print_version)
+			exit(EX_OK);
+		usage(1);
+	}
+	srvr.clnt_id = srvr_clnt_id;
+
+	if (srvr.port == 0)
+		srvr.port = DCC_GREY2PORT(grey_on);
+
+	dcc_clnt_unthread_init();
+	/* move to the target directory
+	 * and set homedir for fnm2rel_good() */
+	if (!dcc_cdhome(dcc_emsg, homedir, 0))
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+	fnm2rel_good(cur_db_nm, cur_db_nm_str, 0);
+	cp = "";
+	switch (clean_mode) {
+	case NORMAL_MODE: cp = "cleaning"; break;
+	case REPAIR_MODE: cp = "repairing"; break;
+	case QUICK_MODE: cp = "quick cleaning"; break;
+	case HASH_MODE: cp = "expanding hash table in"; break;
+	case NO_CRON_MODE: cp = "work around missing cron job for"; break;
+	case DEL_MODE: cp = "clean up deletion in"; break;
+	}
+	quiet_trace_msg(DCC_VERSION" %s %s", cp, fnm2abs_err(0, cur_db_nm));
+
+	atexit(finish);
+	signal(SIGHUP, sigterm);
+	signal(SIGTERM, sigterm);
+	signal(SIGINT, sigterm);
+#ifdef SIGXFSZ
+	signal(SIGXFSZ, SIG_IGN);
+#endif
+
+	if (!standalone) {
+		i = load_ids(dcc_emsg, srvr_clnt_id, &srvr_clnt_tbl, 1);
+		if (i <= 0)
+			dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+		memcpy(srvr.passwd, srvr_clnt_tbl->cur_passwd,
+		       sizeof(srvr.passwd));
+	}
+
+	fnm2rel_good(cur_hash_nm, cur_db_nm, DB_HASH_SUFFIX);
+	fnm2rel_good(old_db_nm, cur_db_nm, "-old");
+	fnm2rel_good(new_db_nm, cur_db_nm, "-new");
+	fnm2rel_good(new_hash_nm, new_db_nm, DB_HASH_SUFFIX);
+
+	/* exclude other instances of this program */
+	if (!lock_dbclean(dcc_emsg, cur_db_nm))
+		dcc_logbad(dcc_ex_code, "%s: dbclean already running?",
+			   dcc_emsg);
+
+	/* create & the lock new database file */
+	new_db_fd = dcc_lock_open(dcc_emsg, new_db_nm, O_RDWR|O_CREAT,
+				  DCC_LOCK_OPEN_NOWAIT, DCC_LOCK_ALL_FILE, 0);
+	if (new_db_fd == -1)
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+	if (0 > ftruncate(new_db_fd, 0))
+		dcc_logbad(EX_IOERR, "truncate(%s,0): %s",
+			   new_db_nm, ERROR_STR());
+	new_db_fsize = 0;
+	new_db_created = 1;
+	new_db_csize = DB_PTR_BASE;
+
+	tgt_db_pagesize = 0;
+	if (0 > stat(cur_db_nm, &cur_db_sb)) {
+		if (errno != ENOENT)
+			dcc_logbad(EX_IOERR, "stat(%s): %s",
+				   cur_db_nm, ERROR_STR());
+		/* empty a missing database */
+		cleardb = 1;
+	} else if (cur_db_sb.st_size == 0) {
+		/* empty an empty database */
+		cleardb = 1;
+	} else if (grey_on && cur_db_sb.st_size < DB_MIN_MIN_MBYTE*1024*1024) {
+		/* Force a relatively large page size for typical tiny
+		 * greylist databases.  Try to use few mmap() pages */
+		tgt_db_pagesize = cur_db_sb.st_size/4;
+		if (tgt_db_pagesize < MIN_HASH_ENTRIES*sizeof(HASH_ENTRY))
+			tgt_db_pagesize = MIN_HASH_ENTRIES*sizeof(HASH_ENTRY);
+	}
+	new_db_pagesize = db_get_pagesize(0, tgt_db_pagesize);
+	write_new_hdr(1);
+
+
+	if (standalone) {
+		u_char busy;
+
+		/* open and lock the current database to ensure
+		 * the daemon is not running */
+		old_db_fd = dcc_lock_open(dcc_emsg, cur_db_nm, O_RDWR,
+					  DCC_LOCK_OPEN_NOWAIT,
+					  DCC_LOCK_ALL_FILE, &busy);
+		if (busy)
+			dcc_logbad(EX_USAGE, "database %s in use: %s",
+				   cur_db_nm, dcc_emsg);
+		if (cleardb
+		    && stat(cur_db_nm, &cur_db_sb) >= 0) {
+			if (cur_db_sb.st_size != 0)
+				dcc_logbad(EX_USAGE, "%s already exists",
+					   cur_db_nm);
+			cur_db_created = 1;
+		}
+
+		/* create and lock the current database if it did not exist
+		 * to ensure that the server daemon is not running */
+		if (old_db_fd < 0) {
+			old_db_fd = dcc_lock_open(dcc_emsg, cur_db_nm,
+						  O_RDWR|O_CREAT,
+						  DCC_LOCK_OPEN_NOWAIT,
+						  DCC_LOCK_ALL_FILE, 0);
+			if (old_db_fd < 0)
+				dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+			cur_db_created = 1;
+		}
+
+	} else {
+		/* Tell the daemon to start turning off the flooding
+		 * so we can adjust its positions in the flood map file
+		 * Try very hard to talk to it because releasing the database
+		 * can cause some UNIX flavors to stall dccd. */
+		DCC_CLNT_FGS clnt_fgs;
+
+		clnt_fgs = DCC_CLNT_FG_SLOW;
+		if (grey_on)
+			clnt_fgs |= DCC_CLNT_FG_GREY;
+		ctxt = dcc_tmp_clnt_init(dcc_emsg, 0, &srvr,
+					 0, clnt_fgs, info_flags);
+		/* try very hard to contact dccd */
+		if (!ctxt)
+			ctxt = dcc_tmp_clnt_init(dcc_emsg, 0, &srvr,
+						 0, clnt_fgs, info_flags);
+		if (!ctxt)
+			dcc_logbad(EX_DCC_RESTART, "initial contact: %s",
+				   dcc_emsg);
+
+		++flods_off;
+		if (!persist_aop(DCC_AOP_FLOD, DCC_AOP_FLOD_SHUTDOWN,
+				 SHORT_DELAY))
+			dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+	}
+
+	/* resolve whitelisted host names before locking the database */
+	parse_white();
+
+	/* Tell the daemon to unlock the database between operations
+	 * and insist it stop flooding. */
+	if (!standalone) {
+		/* give the daemon a chance to stop pumping the floods */
+		for (;;) {
+			if (!persist_aop(DCC_AOP_FLOD, DCC_AOP_FLOD_CHECK,
+					 SHORT_DELAY))
+				dcc_logbad(EX_UNAVAILABLE, "%s", dcc_emsg);
+
+			i = flod_running(aop_resp.resp.val.string);
+			if (i < 0)
+				dcc_logbad(EX_PROTOCOL,
+					   "%s: unrecognized \"%s\"",
+					   dcc_aop2str(0, 0,
+						       DCC_AOP_FLOD,
+						       DCC_AOP_FLOD_CHECK),
+					   aop_resp.resp.val.string);
+			if (i == 0)
+				break;
+			if (time(0) > clean_start.tv_sec+45) {
+				if (flods_off < 2) {
+					++flods_off;
+					if (!persist_aop(DCC_AOP_FLOD,
+							DCC_AOP_FLOD_HALT,
+							SHORT_DELAY))
+					    dcc_logbad(dcc_ex_code, "%s",
+						       dcc_emsg);
+					continue;
+				}
+				if (time(0) > clean_start.tv_sec+60)
+					dcc_logbad(EX_UNAVAILABLE,
+						   "failed to stop floods: %s",
+						   aop_resp.resp.val.string);
+			}
+			usleep(100*1000);
+		}
+		dccd_unlocked = 1;
+		if (!persist_aop(DCC_AOP_DB_CLEAN, 0, SHORT_DELAY))
+			dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+		/* The daemon adds its own and removes our hold on flooding
+		 * when we tell it to unlock the database after every
+		 * operation. */
+		--flods_off;
+	}
+
+	if (cleardb) {
+		quiet_trace_msg(DCC_VERSION" %s database %s",
+				cur_db_created ? "creating" : "clearing",
+				cur_db_nm);
+
+	} else if (clean_mode == REPAIR_MODE) {
+		dcc_error_msg("explicit repair of %s", cur_db_nm);
+
+	} else {
+		if (!db_open(0, old_db_fd, cur_db_nm, 0,
+			     DB_OPEN_RDONLY
+			     | (standalone
+				? DB_OPEN_LOCK_NOWAIT : DB_OPEN_LOCK_WAIT))) {
+			/* If the hash table is sick, check timestamps only
+			 * as much as no hash table allows.
+			 * Then rebuild the hash table. */
+			clean_mode = REPAIR_MODE;
+
+		} else {
+			if (db_debug) {
+				quiet_trace_msg("%s  %s",
+						db_window_size_str, new_db_nm);
+				quiet_trace_msg("%d old hash entries total,"
+						" %d or %d%% used",
+						HADDR2LEN(db_hash_len),
+						HADDR2LEN(db_hash_used),
+						(int)((HADDR2LEN(db_hash_used)
+						       * 100.0)
+						      /HADDR2LEN(db_hash_len)));
+			}
+			old_db_parms = db_parms;
+			old_db_hash_used = db_hash_used;
+
+			/* save a handle on the old database to get
+			 * reports that arrive while we expire it */
+			old_db_fd = dup(db_fd);
+			if (old_db_fd < 0)
+				dcc_logbad(EX_OSERR, "dup(%s): %s",
+					   cur_db_nm, ERROR_STR());
+
+			/* read old and create new database file */
+			if (!expire(db_csize)) {
+				old_db_hash_used = 0;
+				clean_mode = REPAIR_MODE;
+			}
+		}
+
+		if (clean_mode == REPAIR_MODE)
+			dcc_error_msg("repairing %s", cur_db_nm);
+	}
+
+	/* if we are repairing the hash table (including now repairing
+	 * after encountering problems while expiring),
+	 * copy the current file with minimal expiring */
+	if (clean_mode == REPAIR_MODE
+	    && !cleardb
+	    && !copy_db())
+		exit_dbclean(EX_UNAVAILABLE);
+	build_hash();
+
+	/* Copy any records from the old file to the new file that were
+	 * added to the old file while we were creating the new file. */
+	if (!cleardb
+	    && !catchup(dcc_emsg))
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+
+	/* we have the new database locked
+	 *
+	 * preserve the current data file as "*-old" */
+	rename_bail(cur_db_nm, old_db_nm);
+
+	/* delete the current hash file, and install both new files */
+	rename_bail(new_hash_nm, cur_hash_nm);
+	strcpy(new_hash_nm, cur_hash_nm);
+	new_hash_created = 0;
+	if (db_hash_fd >= 0)
+		strcpy(db_hash_nm, cur_hash_nm);
+
+	rename_bail(new_db_nm, cur_db_nm);
+	strcpy(new_db_nm, cur_db_nm);
+	new_db_created = 0;
+	if (db_fd > 0)
+		strcpy(db_nm, cur_db_nm);
+	cur_db_created = 0;
+
+	if (cleardb) {
+		flod_mmap_path_set();
+		unlink_whine(flod_mmap_path, 1);
+		if (!db_close(1))
+			exit_dbclean(EX_UNAVAILABLE);
+		exit_dbclean(EX_OK);
+	}
+
+	/* if the daemon was not running, we're finished */
+	if (standalone) {
+		/* install the flood positions if things are ok */
+		if (flod_mmaps) {
+			memcpy(flod_mmaps, &new_flod_mmaps,
+			       sizeof(new_flod_mmaps));
+			flod_unmap(0, 0);
+		}
+		if (!db_close(1))
+			exit_dbclean(EX_UNAVAILABLE);
+		exit_dbclean(EX_OK);
+	}
+
+	/* tell the daemon to switch to the new database.  This will leave
+	 * the daemon stuck waiting for us to unlock the new database. */
+	dccd_new_db("copy late arrivals");
+
+	/* install the flood positions if things are ok */
+	if (flod_mmaps) {
+		memcpy(flod_mmaps, &new_flod_mmaps,
+		       sizeof(new_flod_mmaps));
+		flod_unmap(0, 0);
+	}
+
+	/* Copy any records from the old file to the new file in the
+	 * race to tell the daemon to switch to the new file.
+	 * The new file is still locked from build_hash().
+	 * The daemon should be stuck waiting to open it in the
+	 * DCC_AOP_DB_NEW request via the preceding dccd_new_db().
+	 *
+	 * Since the daemon has switched and probably cannot go back,
+	 * ignore any errors */
+	catchup(0);
+	if (!db_close(1))
+		exit_dbclean(EX_UNAVAILABLE);
+
+	/* finish() will be called via exit() to tell the daemon to resume
+	 * flooding if necessary.  However, in the normal case, we removed
+	 * all counts against flooding before calling dccd_new_db() */
+	exit_dbclean(EX_OK);
+}
+
+
+
+/* adjust output flood positions */
+static DB_PTR
+adj_mmap(void)
+{
+	FLOD_MMAP *mp;
+	DB_PTR delta, new_pos;
+
+	delta = new_db_csize - old_db_pos;
+	new_pos = 0;
+	for (mp = new_flod_mmaps.mmaps;
+	     mp <= LAST(new_flod_mmaps.mmaps);
+	     ++mp) {
+		/* do nothing to marks we have already adjusted */
+		if (!(mp->flags & FLODMAP_FG_MARK))
+			continue;
+		if (mp->confirm_pos > old_db_pos) {
+			/* note the next mark that will need adjusting
+			 * but do not adjust it yet */
+			if (new_pos == 0
+			    || new_pos > mp->confirm_pos)
+				new_pos = mp->confirm_pos;
+		} else {
+			/* adjust marks not past the current position */
+			mp->confirm_pos += delta;
+			mp->flags &= ~FLODMAP_FG_MARK;
+		}
+	}
+	if (adj_delay_pos) {
+		if (new_flod_mmaps.delay_pos > old_db_pos) {
+			if (new_pos == 0
+			    || new_pos > new_flod_mmaps.delay_pos)
+				new_pos = new_flod_mmaps.delay_pos;
+		} else {
+			new_flod_mmaps.delay_pos += delta;
+			adj_delay_pos = 0;
+		}
+	}
+
+	return new_pos;
+}
+
+
+
+/* find a checksum
+ *	Leave db_sts.rcd2 pointing at the record. */
+static u_char
+get_ck(DB_RCD_CK **ckp,			/* point this to the checksum */
+       DCC_CK_TYPES type, const DCC_SUM sum)
+{
+	DB_FOUND db_result;
+
+	/* We must lock the file to keep the daemon from changing the
+	 * internal hash table links. */
+	if (!DB_IS_LOCKED()
+	    && 0 > db_lock())
+		return 0;
+
+	dcc_emsg[0] = '\0';
+	db_result = db_lookup(dcc_emsg, type, sum, 0, MAX_HASH_ENTRIES,
+			      &db_sts.hash, &db_sts.rcd2, ckp);
+	switch (db_result) {
+	case DB_FOUND_LATER:
+	case DB_FOUND_SYSERR:
+		dcc_error_msg("hash lookup for %s from "L_HPAT" = %d: %s",
+			      DB_TYPE2STR(type), old_db_pos, db_result,
+			      dcc_emsg);
+		break;
+
+	case DB_FOUND_IT:
+	case DB_FOUND_EMPTY:
+	case DB_FOUND_CHAIN:
+	case DB_FOUND_INTRUDER:
+		return 1;
+	}
+
+	return 0;
+}
+
+
+
+/* check the leading report for not recent checksum
+ *	on entry db_sts.rcd points to the record under consideration
+ *	Leave db_sts.rcd2 pointing at the leading record. */
+static int				/* -1=broken database 0=expire 1=keep */
+get_lead(DCC_CK_TYPES type, const DB_RCD_CK *rcd_ck)
+{
+	DB_RCD_CK *lead_ck;
+	DCC_TGTS rcd_tgts, lead_tgts;
+
+	if (DCC_CK_IS_REP_CMN(grey_on, type)) {
+		/* do not keep reputations on systems without reputation code */
+		return 0;
+	}
+
+	if (!get_ck(&lead_ck, type, rcd_ck->sum))
+		return -1;
+
+	if (!lead_ck) {
+		dcc_error_msg("no leader for %s %s at "L_HPAT,
+			      DB_TYPE2STR(type),
+			      dcc_ck2str_err(type, rcd_ck->sum, 0),
+			      old_db_pos);
+		return -1;
+	}
+
+	/* We know the target checksum is not recent.  Forget the target if
+	 * both the target and the leader are ancient. The leader might not be
+	 * the newest checksum, but it usually is. Note also that the target
+	 * might be the leader. */
+	if (dcc_ts_older_ts(&db_sts.rcd2.d.r->ts, &new_ex_ts[type].spam)
+	    && dcc_ts_older_ts(&db_sts.rcd.d.r->ts, &new_ex_ts[type].spam))
+		return 0;
+
+	lead_tgts = DB_TGTS_CK(lead_ck);
+	rcd_tgts = DB_TGTS_CK(rcd_ck);
+
+	/* We know either the leader or the target is not ancient.
+	 * Keep the target if the leader's total is respectable.
+	 * We might eventually compress the target. */
+	return (lead_tgts >= db_tholds[type]);
+}
+
+
+
+static void
+report_progress_init(void)
+{
+	gettimeofday(&db_time, 0);
+	progress_rpt_start.tv_sec = db_time.tv_sec;
+	progress_rpt_checked = db_time;
+	progress_rpt_last = db_time;
+	progress_rpt_base = 100;
+	progress_rpt_cnt = progress_rpt_base;
+	progress_rpt_started = 0;
+}
+
+
+
+static time_t				/* us since last check */
+report_progress(u_char final,
+		const char *s1, const char *s2,
+		DB_PTR done, DB_PTR total, DB_PTR scale)
+{
+	time_t reported_us, checked_us, secs, interval;
+	double percent;
+
+	if (!total)
+		percent = 100.0;
+	else
+		percent = (done*100.0)/total;
+
+	gettimeofday(&db_time, 0);
+	checked_us = tv_diff2us(&db_time, &progress_rpt_checked);
+	progress_rpt_checked = db_time;
+
+	/* Check frequently enough to report or unlock the database.
+	 * Adjust the number of operations until the next check
+	 * based on the time spent on the previous */
+	if (checked_us > 0)
+		progress_rpt_base = ((progress_rpt_base * 0.5 * DCC_US
+				      * min(REPORT_INTERVAL_FAST_SECS*DCC_US,
+					    UNLOCK_INTERVAL_USECS))
+				     / checked_us);
+	else
+		progress_rpt_base = 100;
+	if (progress_rpt_base < 100)
+		progress_rpt_base = 100;
+	if (progress_rpt_base > 10*1000)
+		progress_rpt_base = 10*1000;
+	progress_rpt_cnt = progress_rpt_base;
+
+	interval = ((db_debug > 1)
+		    ? REPORT_INTERVAL_FAST_SECS
+		    : REPORT_INTERVAL_SECS);
+
+	/* try not to start reporting progress at the end */
+	if (!progress_rpt_started
+	    && (total*1.0 - done*1.0) / progress_rpt_base <= interval*1.0)
+		return checked_us;
+
+	reported_us = tv_diff2us(&db_time, &progress_rpt_last);
+	if (reported_us >= interval * DCC_US
+	    || (final && progress_rpt_percent != 100)) {
+		progress_rpt_started = 1;
+		progress_rpt_percent = percent;
+		secs = db_time.tv_sec - progress_rpt_start.tv_sec;
+		secs -= secs % interval;
+		progress_rpt_last.tv_sec = progress_rpt_start.tv_sec + secs;
+		if (db_debug > 1)
+			quiet_trace_msg("%s "L_DPAT" of "L_DPAT" %s or %d%%"
+					"    db_mmaps=%d hash=%d",
+					s1, done/scale, total/scale,
+					s2, progress_rpt_percent,
+					db_stats.db_mmaps, db_stats.hash_mmaps);
+		else
+			quiet_trace_msg("%s "L_DPAT" of "L_DPAT" %s or %d%%",
+					s1, done/scale, total/scale,
+					s2, progress_rpt_percent);
+	}
+
+
+	if (clean_mode == QUICK_MODE
+	    && !final) {
+		if (db_time.tv_sec > clean_start.tv_sec + 30*60)
+			dcc_logbad(EX_UNAVAILABLE, "quick cleaning too slow");
+	}
+
+	return checked_us;
+}
+
+
+
+/* delete old, less fuzzy checksums in the new record */
+static void
+fuzzy_obs(DB_RCD *new, DB_RCD_CK **end_ck)
+{
+	DB_RCD_CK *rcd_ck;
+	DCC_CK_TYPES type;
+	int len;
+
+	rcd_ck = new->cks;
+	while (rcd_ck < *end_ck) {
+		type = DB_CK_TYPE(rcd_ck);
+		if (!dcc_ts_older_ts(&new->ts, &new_ex_ts[type].all)) {
+			++rcd_ck;
+			continue;
+		}
+
+		++obs_rcds;
+		new->fgs_num_cks = (((new->fgs_num_cks - 1)
+				     & ~DB_RCD_FG_DELAY)
+				    | DB_RCD_FG_TRIM);
+		--*end_ck;
+		len = (char *)*end_ck - (char *)rcd_ck;
+		if (len == 0)
+			return;
+		memmove(rcd_ck, rcd_ck+1, len);
+	}
+}
+
+
+
+static void
+adj_def_expire(void)
+{
+	double new_dbsize, new_dbsize1, day_rate, db_ratio;
+	int spam_secs, secs;
+	struct timeval tv;
+	char new_dbsize_buf[20], csize_buf[20], old_csize_buf[20];
+	char day_rate_buf[20];
+
+	/* do this only once */
+	if (def_exp_ratio != 0.0)
+		return;
+
+	/* Compute the ratio of size of the database 24 hours from now
+	 * to the size of the window. Assume:
+	 *  - We will receive about the same number of reports in the next
+	 *	24 hours as the last 24.  This is a good assumption for
+	 *	weekdays, but as much as 30% wrong about weekends.
+	 *  - Dbclean will be run once per day at the current time.
+	 *  - The size of the database is a linear function of expiration
+	 *	duration.  This is tenuous when the spam expiration duration
+	 *	is less than 1 day.
+	 * Use the maximum of two guesses for tomorrow's database size.
+	 *	One guess is the current size, base on assuming that
+	 *	we will use roughly the same expiration durations and
+	 *	so the database will grow to about size it now has.
+	 *	The other guess uses the previous database size and the
+	 *	avarage data rate.  It compensates for short term changes
+	 *	in the rate and for running dbclean more than once per day. */
+	new_dbsize = db_parms.db_csize;
+	size2str(csize_buf, sizeof(csize_buf), new_dbsize, 1);
+	new_dbsize1 = db_parms.old_db_csize;
+	size2str(old_csize_buf, sizeof(old_csize_buf), new_dbsize1, 1);
+	day_rate = db_add_rate(&db_parms, 0);
+	if (day_rate >= 0.0)
+		day_rate *= (24*60*60);
+	size2str(day_rate_buf, sizeof(day_rate_buf), day_rate, 1);
+
+	/* without information, be pessimistic and assume 1.4 GByte/day */
+	if (day_rate <= 0.0 && !grey_on)
+		day_rate = 1.4*1024.0*1024.0*1024.0;
+	if (day_rate > 0.0) {
+		new_dbsize1 += day_rate;
+		if (new_dbsize < new_dbsize1)
+			new_dbsize = new_dbsize1;
+	}
+
+	size2str(new_dbsize_buf, sizeof(new_dbsize_buf), new_dbsize, 1);
+	if (db_debug)
+		quiet_trace_msg("predict new_dbsize=%s from db_csize=%s"
+				" old_db_csize=%s rate=%s",
+				new_dbsize_buf,
+				csize_buf, old_csize_buf, day_rate_buf);
+
+	/*  Assume there will be 20% as many bytes used in the hash table
+	 * as in the database */
+	new_dbsize *= 1.2;
+
+	/* we cannot adjust the defaults
+	 *	- 1st time dbclean run
+	 *	- if the previous run used a larger than default value
+	 *	- there is no need to reduce the default because the predicted
+	 *	    maximum size is smaller than the target maximum
+	 */
+	spam_secs = db_parms.ex_secs[DCC_CK_FUZ2].spam;
+	if (spam_secs != 0
+	    && spam_secs <= DB_EXPIRE_SPAMSECS_DEF
+	    && new_dbsize > db_max_byte
+	    && (db_ratio = (db_max_byte / new_dbsize)) < 1.0) {
+		def_exp_ratio = (spam_secs * db_ratio) / DB_EXPIRE_SPAMSECS_DEF;
+
+		/* change the two durations together and so with same errors */
+		def_expire_spamsecs = DB_EXPIRE_SPAMSECS_DEF * def_exp_ratio;
+		def_expire_secs = DB_EXPIRE_SECS_DEF * def_exp_ratio;
+
+		def_expire_secs -= def_expire_secs % (60*60);
+		if (def_expire_secs < DB_EXPIRE_SECS_DEF_MIN)
+			def_expire_secs = DB_EXPIRE_SECS_DEF_MIN;
+
+		def_expire_spamsecs -= def_expire_spamsecs % (24*60*60);
+		if (def_expire_spamsecs < DB_EXPIRE_SPAMSECS_DEF_MIN)
+			def_expire_spamsecs = DB_EXPIRE_SPAMSECS_DEF_MIN;
+
+#if DB_MIN_MBYTE == 0 && !defined(GOT_PHYSMEM)
+		if (def_expire_secs == DB_EXPIRE_SECS_DEF_MIN
+		    || def_expire_spamsecs == DB_EXPIRE_SPAMSECS_DEF_MIN)
+			quiet_trace_msg("cannot determine physical RAM; rebuild"
+					" with ./configure with-db-memory");
+#endif
+		return;
+	}
+
+	def_exp_ratio = 1.0;
+
+	/* if the defaults do not need to be reduced now but they
+	 * were reduced before, then relax them gently */
+	if (spam_secs < DB_EXPIRE_SPAMSECS_DEF) {
+		dcc_ts2timeval(&tv, &db_parms.ex_spam[DCC_CK_FUZ2]);
+		secs = clean_start.tv_sec - tv.tv_sec;
+		if (secs > 0
+		    && secs < DB_EXPIRE_SPAMSECS_DEF)
+			def_expire_spamsecs = secs;
+
+		dcc_ts2timeval(&tv, &db_parms.ex_all[DCC_CK_FUZ2]);
+		secs = clean_start.tv_sec - tv.tv_sec;
+		if (secs > 0
+		    && secs < DB_EXPIRE_SECS_DEF)
+			def_expire_secs = secs;
+	}
+}
+
+
+
+/* copy the existing database, discard junk and old entries */
+static u_char				/* 1=done 0=database broken */
+expire(DB_PTR old_db_csize)
+{
+#define EXPIRE_BAIL() {alarm(0); flod_unmap(0, 0); db_close(0); return 0;}
+
+	DCC_TS ts;
+	u_char emptied, reduced_defaults;
+	u_char old_ok[DCC_DIM_CKS];
+	DB_RCD rcd, new;
+	const DB_RCD_CK *rcd_ck, *rcd_ck2;
+	DB_RCD_CK *new_ck;
+	DCC_TGTS tgts_raw, ck_tgts;
+	u_char needed, obs_lvl, timely;
+	int old_num_cks, new_num_cks, nokeep_num_cks;
+	DB_PTR min_confirm_pos, next_adj_pos;
+	FLOD_MMAP *mp;
+	DCC_CK_TYPES prev_type, type, type2;
+	int rcd_len;
+	struct stat sb;
+	time_t need_unlock;
+	int i;
+
+	reduced_defaults = 0;
+	if (expire_secs < 0) {
+		adj_def_expire();
+		if (def_expire_secs > expire_spamsecs
+		    && expire_spamsecs > 0) {
+			expire_secs = expire_spamsecs;
+		} else {
+			if (def_expire_secs != DB_EXPIRE_SECS_DEF
+			    && def_exp_ratio != 1.0)
+				reduced_defaults = 1;
+			expire_secs = def_expire_secs;
+		}
+	}
+	if (expire_spamsecs < 0) {
+		adj_def_expire();
+		if (def_expire_spamsecs < expire_secs) {
+			expire_spamsecs = expire_secs;
+		} else {
+			if (def_expire_spamsecs != DB_EXPIRE_SPAMSECS_DEF
+			    && def_exp_ratio != 1.0)
+				reduced_defaults = 1;
+			expire_spamsecs = def_expire_spamsecs;
+		}
+	}
+
+	if (expire_spamsecs > 0 && expire_spamsecs < expire_secs)
+		dcc_logbad(EX_USAGE,
+			   "spam expiration -E must be longer than -e");
+
+	expired_rcds = 0;
+	expired_cks = 0;
+	kept_cks = white_cks;
+	need_unlock = 0;
+	report_progress_init();
+
+	/* Compute timestamps for records we keep.
+	 * Use the values from the previous use of dbclean as defaults
+	 * unless they are bogus */
+	memset(old_ok, 0, sizeof(old_ok));
+	dcc_secs2ts(&ts, clean_start.tv_sec);
+	for (type = DCC_CK_TYPE_FIRST; type <= DCC_CK_TYPE_LAST; ++type) {
+		DB_EX_SEC *th = &db_parms.ex_secs[type];
+
+		if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+			continue;
+		if (DCC_CK_IS_REP_OP(grey_on, type))
+			continue;
+
+		if (th->spam <= 0 || th->spam > DB_EXPIRE_SECS_MAX)
+			continue;
+		if (th->all <= 0 || th->all > th->spam)
+			continue;
+
+		if (dcc_ts_newer_ts(&db_parms.ex_spam[type], &ts))
+			continue;
+		if (dcc_ts_newer_ts(&db_parms.ex_all[type], &ts))
+			continue;
+
+		old_ok[type] = 1;	/* old values for this type are ok */
+	}
+
+	for (type = DCC_CK_TYPE_FIRST; type <= DCC_CK_TYPE_LAST; ++type) {
+		DB_EX_SEC *new_th = &new_ex_secs[type];
+		DB_EX_TS_TYPE *new_ts = &new_ex_ts[type];
+		int old_all = db_parms.ex_secs[type].all;
+		int old_spam = db_parms.ex_secs[type].spam;
+
+		if (type == DCC_CK_SRVR_ID) {
+			/* keep server-ID declarations 5 weeks or a week longer
+			 * than reputations so that they will be flooded 1st */
+			new_th->all = DB_EXPIRE_SRVR_ID_SECS;
+			new_th->spam = DB_EXPIRE_SRVR_ID_SECS;
+
+		} else if (grey_on) {
+			if (old_ok[type]) {
+				/* This is the path by which the dccd -G
+				 * parameters are used. */
+				new_th->all = old_all;
+				new_th->spam = old_spam;
+			} else if (DCC_CK_IS_GREY_TRIPLE(1, type)) {
+				new_th->all = DEF_GREY_WINDOW;
+				new_th->spam = DEF_GREY_WHITE;
+			} else if (DCC_CK_IS_GREY_MSG(1, type)
+				   || type == DCC_CK_BODY) {
+				new_th->all = DEF_GREY_WINDOW;
+				new_th->spam = DEF_GREY_WINDOW;
+			} else {
+				new_th->all = 1;
+				new_th->spam = 1;
+			}
+		} else if (have_expire_parms < 0 && old_ok[type]
+			   && (db_parms.flags & DB_PARM_EXP_SET)) {
+			/* use the old durations they are valid
+			 * and we have no expiriation parameters */
+			new_th->all = old_all;
+			new_th->spam = old_spam;
+
+		} else {
+			new_th->all = expire_secs;
+			new_th->spam = (DCC_CK_LONG_TERM(type)
+					? expire_spamsecs
+					: expire_secs);
+			if (reduced_defaults) {
+				quiet_trace_msg("adjust default by"
+						" %4.2f to -e%dhours"
+						" -E%ddays",
+						def_exp_ratio,
+						expire_secs/(60*60),
+						expire_spamsecs
+						/ (24*60*60));
+				reduced_defaults = 0;
+			}
+		}
+
+		/* compute oldest timestamp for this type of checksum,
+		 * without going crazy with "-Enever" */
+		dcc_secs2ts(&new_ts->spam,
+			    clean_start.tv_sec - min(clean_start.tv_sec,
+						     new_th->spam));
+		dcc_secs2ts(&new_ts->all,
+			    clean_start.tv_sec - min(clean_start.tv_sec,
+						     new_th->all));
+	}
+
+	/* put the timestampes into the new file */
+	write_new_hdr(1);
+
+	/* if we are running as root,
+	 * don't change the owner of the database */
+	if (getuid() == 0) {
+		if (0 > fstat(old_db_fd, &sb))
+			dcc_logbad(EX_IOERR, "fstat(%s): %s",
+				   old_db_nm, ERROR_STR());
+		if (0 > fchown(new_db_fd, sb.st_uid, sb.st_gid))
+			dcc_logbad(EX_IOERR, "fchown(%s,%d,%d): %s",
+				   new_db_nm, (int)sb.st_uid, (int)sb.st_gid,
+				   ERROR_STR());
+	}
+
+	if (DB_PTR_BASE != lseek(old_db_fd, DB_PTR_BASE, SEEK_SET))
+		dcc_logbad(EX_IOERR, "lseek(%s,%d): %s",
+			   cur_db_nm, DB_PTR_BASE, ERROR_STR());
+	read_rcd_invalidate(0);
+
+	flod_mmap(0, &db_parms.sn, 0, 1, 1);
+	if (flod_mmaps)
+		memcpy(&new_flod_mmaps, flod_mmaps, sizeof(new_flod_mmaps));
+	min_confirm_pos = new_flod_mmaps.delay_pos;
+	next_adj_pos = DB_PTR_BASE;
+	for (mp = new_flod_mmaps.mmaps;
+	     mp <= LAST(new_flod_mmaps.mmaps);
+	     ++mp) {
+		if (mp->rem_hostname[0] == '\0') {
+			mp->flags &= ~FLODMAP_FG_MARK;
+		} else {
+			mp->flags |= FLODMAP_FG_MARK;
+			if (min_confirm_pos > mp->confirm_pos)
+				min_confirm_pos = mp->confirm_pos;
+		}
+	}
+	adj_delay_pos = (new_flod_mmaps.delay_pos != 0) ? 1 : 0;
+
+	emptied = cleardb;
+	dcc_timeval2ts(&new_flod_mmaps.sn, &clean_start, 0);
+
+	/* copy the old file to the new,
+	 * discarding and compressing old data as we go */
+	for (old_db_pos = DB_PTR_BASE;
+	     old_db_pos < old_db_csize;
+	     old_db_pos += rcd_len) {
+		if (--progress_rpt_cnt <= 0)
+			need_unlock += report_progress(0, "  processed",
+						       "MBytes",
+						       old_db_pos, old_db_csize,
+						       1024*1024);
+
+		if (old_db_pos == next_adj_pos)
+			next_adj_pos = adj_mmap();
+
+		if (clean_mode != REPAIR_MODE) {
+			/* read the record by mapping if not repairing */
+			if (!db_map_rcd(0, &db_sts.rcd, old_db_pos, &rcd_len))
+				EXPIRE_BAIL();
+			memcpy(&rcd, db_sts.rcd.d.r, rcd_len);
+		} else {
+			rcd_len = read_rcd(0, &rcd,
+					   old_db_fd, old_db_pos, cur_db_nm);
+			if (rcd_len <= 0) {
+				if (rcd_len == 0)
+					dcc_error_msg("unexpected EOF in %s at "
+						      L_HPAT" instead of "
+						      L_HPAT,
+						      cur_db_nm,
+						      old_db_pos,
+						      old_db_csize);
+				/* give up and ask our neighbors to rewind */
+				emptied = 1;
+				old_db_pos = old_db_csize;
+				break;
+			}
+		}
+
+		/* skip end-of-page padding */
+		if (rcd_len == sizeof(rcd)-sizeof(rcd.cks))
+			continue;
+
+		if (DB_RCD_ID(&rcd) == DCC_ID_WHITE) {
+			/* skip whitelist entries if whitelist source is ok */
+			if (!keep_white)
+				continue;
+			/* refresh whitelist entries if source is bad */
+			dcc_timeval2ts(&rcd.ts, &clean_start, 0);
+		}
+
+		old_num_cks = DB_NUM_CKS(&rcd);
+
+		/* expire or throw away deleted reports */
+		tgts_raw = DB_TGTS_RCD_RAW(&rcd);
+		if (tgts_raw == 0) {
+			++expired_rcds;
+			expired_cks += old_num_cks;
+			continue;
+		}
+		if (tgts_raw > DCC_TGTS_MAX_DB) {
+			dcc_error_msg("discarding report at "L_HPAT
+				      " with bogus target count %#x",
+				      old_db_pos, tgts_raw);
+			++expired_rcds;
+			expired_cks += old_num_cks;
+			continue;
+		}
+
+		if (dcc_ts_newer_ts(&rcd.ts, &future_ts)) {
+			static int whines = 0;
+			if (whines < 50)
+				dcc_error_msg("discarding report at "L_HPAT
+					      " from the future %s%s",
+					      old_db_pos,
+					      ts2str_err(&rcd.ts),
+					      ++whines >= 20
+					      ? "; stop complaining"
+					      : "");
+			++expired_rcds;
+			expired_cks += old_num_cks;
+			continue;
+		}
+
+
+		needed = 0;
+		obs_lvl = 0;
+		timely = 1;
+		nokeep_num_cks = 0;
+		memcpy(&new, &rcd, sizeof(new)-sizeof(new.cks));
+		new.fgs_num_cks &= (DB_RCD_FG_TRIM | DB_RCD_FG_SUMRY
+				    | DB_RCD_FG_DELAY);
+		new_ck = new.cks;
+		for (prev_type = DCC_CK_INVALID, rcd_ck = rcd.cks;
+		     rcd_ck < &rcd.cks[old_num_cks];
+		     prev_type = type, ++rcd_ck) {
+			type = DB_CK_TYPE(rcd_ck);
+			if (!DCC_CK_OK_DB(grey_on, type)) {
+				static int whines = 0;
+				if (whines < 20)
+					dcc_error_msg("discarding %s"
+						      " checksum at "L_HPAT"%s",
+						      DB_TYPE2STR(type),
+						      old_db_pos,
+						      ++whines >= 20
+						      ? "; stop complaining"
+						      : "");
+				++expired_cks;
+				new.fgs_num_cks |= DB_RCD_FG_TRIM;
+				new.fgs_num_cks &= ~DB_RCD_FG_DELAY;
+				continue;
+			}
+
+			if (type <= prev_type
+			    && prev_type != DCC_CK_FLOD_PATH) {
+				dcc_error_msg("discarding out of order %s"
+					      " checksum at "L_HPAT,
+					      DB_TYPE2STR(type),
+					      old_db_pos);
+				++expired_cks;
+				new.fgs_num_cks |= DB_RCD_FG_TRIM;
+				new.fgs_num_cks &= ~DB_RCD_FG_DELAY;
+				continue;
+			}
+
+			/* Silently discard pure junk from other servers,
+			 * provided it is junk by default */
+			if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type)
+			    && DB_GLOBAL_NOKEEP(grey_on, type)
+			    && type != DCC_CK_FLOD_PATH
+			    && type != DCC_CK_SRVR_ID
+			    && DB_RCD_ID(&rcd) != DCC_ID_WHITE) {
+				++expired_cks;
+				continue;
+			}
+
+			/* Keep paths except on old records or records that
+			 * have been trimmed or compressed.
+			 * Never remove paths from server-ID declarations. */
+			if (type == DCC_CK_FLOD_PATH) {
+				if (DB_RCD_TRIMMED(&new)
+				    || DB_RCD_ID(&new) == DCC_ID_COMP)
+					continue;
+				/* forget line number on old whitelist entry */
+				if (DB_RCD_ID(&rcd) == DCC_ID_WHITE)
+					continue;
+				rcd_ck2 = rcd_ck+1;
+				for (;;) {
+					type2 = DB_CK_TYPE(rcd_ck2);
+					if (type2 == DCC_CK_SRVR_ID
+					    || !dcc_ts_older_ts(&rcd.ts,
+							&new_ex_ts[type2
+							    ].all)) {
+					    /* keep this path since this report
+					     * is a server-ID declaration
+					     * or not old */
+					    *new_ck = *rcd_ck;
+					    ++new_ck;
+					    ++new.fgs_num_cks;
+					    ++nokeep_num_cks;
+					    break;
+					}
+					if (++rcd_ck2>=&rcd.cks[old_num_cks]) {
+					    /* we are discarding this path */
+					    new.fgs_num_cks |= DB_RCD_FG_TRIM;
+					    new.fgs_num_cks &= ~DB_RCD_FG_DELAY;
+					    break;
+					}
+				}
+				continue;
+			}
+
+			if (!dcc_ts_older_ts(&rcd.ts, &new_ex_ts[type].all)) {
+				/* This report is recent.
+				 * However, obsolete or junk checksums
+				 * don't make the report needed */
+				if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type)
+				    && DB_RCD_ID(&rcd) != DCC_ID_WHITE) {
+					++nokeep_num_cks;
+				} else if (DB_CK_OBS(rcd_ck)) {
+					/* This checksum is obsolete.
+					 * If it has the highest level of
+					 * fuzziness, then it controls whether
+					 * the whole report is needed,. */
+					if (obs_lvl < db_ck_fuzziness[type]) {
+					    obs_lvl = db_ck_fuzziness[type];
+					    needed = 0;
+					}
+				} else {
+					/* This checksum is not obsolete.
+					 * If it is at least as fuzzy as any
+					 * other checksum, then it can say
+					 * the report is needed */
+					if (obs_lvl <= db_ck_fuzziness[type]) {
+					    obs_lvl = db_ck_fuzziness[type];
+					    needed = 1;
+					}
+				}
+
+			} else {
+				/* This checksum is at least somewhat old.
+				 * Throw away delete requests
+				 * and other servers' useless checksums */
+				if (tgts_raw == DCC_TGTS_DEL
+				    || DB_TEST_NOKEEP(db_parms.nokeep_cks,
+						      type)) {
+					++expired_cks;
+					new.fgs_num_cks |= DB_RCD_FG_TRIM;
+					new.fgs_num_cks &= ~DB_RCD_FG_DELAY;
+					continue;
+				}
+				/* Throw away old obsolete checksums
+				 * and entire reports if the fuzziest
+				 * checksum is obsolete */
+				if (DB_CK_OBS(rcd_ck)) {
+					if (obs_lvl < db_ck_fuzziness[type]) {
+					    obs_lvl = db_ck_fuzziness[type];
+					    needed = 0;
+					}
+					++expired_cks;
+					new.fgs_num_cks |= DB_RCD_FG_TRIM;
+					new.fgs_num_cks &= ~DB_RCD_FG_DELAY;
+					continue;
+				}
+
+				/* old summaries are unneeded, because
+				 * they have already been flooded.
+				 * They do not contribute to local counts */
+				if (DB_RCD_SUMRY(&rcd))
+					continue;
+
+				/* The checksum is old enough to compress, so
+				 * mark the record as eligible for splitting. */
+				timely = 0;
+
+				/* Discard this checksum if its ultimate total
+				 * is low or ancient
+				 * or if it reaches spam after this report.
+				 * To determine the ultimate total, we must
+				 * have a hash table to find the newest record,
+				 * which contains the final total */
+				if (clean_mode != REPAIR_MODE) {
+					i = get_lead(type, rcd_ck);
+					if (i < 0)
+					    EXPIRE_BAIL();
+					if (!i) {
+					    ++expired_cks;
+					    new.fgs_num_cks |= DB_RCD_FG_TRIM;
+					    new.fgs_num_cks &= ~DB_RCD_FG_DELAY;
+					    continue;
+					}
+				}
+
+				if (obs_lvl <= db_ck_fuzziness[type]) {
+					/* Since we did not delete this
+					 * checksum, we need the record if this
+					 * checksum is fuzzy enough to control
+					 * our need. */
+					needed = 1;
+					/* If this is the fuzziest checksum we
+					 * have seen, then preceding and so
+					 * less fuzzy checksums are obsolete,
+					 * if they are old.
+					 * Assume that checksums are ordered
+					 * in the record by fuzziness. */
+					if (obs_lvl < db_ck_fuzziness[type]) {
+					    obs_lvl = db_ck_fuzziness[type];
+					    if (obs_lvl != DCC_CK_FUZ_LVL_REP
+						&& !grey_on)
+						fuzzy_obs(&new, &new_ck);
+					}
+				}
+			}
+
+			/* Keep this checksum if we decide the whole report
+			 * is needed. */
+			*new_ck = *rcd_ck;
+
+			++new_ck;
+			++new.fgs_num_cks;
+		}
+
+		/* occassionally let the daemon work with the old file */
+		if (need_unlock >= UNLOCK_INTERVAL_USECS) {
+			need_unlock = 0;
+			if (!standalone && !db_unlock())
+				EXPIRE_BAIL();
+		}
+
+		/* if none of its checksums are needed,
+		 * then discard the entire record */
+		if (!needed) {
+			expired_cks += DB_NUM_CKS(&new);
+			++expired_rcds;
+			continue;
+		}
+
+		new_num_cks = DB_NUM_CKS(&new);
+		kept_cks += new_num_cks - nokeep_num_cks;
+
+		/* Put the new record into the new file.
+		 *
+		 * If all of the record is recent, if it contains 1 checksum,
+		 * or if all of its totals are the same, then simply add it.
+		 *
+		 * Otherwise, divide it into records of identical counts
+		 * to allow compression or combining with other records. */
+		if (new_num_cks > 1
+		    && (!timely
+			|| DB_RCD_ID(&new) == DCC_ID_COMP
+			|| DB_RCD_TRIMMED(&new))) {
+			for (;;) {
+				/* skip the checksums that have the same total
+				 * as the first checksum to leave them with the
+				 * original new report */
+				new_ck = new.cks;
+				ck_tgts = DB_TGTS_CK(new_ck);
+				for (i = 1; i < new_num_cks; ++i) {
+					++new_ck;
+					if (DB_TGTS_CK(new_ck) != ck_tgts)
+					    break;
+				}
+				if (new_num_cks <= i)
+					break;
+				new_num_cks -= i;
+
+				/* write the checksums with the common total */
+				new.srvr_id_auth = DCC_ID_COMP;
+				new.fgs_num_cks = i;
+				if (!write_new_rcd(&new,
+						   sizeof(new) - sizeof(new.cks)
+						   + i*sizeof(new.cks[0])))
+					EXPIRE_BAIL();
+
+				/* handle the remaining checksums */
+				new.fgs_num_cks = new_num_cks;
+				memmove(&new.cks[0], &new.cks[i],
+					new_num_cks*sizeof(new.cks[0]));
+			}
+		}
+
+		/* write the rest (or all) of the new record */
+		if (!write_new_rcd(&new,
+				   sizeof(new) - sizeof(new.cks)
+				   + new_num_cks*sizeof(new.cks[0])))
+			EXPIRE_BAIL();
+	}
+	write_new_flush(1);
+	alarm(0);
+
+	/* do final adjustment of the flooding positions */
+	adj_mmap();
+	/* force them to be right if the system crashed with the
+	 * flod.map file on the disk more up to date and so after the
+	 * database file on the disk */
+	for (mp = new_flod_mmaps.mmaps;
+	     mp <= LAST(new_flod_mmaps.mmaps);
+	     ++mp) {
+		if (mp->rem_hostname[0] != '\0'
+		    && mp->confirm_pos > new_db_csize)
+			mp->confirm_pos = new_db_csize;
+	}
+
+	/* We are finished with the old file.
+	 *	Mark all of its pages MADV_DONTNEED */
+	rel_db_states();
+	i = (db_unload(0, 2) != 0);
+	if (!db_close(1))
+		i = 0;
+
+	write_new_hdr(emptied);
+	report_progress(1, "  processed", "MBytes",
+			old_db_pos, old_db_csize, 1024*1024);
+	if (grey_on)
+		quiet_trace_msg("expired %d records and %d checksums in %s",
+				expired_rcds, expired_cks, cur_db_nm);
+	else
+		quiet_trace_msg("expired %d records and %d checksums,"
+				" obsoleted %d checksums in %s",
+				expired_rcds, expired_cks, obs_rcds, cur_db_nm);
+	return i;
+}
+
+
+
+/* copy the database copy while doing minimal expiring */
+static u_char
+copy_db(void)
+{
+	static DB_VERSION_BUF old_version4 = DB_VERSION4_STR;
+	static DB_VERSION_BUF old_version3 = DB_VERSION3_STR;
+	union {
+	    DB_HDR	hdr;
+	    DB_V4_PARMS	v4;
+	    DB_V3_PARMS v3;
+	} old_db;
+	struct timeval sn;
+
+	/* do not lock the old database because the daemon must continue
+	 * to answer requests */
+	if (old_db_fd < 0) {
+		old_db_fd = open(cur_db_nm, O_RDONLY, 0);
+		if (old_db_fd == -1)
+			dcc_logbad(EX_IOERR, "open(%s): %s",
+				   cur_db_nm, ERROR_STR());
+	}
+
+	if (!read_db_hdr(dcc_emsg, &old_db.hdr, old_db_fd, cur_db_nm))
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+	if (memcmp(old_db.hdr.p.version, db_version_buf,
+		   sizeof(old_db.hdr.p.version))) {
+		if (!memcmp(old_db.v4.version, old_version4,
+			   sizeof(old_db.v4.version))) {
+			memset(&old_db_parms, 0,
+			       sizeof(old_db_parms));
+			memcpy(old_db_parms.version, db_version_buf,
+			       sizeof(old_db_parms.version));
+
+			old_db_parms.db_csize = old_db.v4.db_csize;
+			old_db_parms.pagesize = old_db.v4.pagesize;
+			old_db_parms.sn = old_db.v4.sn;
+			old_db_parms.cleared = old_db.v4.cleared;
+			old_db_parms.cleaned = old_db.v4.cleaned;
+			old_db_parms.cleaned_cron = old_db.v4.cleaned_cron;
+			memcpy(old_db_parms.ex_spam, old_db.v4.ex_spam,
+			       sizeof(old_db_parms.ex_spam));
+			memcpy(old_db_parms.ex_all, old_db.v4.ex_spam,
+			       sizeof(old_db_parms.ex_all));
+			memcpy(old_db_parms.ex_secs, old_db.v4.ex_secs,
+			       sizeof(old_db_parms.ex_secs));
+			old_db_parms.nokeep_cks = old_db.v4.nokeep_cks;
+			old_db_parms.flags = old_db.v4.flags;
+			old_db_parms.old_db_csize = old_db.v4.old_db_csize;
+			old_db_parms.db_added = old_db.v4.db_added;
+			old_db_parms.hash_used = old_db.v4.hash_used;
+			old_db_parms.old_hash_used = old_db.v4.old_hash_used;
+			old_db_parms.hash_added = old_db.v4.hash_added;
+			old_db_parms.rate_secs = old_db.v4.rate_secs;
+			old_db_parms.last_rate_sec = old_db.v4.last_rate_sec;
+			old_db_parms.old_kept_cks = old_db.v4.old_kept_cks;
+
+		} else if (!memcmp(old_db.v3.version, old_version3,
+				   sizeof(old_db.v3.version))) {
+			memset(&old_db_parms, 0,
+			       sizeof(old_db_parms));
+			memcpy(old_db_parms.version, db_version_buf,
+			       sizeof(old_db_parms.version));
+
+			old_db_parms.db_csize = old_db.v3.db_csize;
+			old_db_parms.pagesize = old_db.v3.pagesize;
+			old_db_parms.sn = old_db.v3.sn;
+			memcpy(old_db_parms.ex_spam, old_db.v3.ex_spam,
+			       sizeof(old_db_parms.ex_spam));
+			memcpy(old_db_parms.ex_secs, old_db.v3.ex_secs,
+			       sizeof(old_db_parms.ex_secs));
+			old_db_parms.nokeep_cks = old_db.v3.nokeep_cks;
+			if (old_db.v3.flags & DB_PARM_V3_FG_GREY)
+				old_db_parms.flags |= DB_PARM_FG_GREY;
+			if (old_db.v3.flags & DB_PARM_V3_FG_CLEARED)
+				old_db_parms.flags |= DB_PARM_FG_CLEARED;
+			old_db_parms.old_db_csize = old_db.v3.old_db_csize;
+			old_db_parms.db_added = old_db.v3.db_added;
+			old_db_parms.hash_used = old_db.v3.hash_used;
+			old_db_parms.old_hash_used = old_db.v3.old_hash_used;
+			old_db_parms.hash_added = old_db.v3.hash_added;
+			old_db_parms.rate_secs = old_db.v3.rate_secs;
+			old_db_parms.last_rate_sec = old_db.v3.last_rate_sec;
+			old_db_parms.old_kept_cks = old_db.v3.old_kept_cks;
+
+			dcc_ts2timeval(&sn, &old_db_parms.sn);
+			old_db_parms.cleared = sn.tv_sec;
+			old_db_parms.cleaned = sn.tv_sec;
+			if (old_db.v3.flags & DB_PARM_V3_FG_SELF_CLEAN2) {
+				old_db_parms.cleared -= 2*24*60*60;
+				old_db_parms.cleaned -= 24*60*60;
+			}
+		} else {
+			dcc_logbad(EX_IOERR, "%s has the wrong magic \"%.*s\"",
+				   cur_db_nm,
+				   ISZ(DB_VERSION_BUF), old_db.hdr.p.version);
+		}
+	} else {
+		old_db_parms = old_db.hdr.p;
+	}
+
+	db_parms.sn = old_db_parms.sn;
+	db_parms.cleared = old_db_parms.cleared;
+	db_parms.cleaned = old_db_parms.cleaned;
+	db_parms.cleaned_cron = old_db_parms.cleaned_cron;
+	memcpy(db_parms.ex_all, old_db_parms.ex_all,
+	       sizeof(db_parms.ex_all));
+	memcpy(db_parms.ex_spam, old_db_parms.ex_spam,
+	       sizeof(db_parms.ex_spam));
+	memcpy(&db_parms.ex_secs, &old_db_parms.ex_secs,
+	       sizeof(db_parms.ex_secs));
+	db_parms.nokeep_cks = old_db_parms.nokeep_cks;
+	db_parms.flags = old_db_parms.flags;
+
+	set_db_tholds(db_parms.nokeep_cks);
+
+	return expire(old_db_parms.db_csize);
+}
+
+
+
+/* Copy any records from the old file to the new file that were
+ * added to the old file while we were creating the new file. */
+static u_char
+catchup(DCC_EMSG emsg)
+{
+	DB_HDR old_db_hdr;
+	DB_RCD rcd;
+	int rcd_len;
+	u_char result;
+	int count, old_count;
+
+	/* Because dccd knows dbclean is running, dccd will have been
+	 * keeping its header block more accurate than usual. */
+	result = 1;
+	count = 0;
+	do {
+		old_count = count;
+		if (!read_db_hdr(dcc_emsg, &old_db_hdr,
+				old_db_fd, old_db_nm)) {
+			emsg = 0;
+			result = 0;
+			break;
+		}
+		if (old_db_hdr.p.db_csize < old_db_pos) {
+			dcc_error_msg("%s mysteriously truncated", old_db_nm);
+			result = 0;
+			break;
+		}
+		if ((off_t)old_db_pos != lseek(old_db_fd, old_db_pos,
+					       SEEK_SET)) {
+			dcc_pemsg(EX_IOERR, emsg, "lseek(%s, "L_HPAT"): %s",
+				  old_db_nm, old_db_pos, ERROR_STR());
+			emsg = 0;
+			result = 0;
+			break;
+		}
+		read_rcd_invalidate(0);
+		while (old_db_pos < old_db_hdr.p.db_csize) {
+			rcd_len = read_rcd(emsg, &rcd,
+					   old_db_fd, old_db_pos, old_db_nm);
+			if (rcd_len <= 0) {
+				if (rcd_len == 0)
+					dcc_pemsg(EX_IOERR, emsg,
+						  "premature EOF in %s"
+						  " at "L_HPAT
+						  " instead of "L_HPAT,
+						  old_db_nm,
+						  old_db_pos,
+						  old_db_hdr.p.db_csize);
+				emsg = 0;
+				result = 0;
+				break;
+			}
+			/* If something bad happens, we may not be able to
+			 * go back to the old file.  Carry on to get as much
+			 * data as we can although we know the dccd daemon
+			 * may croak when we release it */
+			if (!db_add_rcd(emsg, &rcd)) {
+				emsg = 0;
+				result = 0;
+				break;
+			}
+			old_db_pos += rcd_len;
+			++count;
+		}
+	} while (result && old_count != count);
+
+	if (count > 0 && db_debug >= 1)
+		quiet_trace_msg("copied %d late reports%s",
+				count, result ? "" : " with problems");
+
+	return result;
+}
+
+
+
+/* try to compress old report pointed to by db_sts.rcd with a predecessor */
+static void
+compress_old(void)
+{
+	DB_PTR prev, prev1;
+	DB_RCD_CK *new_ck, *prev_ck;
+	int new_ck_num, prev_ck_num;
+	DCC_TGTS new_tgts, prev_tgts;
+	DCC_CK_TYPES new_type, prev_type;
+#define NEWER (db_sts.rcd.d.r)
+#define OLDER (db_sts.rcd2.d.r)
+
+	/* Before spending the time to map a preceding checksum,
+	 * find at least one checksum worth keeping and that might
+	 * be combined or compressed with its predecessor. */
+	prev = DB_PTR_NULL;
+	prev_type = DCC_CK_INVALID;
+	for (new_ck_num = DB_NUM_CKS(NEWER),
+	     new_ck = NEWER->cks;
+	     new_ck_num != 0;
+	     --new_ck_num, ++new_ck) {
+		if (DB_CK_OBS(new_ck))
+			continue;
+		new_type = DB_CK_TYPE(new_ck);
+		if (DB_TEST_NOKEEP(db_parms.nokeep_cks, new_type))
+			continue;
+		/* all of the checksums in this record must be old */
+		if (!dcc_ts_older_ts(&NEWER->ts, &new_ex_ts[new_type].all))
+			return;
+		/* you can compress reports only if you have >=2 */
+		prev1 = DB_PTR_EX(new_ck->prev);
+		if (prev1 != DB_PTR_NULL) {
+			prev = prev1;
+			prev_type = new_type;
+		}
+	}
+	if (prev_type == DCC_CK_INVALID)
+		return;
+
+	/* having picked a checksum,
+	 * map the record containing its predecessor */
+	prev_ck = db_map_rcd_ck(dcc_emsg, &db_sts.rcd2, prev, prev_type);
+	if (!prev_ck)
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+
+	/* The current and previous records must be old
+	 * and contain the same useful checksums. */
+	new_ck_num = DB_NUM_CKS(NEWER);
+	new_ck = NEWER->cks;
+	prev_ck_num = DB_NUM_CKS(OLDER);
+	prev_ck = OLDER->cks;
+	for (;;) {
+		/* we must run out of checksums in the two reports at the
+		 * same time */
+		if (prev_ck_num == 0 || new_ck_num == 0) {
+			if (prev_ck_num == new_ck_num)
+				break;
+			return;
+		}
+
+		/* ignore paths and other junk */
+		if (DB_CK_OBS(prev_ck)) {
+			--prev_ck_num;
+			++prev_ck;
+			continue;
+		}
+		prev_type = DB_CK_TYPE(prev_ck);
+		if (DB_TEST_NOKEEP(db_parms.nokeep_cks, prev_type)) {
+			--prev_ck_num;
+			++prev_ck;
+			continue;
+		}
+		if (DB_CK_OBS(new_ck)) {
+			--new_ck_num;
+			++new_ck;
+			continue;
+		}
+		new_type = DB_CK_TYPE(new_ck);
+		if (DB_TEST_NOKEEP(db_parms.nokeep_cks, new_type)) {
+			--new_ck_num;
+			++new_ck;
+			continue;
+		}
+
+		/* because the checksums are ordered,
+		 * give up at the first difference in checksums */
+		if (new_type != prev_type
+		    || memcmp(new_ck->sum, prev_ck->sum, sizeof(new_ck->sum)))
+			return;
+
+		/* Give up at the first recent and valuable checksum. */
+		if (!dcc_ts_older_ts(&OLDER->ts, &new_ex_ts[new_type].all))
+			return;
+
+		--prev_ck_num;
+		++prev_ck;
+		--new_ck_num;
+		++new_ck;
+	}
+
+	/* The current and previous records are compatiable.
+	 * Add the count of the previous record to the current record
+	 * and mark the previous record useless.
+	 * The individual totals in the current record are already correct,
+	 * so postpone worrying about the deleted record. */
+	new_tgts = DB_TGTS_RCD_RAW(NEWER);
+	if (new_tgts < DCC_TGTS_TOO_MANY) {
+		prev_tgts = DB_TGTS_RCD(OLDER);
+		if (prev_tgts > DCC_TGTS_TOO_MANY
+		    || prev_tgts == 0)
+			return;
+		if (prev_tgts == DCC_TGTS_TOO_MANY) {
+			new_tgts = DCC_TGTS_TOO_MANY;
+		} else {
+			new_tgts += prev_tgts;
+			if (new_tgts > DCC_TGTS_TOO_MANY)
+				new_tgts = DCC_TGTS_TOO_MANY;
+		}
+		DB_TGTS_RCD_SET(NEWER, new_tgts);
+	}
+
+	/* Mark the previous record to be deleted next time. */
+	DB_TGTS_RCD_SET(OLDER, 0);
+	/* Mark it dirty so that the need to delete it gets to the file. */
+	SET_FLUSH_RCD(&db_sts.rcd2, 1);
+
+	NEWER->srvr_id_auth = DCC_ID_COMP;
+	NEWER->fgs_num_cks &= ~(DB_RCD_FG_TRIM
+				| DB_RCD_FG_SUMRY
+				| DB_RCD_FG_DELAY);
+	/* use the newest timestamp */
+	if (dcc_ts_older_ts(&NEWER->ts, &OLDER->ts))
+		NEWER->ts = OLDER->ts;
+	SET_FLUSH_RCD(&db_sts.rcd, 1);
+
+	++comp_rcds;
+
+#undef NEWER
+#undef OLDER
+}
+
+
+
+/* write a parsed whitelist checksum */
+static int
+white_write(DCC_EMSG emsg, DCC_WF *wf,
+	    DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts)
+{
+	DB_RCD rcd;
+	int rcd_len;
+	char buf[30];
+	DCC_FNM_LNO_BUF fnm_buf;
+
+	/* ignore checksums that clients are never supposed to send
+	 * to the server or for some other reason cannot be whitelisted */
+	switch (type) {
+	case DCC_CK_INVALID:
+	case DCC_CK_ENV_TO:
+	case DCC_CK_G_MSG_R_TOTAL:
+	case DCC_CK_G_TRIPLE_R_BULK:
+	case DCC_CK_SRVR_ID:
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s checksum cannot be used%s",
+			  dcc_type2str_err(type, 0, 0, grey_on),
+			  wf_fnm_lno(&fnm_buf, wf));
+		return 0;
+
+	case DCC_CK_IP:
+	case DCC_CK_ENV_FROM:
+	case DCC_CK_FROM:
+	case DCC_CK_MESSAGE_ID:
+	case DCC_CK_RECEIVED:
+	case DCC_CK_SUB:
+	case DCC_CK_BODY:
+	case DCC_CK_FUZ1:
+	case DCC_CK_FUZ2:
+		break;			/* these are ok */
+	}
+
+	if (tgts == DCC_TGTS_OK_MX
+	    || tgts == DCC_TGTS_OK_MXDCC
+	    || tgts == DCC_TGTS_SUBMIT_CLIENT) {
+		dcc_pemsg(EX_DATAERR, emsg,"\"%s\" ignored%s",
+			  dcc_tgts2str(buf, sizeof(buf), tgts, 0),
+			  wf_fnm_lno(&fnm_buf, wf));
+		return 0;
+	}
+
+	/* Greylist whitelist entries cannot involve blacklisting.
+	 * They use DCC_TGTS_GREY_WHITE to signal whitelisting */
+	if (grey_on) {
+		/* ignore anything except whitelisting */
+		if (tgts != DCC_TGTS_OK) {
+			dcc_pemsg(EX_DATAERR, emsg, "\"%s\" ignored%s",
+				  dcc_tgts2str(buf, sizeof(buf), tgts, 0),
+				  wf_fnm_lno(&fnm_buf, wf));
+			return 0;
+		}
+		tgts = DCC_TGTS_GREY_WHITE;
+	}
+
+	memset(&rcd, 0, sizeof(rcd));
+	dcc_timeval2ts(&rcd.ts, &clean_start, 0);
+	rcd.srvr_id_auth = DCC_ID_WHITE;
+	DB_TGTS_RCD_SET(&rcd, tgts);
+
+	rcd.cks[0].type_fgs = DCC_CK_FLOD_PATH;
+	memcpy(rcd.cks[0].sum, &wf->lno, sizeof(wf->lno));
+	rcd.cks[0].sum[sizeof(wf->lno)] = wf->fno;
+
+	rcd.cks[1].type_fgs = type;
+	memcpy(rcd.cks[1].sum, sum, sizeof(rcd.cks[1]));
+
+	rcd_len = sizeof(rcd) - sizeof(rcd.cks) + 2*sizeof(rcd.cks[0]);
+	rcd.fgs_num_cks = 2;
+
+	if (!write_new_rcd(&rcd, rcd_len))
+		return -1;
+
+	++white_cks;
+	return 1;
+}
+
+
+
+/* Add the whitelist of certified non-spam and non-spammers
+ *	and otherwise start the database */
+static void
+parse_white(void)
+{
+	int white_fd;
+
+	white_cks = 0;
+
+	if (!keep_white) {
+		memset(&dbclean_white_tbl, 0,sizeof(dbclean_white_tbl));
+		dcc_wf_init(&dbclean_wf, 0);
+		fnm2rel_good(dbclean_wf.ascii_nm, WHITELIST_NM(grey_on), 0);
+		dbclean_wf.wtbl = &dbclean_white_tbl;
+		white_fd = open(dbclean_wf.ascii_nm, O_RDONLY, 0);
+		if (white_fd < 0) {
+			/* worry only if the file exists but can't be used */
+			if (errno != ENOENT) {
+				dcc_error_msg("open(%s): %s",
+					      dbclean_wf.ascii_nm, ERROR_STR());
+				keep_white = 1;
+			}
+		} else {
+			if (0 > dcc_parse_whitefile(0, &dbclean_wf, white_fd,
+						    white_write, 0))
+				keep_white = 1;
+			if (0 > close(white_fd))
+				dcc_error_msg("close(%s): %s",
+					      dbclean_wf.ascii_nm, ERROR_STR());
+		}
+	}
+	if (keep_white) {
+		/* If the whitelist was bad, purge the new database of
+		 * the bad new whitelist.  We will use the existing
+		 * whitelist */
+		write_new_flush(1);
+		new_db_csize = DB_PTR_BASE;
+		if (0 > ftruncate(new_db_fd, DB_PTR_BASE))
+			dcc_logbad(EX_IOERR, "truncate(%s, %d): %s",
+				   new_db_nm, DB_PTR_BASE, ERROR_STR());
+		new_db_fsize = DB_PTR_BASE;
+		white_cks = 0;
+	}
+
+	/* update the counts in the database file */
+	write_new_hdr(1);
+}
+
+
+
+/* check for conflicts in the whitelist file in the record pointed to
+ *	by db_sts.rcd */
+static void
+check_white(void)
+{
+	static int msgs;
+	static int prev_lno1, prev_lno2;
+	static int prev_fno1, prev_fno2;
+	const DB_RCD_CK *rcd_ck, *prev_ck;
+	int lno1, lno2;
+	int fno1, fno2;
+	DCC_TGTS tgts1, tgts2;
+	char tgts1_buf[30], tgts2_buf[30];
+	const char *fname1, *fname2;
+	DCC_CK_TYPES type;
+	DB_PTR prev;
+
+	/* don't check if we have already complained enough */
+	if (msgs > 20)
+		return;
+
+	rcd_ck = db_sts.rcd.d.r->cks;
+
+	/* it is pointless without line numbers, which are lacking only
+	 * if we saved the old whitelist entries because the file is
+	 * broken */
+	if (DB_NUM_CKS(db_sts.rcd.d.r) != 2
+	    || DB_CK_TYPE(rcd_ck) != DCC_CK_FLOD_PATH)
+		return;
+
+	/* conflict is impossible with a single line */
+	++rcd_ck;
+	prev = DB_PTR_EX(rcd_ck->prev);
+	if (prev == DB_PTR_NULL)
+		return;
+
+	type = DB_CK_TYPE(rcd_ck);
+	prev_ck = db_map_rcd_ck(dcc_emsg, &db_sts.rcd2, prev, type);
+	if (!prev_ck)
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+
+	tgts1 = DB_TGTS_RCD(db_sts.rcd2.d.r);
+	tgts2 = DB_TGTS_RCD(db_sts.rcd.d.r);
+	if (tgts1 == tgts2)
+		return;			/* no conflict */
+
+	memcpy(&lno1, db_sts.rcd2.d.r->cks[0].sum, sizeof(lno1));
+	fno1 = db_sts.rcd2.d.r->cks[0].sum[sizeof(lno1)];
+	memcpy(&lno2, db_sts.rcd.d.r->cks[0].sum, sizeof(lno2));
+	fno2 = db_sts.rcd.d.r->cks[0].sum[sizeof(lno2)];
+
+	if (lno1 == prev_lno1 && fno1 == prev_fno1
+	    && lno2 == prev_lno2 && fno2 == prev_fno2)
+		return;
+
+	fname1 = wf_fnm(&dbclean_wf, fno1);
+	fname2 = wf_fnm(&dbclean_wf, fno2);
+	if (fname1 == fname2) {
+		fname1 = "";
+	} else {
+		fname1 = path2fnm(fname1);
+	}
+	dcc_error_msg("\"%s\" in line %d%s%s conflicts with \"%s\""
+		      " in line %d of %s",
+		      dcc_tgts2str(tgts1_buf, sizeof(tgts1_buf),
+				   tgts1, grey_on),
+		      lno1,
+		      *fname1 != '\0' ? " of " : "", fname1,
+		      dcc_tgts2str(tgts2_buf, sizeof(tgts2_buf),
+				   tgts2, grey_on),
+		      lno2,
+		      fname2);
+	++msgs;
+	prev_lno1 = lno1;
+	prev_fno1 = fno1;
+	prev_lno2 = lno2;
+	prev_fno2 = fno2;
+}
+
+
+
+/* rebuild the hash table and the totals and links within the database file
+ *	finish with the file locked */
+static void
+build_hash(void)
+{
+	DB_PTR rcd_pos;
+	DB_HADDR haddr_window, haddr_lo, haddr_hi;
+	int pass, total_passes;
+	int rcd_len;
+	int rcd_cks, rcd_sums;
+	DB_PTR rcds, sums;		/* passes can inflate these */
+	const DB_RCD_CK *rcd_ck;
+	DB_HADDR guess_hash_len;
+	double db_rate, hash_ratio;
+	struct timeval db_flushed;
+
+	db_buf_init(new_db_pagesize, 0);
+
+	if (new_hash_len == 0) {
+		/* Try to choose a hash table size now so that when it
+		 * is next time to rebuild after 24 hours of incoming
+		 * checksums, the alpha or load factor will still be 0.9.
+		 * We probably ran 24 hours ago, so the old hash size
+		 * is an estimate of the size tomorrow. */
+
+		/* Guess the number of distinct checksums added
+		 * tomorrow based on the current average rate */
+		db_rate = db_add_rate(&new_db_parms, 1);
+		if (db_rate > 0.0) {
+			/* Increase the average rate by 10% to account
+			 * for the 30% decrease often seen on weekends. */
+			guess_hash_len = db_rate * 1.1 * 24*60*60;
+
+			/* predict # of distinct checksums in current data */
+			hash_ratio = old_db_parms.old_kept_cks;
+			if (hash_ratio == 0.0) {
+				hash_ratio = 1.0;
+			} else {
+				hash_ratio = (HADDR2LEN(old_db_parms
+							.old_hash_used)
+					      / hash_ratio);
+				if (hash_ratio > 1.0 || hash_ratio < 0.3)
+					hash_ratio = 1.0;
+			}
+			guess_hash_len += (kept_cks * hash_ratio) + white_cks;
+
+			if (db_debug)
+				quiet_trace_msg("hash size from old=%d"
+						"  %d from db_rate=%.1f"
+						" hash_ratio=%.1f=%d/%d"
+						" kept=%d white=%d",
+						old_db_hash_used,
+						guess_hash_len,
+						db_rate, hash_ratio,
+						HADDR2LEN(old_db_parms
+							.old_hash_used),
+						old_db_parms.old_kept_cks,
+						kept_cks, white_cks);
+
+		} else {
+			/* guess if we do not have a good measure
+			 * of the recent rate */
+			guess_hash_len = kept_cks+white_cks;
+			guess_hash_len += guess_hash_len/5;
+		}
+
+		new_hash_len = old_db_hash_used;
+		if (new_hash_len < guess_hash_len)
+			new_hash_len = guess_hash_len;
+
+		/* go for load factor 0.9 */
+		new_hash_len += new_hash_len/10;
+
+		if (new_hash_len > db_max_hash_entries)
+			quiet_trace_msg("default hash size %d entries"
+					" > maximum %d",
+					new_hash_len, db_max_hash_entries);
+
+		if (grey_on) {
+			if (new_hash_len < MIN_HASH_ENTRIES)
+				new_hash_len = MIN_HASH_ENTRIES;
+		} else {
+			if (new_hash_len < DEF_HASH_ENTRIES)
+				new_hash_len = DEF_HASH_ENTRIES;
+		}
+	}
+
+	/* Open and lock the new database */
+	unlink_whine(new_hash_nm, 1);
+	new_hash_created = 1;
+	if (!db_open(0, -1, new_db_nm, new_hash_len,
+		     DB_OPEN_LOCK_NOWAIT | db_mode)) {
+		dcc_logbad(dcc_ex_code, "could not start database %s",
+			   new_db_nm);
+	}
+	if (db_debug)
+		quiet_trace_msg("%s  %s", db_window_size_str, new_db_nm);
+
+	/* guess which checksums we will keep so that we can count them */
+	if (old_db_parms.nokeep_cks != 0)
+		db_parms.nokeep_cks = old_db_parms.nokeep_cks;
+
+	/* add every record in the database file to the hash table and
+	 * fix its accumulated counts and reverse links */
+	comp_rcds = 0;
+	sums = 0;
+	rcds = 0;
+	report_progress_init();
+	db_flushed = db_time;
+
+	/* if the hash table does not fit in 75% of RAM,
+	 * then make several passes over the data with as much of the
+	 * hash table as fits. */
+	haddr_window = db_hash_page_len*((db_buf_total*3)/4);
+	if (haddr_window < db_hash_len/16)
+		haddr_window = db_hash_len/16;
+	total_passes = (db_hash_len+haddr_window-1)/haddr_window;
+
+	for (haddr_lo = 0, pass = 1;
+	     haddr_lo < db_hash_len;
+	     haddr_lo = haddr_hi, ++pass) {
+		if (haddr_lo > db_hash_len-haddr_window)
+			haddr_hi = MAX_HASH_ENTRIES;
+		else
+			haddr_hi = haddr_lo+haddr_window;
+		for (rcd_pos = DB_PTR_BASE;
+		     rcd_pos < db_csize;
+		     rcd_pos += rcd_len) {
+			/* skip reports crossing page bounardies */
+			if (rcd_pos%db_pagesize > db_page_max) {
+				rcd_len = DB_RCD_HDR_LEN;
+				continue;
+			}
+			if (--progress_rpt_cnt <= 0) {
+				report_progress(0, "  hash rebuilt",
+						"checksums",
+						sums/total_passes, kept_cks, 1);
+				if (db_time.tv_sec != db_flushed.tv_sec) {
+					db_flushed = db_time;
+					if (!db_flush_db(dcc_emsg))
+					    dcc_logbad(dcc_ex_code,
+						       "flushing after linking"
+						       L_HPAT": %s",
+						       rcd_pos, dcc_emsg);
+				}
+			}
+
+			if (!db_map_rcd(0, &db_sts.rcd, rcd_pos, &rcd_len)) {
+				dcc_logbad(dcc_ex_code,
+					   "hash build failed reading"
+					   " record at "L_HPAT,
+					   rcd_pos);
+			}
+
+			/* skip end of page padding */
+			if (db_sts.rcd.d.r->fgs_num_cks == 0)
+				continue;
+
+			++rcds;
+
+			/* count the checksums we'll link in this record */
+			rcd_cks = DB_NUM_CKS(db_sts.rcd.d.r);
+			rcd_sums = 0;
+			for (rcd_ck = db_sts.rcd.d.r->cks;
+			     rcd_ck < &db_sts.rcd.d.r->cks[rcd_cks];
+			     ++rcd_ck) {
+				if (!DB_TEST_NOKEEP(db_parms.nokeep_cks,
+						    DB_CK_TYPE(rcd_ck)))
+					++rcd_sums;
+			}
+			sums += rcd_sums;
+
+			/* Mark the record dirty so that any new hash links
+			 * get to the file if we are using -F. */
+			db_set_flush(&db_sts.rcd, 0, rcd_len);
+			if (!db_link_rcd(dcc_emsg, haddr_lo, haddr_hi)) {
+				dcc_logbad(dcc_ex_code,
+					   "relinking record at "L_HPAT": %s",
+					   rcd_pos, dcc_emsg);
+			}
+
+			/* check for conflicts in the whitelist file */
+			if (DB_RCD_ID(db_sts.rcd.d.r) == DCC_ID_WHITE)
+				check_white();
+
+			compress_old();
+		}
+
+		if (progress_rpt_started && pass < total_passes)
+			quiet_trace_msg("    pass %d", pass);
+	}
+
+	report_progress(1, "  hash rebuilt", "checksums",
+			sums/total_passes, kept_cks, 1);
+
+	db_parms.old_hash_used = db_hash_used;
+	db_parms.old_kept_cks = kept_cks;
+	db_parms.hash_used = db_hash_used;
+	db_parms.old_db_csize = db_csize;
+	if (!db_flush_parms(dcc_emsg))
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+
+	quiet_trace_msg("hashed "L_DPAT" records containing "L_DPAT" checksums,"
+			" compressed %d records",
+			rcds/total_passes, sums/total_passes, comp_rcds);
+
+	/* Try to finish as much disk I/O on the new file as we can to minimize
+	 * stalling by dccd when we close the file and hand it over.  This also
+	 * reduces system stalling hours later when dbclean runs again. */
+	if (!make_clean(1))
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+
+
+	quiet_trace_msg("%d hash entries total, %d or %d%% used",
+			HADDR2LEN(db_hash_len),
+			HADDR2LEN(db_hash_used),
+			(int)((HADDR2LEN(db_hash_used)*100.0)
+			      / HADDR2LEN(db_hash_len)));
+}
+
+
+
+static u_char
+write_new_db(const void *buf, int buflen, off_t pos, u_char fatal)
+{
+	int i;
+
+	if (pos != lseek(new_db_fd, pos, SEEK_SET)) {
+		if (fatal) {
+			dcc_logbad(EX_IOERR, "lseek(%s, 0): %s",
+				   new_db_nm, ERROR_STR());
+		} else {
+			dcc_error_msg("lseek(%s, 0): %s",
+				      new_db_nm, ERROR_STR());
+		}
+		return 0;
+	}
+
+	i = write(new_db_fd, buf, buflen);
+	if (i == buflen) {
+		if (new_db_fsize < pos+buflen)
+			new_db_fsize = pos+buflen;
+		return 1;
+	}
+
+	if (fatal) {
+		if (i < 0)
+			dcc_logbad(EX_IOERR, "write(%s): %s",
+				   new_db_nm, ERROR_STR());
+		else
+			dcc_logbad(EX_IOERR, "write(%s)=%d instead of %d",
+				   new_db_nm, i, buflen);
+	} else {
+		if (i < 0)
+			dcc_error_msg("write(%s): %s",
+				      new_db_nm, ERROR_STR());
+		else
+			dcc_error_msg("write(%s)=%d instead of %d",
+				      new_db_nm, i, buflen);
+	}
+	return 0;
+}
+
+
+
+/* use a large buffer to encourage the file system to avoid fragmentation */
+static union {
+    u_char  c[DB_MIN_MIN_MBYTE*(1024*1024)/4];
+    DB_HDR  hdr;
+} write_new_db_buf;
+static u_int write_new_db_buflen = 0;
+static DB_PTR write_new_base;
+
+static u_char
+write_new_flush(u_char fatal)
+{
+	u_char result = 1;
+
+	if (write_new_db_buflen != 0) {
+		if (!write_new_db(&write_new_db_buf, write_new_db_buflen,
+				  write_new_base, fatal))
+			result = 0;
+	}
+
+	write_new_base = new_db_csize;
+	write_new_db_buflen = 0;
+	return result;
+}
+
+
+static u_char
+write_new_buf(const void *buf, int buflen)
+{
+	if (write_new_db_buflen + buflen > ISZ(write_new_db_buf)
+	    && !write_new_flush(0))
+		return 0;
+
+	memcpy(&write_new_db_buf.c[write_new_db_buflen], buf, buflen);
+	write_new_db_buflen += buflen;
+	return 1;
+}
+
+
+
+/* add a record to the new file */
+static u_char
+write_new_rcd(const void *buf, int buflen)
+{
+	static const u_char zeros[DB_RCD_LEN_MAX] = {0};
+	DB_PTR new_page_num;
+	u_char result;
+	int pad, i;
+
+	/* pad accross page boundaries */
+	new_page_num = DB_PTR2PG_NUM(new_db_csize + buflen, new_db_pagesize);
+	if (new_page_num != DB_PTR2PG_NUM(new_db_csize, new_db_pagesize)) {
+		pad = new_page_num*new_db_pagesize - new_db_csize;
+		pad = (((pad + DB_RCD_HDR_LEN-1) / DB_RCD_HDR_LEN)
+		       * DB_RCD_HDR_LEN);
+		do {
+			i = sizeof(zeros);
+			if (i > pad)
+				i = pad;
+			if (!write_new_buf(zeros, i))
+				return 0;
+			pad -= i;
+			new_db_csize += i;
+		} while (pad != 0);
+	}
+
+	result = write_new_buf(buf, buflen);
+	new_db_csize += buflen;
+	return result;
+}
+
+
+
+/* write the magic string at the head of the database file */
+static void
+write_new_hdr(u_char emptied)
+{
+	DB_HDR *new;
+	struct timeval old_sn;
+	time_t new_rate_secs;
+	DCC_CK_TYPES type;
+	int i;
+
+	write_new_flush(1);
+
+	memset(&write_new_db_buf, 0, sizeof(write_new_db_buf));
+	write_new_base = 0;
+	if (new_db_fsize > ISZ(DB_HDR)
+	    || new_db_pagesize == 0) {
+		write_new_db_buflen = sizeof(DB_HDR);
+	} else {
+		write_new_db_buflen = new_db_pagesize;
+		if (write_new_db_buflen > ISZ(write_new_db_buf))
+			write_new_db_buflen = ISZ(write_new_db_buf);
+	}
+
+	new = &write_new_db_buf.hdr;
+	memset(new, 0, sizeof(*new));
+	memcpy(new->p.version, db_version_buf, sizeof(new->p.version));
+
+	dcc_timeval2ts(&new->p.sn, &clean_start, 0);
+	if (emptied) {
+		new->p.cleared = clean_start.tv_sec;
+	} else {
+		new->p.cleared = old_db_parms.cleared;
+		switch (clean_mode) {
+		case NORMAL_MODE:
+			new->p.cleaned = clean_start.tv_sec;
+			new->p.cleaned_cron = clean_start.tv_sec;
+			break;
+		case NO_CRON_MODE:
+			new->p.cleaned = clean_start.tv_sec;
+			new->p.cleaned_cron = old_db_parms.cleaned_cron;
+			break;
+		case REPAIR_MODE:
+		case QUICK_MODE:
+		case HASH_MODE:
+		case DEL_MODE:
+			new->p.cleaned = old_db_parms.cleaned;
+			new->p.cleaned_cron = old_db_parms.cleaned_cron;
+			break;
+		}
+	}
+
+	if (grey_on)
+		new->p.flags |= DB_PARM_FG_GREY;
+	if (emptied || (old_db_parms.flags & DB_PARM_FG_CLEARED))
+		new->p.flags |= DB_PARM_FG_CLEARED;
+	if (have_expire_parms > 0
+	    || (have_expire_parms < 0
+		&& (old_db_parms.flags & DB_PARM_EXP_SET)))
+		new->p.flags |= DB_PARM_EXP_SET;
+
+	new->p.nokeep_cks = (emptied || old_db_parms.nokeep_cks == 0
+			     ? def_nokeep_cks()
+			     : old_db_parms.nokeep_cks);
+
+	new->p.pagesize = new_db_pagesize;
+	new->p.db_csize = new_db_csize;
+
+	/* update the traffic counts */
+	if (!emptied
+	    && old_db_parms.db_csize != 0
+	    && old_db_parms.db_csize >= old_db_parms.old_db_csize
+	    && old_db_parms.hash_used != 0
+	    && old_db_parms.hash_used >= old_db_parms.old_hash_used) {
+		if (old_db_parms.rate_secs > 0
+		    && old_db_parms.rate_secs <= DB_MAX_RATE_SECS) {
+			new->p.rate_secs = old_db_parms.rate_secs;
+			new->p.db_added = old_db_parms.db_added;
+			new->p.hash_added = old_db_parms.hash_added;
+		}
+		new->p.last_rate_sec = clean_start.tv_sec;
+		dcc_ts2timeval(&old_sn, &old_db_parms.sn);
+		new_rate_secs = clean_start.tv_sec - old_sn.tv_sec;
+		if (new_rate_secs > 0 && new_rate_secs <= DB_MAX_RATE_SECS) {
+			new_rate_secs += new->p.rate_secs;
+			if (new_rate_secs > DB_MAX_RATE_SECS) {
+				double trim, new_val;
+				trim = DB_MAX_RATE_SECS;
+				trim /= new_rate_secs;
+
+				new_val = new->p.db_added;
+				new_val *= trim;
+				new->p.db_added = new_val;
+
+				new_val = new->p.hash_added;
+				new_val *= trim;
+				new->p.hash_added = new_val;
+
+				new_rate_secs = DB_MAX_RATE_SECS;
+			}
+			new->p.db_added += (old_db_parms.db_csize
+					    - old_db_parms.old_db_csize);
+			new->p.hash_added += (old_db_parms.hash_used
+					      - old_db_parms.old_hash_used);
+			new->p.rate_secs = new_rate_secs;
+		}
+	}
+
+	for (type = DCC_CK_TYPE_FIRST; type <= DCC_CK_TYPE_LAST; ++type) {
+		if (new_ex_secs[type].all != 0) {
+			new->p.ex_secs[type].all = new_ex_secs[type].all;
+			new->p.ex_secs[type].spam = new_ex_secs[type].spam;
+			new->p.ex_all[type] = new_ex_ts[type].all;
+			new->p.ex_spam[type] = new_ex_ts[type].spam;
+		} else {
+			new->p.ex_secs[type].all = def_expire_secs;
+			new->p.ex_secs[type].spam = (DCC_CK_LONG_TERM(type)
+						     ? def_expire_spamsecs
+						     : def_expire_secs);
+		}
+	}
+
+	new_db_parms = new->p;
+
+	for (;;) {
+		write_new_flush(1);
+
+		/* ensure that the last page of the file is complete */
+		if (new_db_pagesize == 0)
+			break;
+		i = new_db_fsize % new_db_pagesize;
+		if (i == 0)
+			break;
+		write_new_db_buflen = new_db_pagesize - i;
+		if (write_new_db_buflen > ISZ(write_new_db_buf))
+			write_new_db_buflen = ISZ(write_new_db_buf);
+		memset(&write_new_db_buf, 0, write_new_db_buflen);
+		write_new_base = new_db_fsize;
+	}
+}
+
+
+
+static void
+unlink_whine(const char *nm, u_char enoent_ok)
+{
+	if (0 > unlink(nm)
+	    && (!enoent_ok || errno != ENOENT))
+		dcc_error_msg("unlink(%s): %s", nm, ERROR_STR());
+}
+
+
+
+static void
+rename_bail(const char *from, const char *to)
+{
+	if (0 > rename(from, to))
+		dcc_logbad(EX_IOERR, "rename(%s, %s): %s",
+			   from, to, ERROR_STR());
+}
+
+
+
+/* try for a long time or until the server hears */
+static u_char				/* 1=ok, 0=failed */
+persist_aop(DCC_AOPS aop, u_int32_t val1,
+	    int secs)			/* try for this long */
+{
+	return dcc_aop_persist(dcc_emsg, ctxt,
+			       grey_on ? DCC_CLNT_FG_GREY : 0,
+			       db_debug != 0,
+			       aop, val1, secs, &aop_resp);
+}
+
+
+
+/* tell the daemon to switch to the new database */
+static void
+dccd_new_db(const char *msg)
+{
+	/* Send a round of NOPs and ask about status to ensure the server
+	 * has dealt with requests that arrived while we had the database
+	 * locked and otherwise caught up.  We want to try to ensure that
+	 * the server is listening when we re-open the database so that
+	 * it does not leave flooding off.
+	 * On some systems with lame mmap() support including BSD/OS, the
+	 * the daemon can stall for minutes in close().  If that or something
+	 * else makes the daemon stall, this can appear to fail. */
+	if (!persist_aop(DCC_AOP_FLOD, DCC_AOP_FLOD_LIST, RESTART_DELAY))
+		dcc_error_msg("%s: %s; continuing", msg, dcc_emsg);
+
+	dccd_unlocked = 0;
+	if (!persist_aop(DCC_AOP_DB_NEW, 0, RESTART_DELAY)) {
+		/* This cannot be a fatal error,
+		 * lest we leave the database broken */
+		dcc_error_msg("%s: %s; continuing", msg, dcc_emsg);
+	}
+}
+
+
+
+static void
+finish(void)
+{
+	int bailing = 0;
+
+	/* delete the new files */
+#ifndef DCC_DBCLEAN_KEEP_NEW			/* for debugging */
+	if (new_db_created) {
+		unlink_whine(new_db_nm, 0);
+		new_db_created = 0;
+		bailing = -1;
+	}
+	/* we don't really know if the new hash file was created,
+	 * so don't worry about problems */
+	if (new_hash_created) {
+		unlink_whine(new_hash_nm, 1);
+		new_hash_created = 0;
+		bailing = -1;
+	}
+#endif
+	if (cur_db_created) {
+		unlink_whine(cur_db_nm, 0);
+		unlink_whine(cur_hash_nm, 1);
+		cur_db_created = 0;
+		bailing = -1;
+	}
+
+	if (new_db_fd >= 0) {
+		if (0 > close(new_db_fd))
+			dcc_error_msg("close(%s): %s",
+				      new_db_nm, ERROR_STR());
+		new_db_fd = -1;
+	}
+	if (old_db_fd >= 0) {
+		/* In most cases nothing cares about the old database now.
+		 * We often have kept the old database open and locked until
+		 * now.  Delete it unless we are debugging */
+		if (db_debug < 4 && exit_value == EX_OK) {
+			unlink_whine(old_db_nm, 0);
+		} else {
+			/* Push it to the disk so it won't lurk in the buffer
+			 * cache or elsewhere to slow a system reboot */
+			if (exit_value == EX_OK
+			    && 0 > fsync(old_db_fd))
+				dcc_error_msg("fsync(%s): %s",
+					      old_db_nm, ERROR_STR());
+		}
+		if (0 > close(old_db_fd))
+			dcc_error_msg("close(%s): %s",
+				      old_db_nm, ERROR_STR());
+		old_db_fd = -1;
+	}
+	flod_unmap(0, 0);
+
+	/* release the daemon, but if the database is still open, it's bad */
+	db_close(bailing);
+	/* tell the daemon to switch databases */
+	if (dccd_unlocked)
+		dccd_new_db("finish");
+
+	while (flods_off > 0) {
+		--flods_off;
+		if (!persist_aop(DCC_AOP_FLOD, DCC_AOP_FLOD_RESUME,
+				 RESTART_DELAY))
+			dcc_error_msg("%s", dcc_emsg);
+	}
+
+	unlock_dbclean();
+}
+
+
+
+static void NRATTRIB
+exit_dbclean(int v)
+{
+	exit(exit_value = v);
+}
+
+
+
+/* terminate with a signal */
+static void NRATTRIB
+sigterm(int s)
+{
+	dcc_error_msg("interrupted by signal %d", s);
+	exit_dbclean(s+100);
+}
diff -r 000000000000 -r c7f6b056b673 dblist.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dblist.0	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,152 @@
+dblist(8)             Distributed Checksum Clearinghouse             dblist(8)
+
+NNAAMMEE
+     ddbblliisstt -- Database List Distributed Checksum Clearinghouse
+
+SSYYNNOOPPSSIISS
+     ddbblliisstt [--vvVVHHDD] [--GG _o_n | _o_f_f] [--hh _h_o_m_e_d_i_r]
+            [--ss [_s_e_r_v_e_r_-_I_D][_,_s_e_r_v_e_r_-_a_d_d_r][_,_s_e_r_v_e_r_-_p_o_r_t]]
+            [--CC _'_t_y_p_e _h_1 _h_2 _h_3 _h_4_'] [--II _s_e_r_v_e_r_-_I_D] [--AA _d_b_a_d_d_r] [--LL _p_a_t_h_l_e_n]
+            [--PP _p_a_g_e_s] [--TT _t_i_m_e_s_t_a_m_p] [_f_i_l_e_1 _f_i_l_e_2 _._._.]
+
+DDEESSCCRRIIPPTTIIOONN
+     DDbblliisstt lists the contents of a DCC database as it does some consistency
+     checking.
+
+     --vv   lists more of the database.  Additional information is produced with
+          additional --vv arguments.
+
+     --VV   displays the version of the DCC database lister.
+
+     --HH   turns off the listing of the hash table as well as the analysis of
+          the hash table.  Determining the worst case and average lengths of
+          chains in the hash table can take a long time for a large database
+          on a small computer.
+
+     --DD   turns off the listing of the data or checksum records.
+
+     --GG _o_n
+          lists a greylist database.
+
+     --hh _h_o_m_e_d_i_r
+          overrides the default DCC home directory, _/_v_a_r_/_d_c_c.
+
+     --ss [_s_e_r_v_e_r_-_I_D][_,_s_e_r_v_e_r_-_a_d_d_r][_,_s_e_r_v_e_r_-_p_o_r_t]
+          somewhat quiets the DCC server process, dccd(8), to get somewhat
+          more consistent results.  _s_e_r_v_e_r_-_I_D must be in the _i_d_s file.
+          _s_e_r_v_e_r_-_a_d_d_r and _s_e_r_v_e_r_-_p_o_r_t are the IP address and UDP port at which
+          the server process listens.
+
+     --CC _'_t_y_p_e _h_1 _h_2 _h_3 _h_4_'
+          limits the listing to records containing that checksum or one of the
+          other checksums specified with --CC.  If the four hexadecimal values
+          _h_1 _h_2 _h_3 _h_4 are absent, records with the matching _t_y_p_e will be
+          listed.  If _t_y_p_e is absent, any checksum with the four hexadecimal
+          values will be listed.  As many as 16 checksums can be specified.
+
+     --II _s_e_r_v_e_r_-_I_D
+          limits the listing to records with that server-ID or one of the
+          other server-IDs specified with --II.  As many as 16 server-IDs can be
+          specified.
+
+     --AA _d_b_a_d_d_r
+          excludes database records before _d_b_a_d_d_r.
+
+     --LL _p_a_t_h_l_e_n
+          excludes records with path lengths shorter than _p_a_t_h_l_e_n.
+
+     --PP _p_a_g_e_s
+          ignores all but the last _p_a_g_e_s of the database.
+
+     --TT _t_i_m_e_t_a_m_p
+          excludes records with other timestamps.  A timestamp with a missing
+          microsecond value matches any record with that second.  As many as
+          16 timestamps can be specified.
+
+     _f_i_l_e_1 _f_i_l_e_2 _._._.
+          are names of databases to be listed.  The default is _d_c_c___d_b and its
+          companion, _d_c_c___d_b_._h_a_s_h in the DCC home directory.
+
+     By default, the sizes of the main file and the hash table as well as how
+     much they contain and values related to the performance of the hash are
+     displayed.
+
+     With a single --vv, most of the mail database file and the contents of mem-
+     ory mapped server flooding positions in the _f_l_o_d_._m_a_p file  are listed.
+     The listing starts with the serial number of the database file which is
+     when old entries were last removed from it by dbclean(8) That is followed
+     by similar lines showing the oldest timestamp of checksums not expired by
+     dbclean and of mail that is not "spam."
+
+     The flooding positions from the _f_l_o_d_._m_a_p file are record offsets or
+     addresses in the main database file.
+
+     A typical record in the main database file looks like:
+
+     02/07/02 20:25:12.497032    5    auth 1601              2fe5b94
+          path: 103<-101<-1601
+       Body      6       e2d3f96a c65aea01 3fece361 edff9ecf  2f21364 772d2
+       Fuz1      many    6ff56fe8 ffc312d7 a5fe8f13 12a537ae  2f21364 200a9
+       Fuz2      many    fac882b8 03eea34f bd792c40 2fe6fd54  2f21364 72816
+
+     That example was received by a DCC server with server-ID _1_6_0_1 at about
+     8:25 UTC on the evening of February 7, 2000.  The report was about a mail
+     message set to _5 addressees.  The report was from a client that presented
+     a client-ID and matching password that the server recognized or authenti-
+     cated.  The report was then sent or `flooded' to the server with server-
+     ID _1_0_1 which in turn sent it to a server with server-ID _1_0_3.  That server
+     sent it to the local DCC server.  The record is at the address _0_x_2_f_e_5_b_9_4
+     in the database.  The record contains 3 checksums.  The simple checksum
+     of the body of the message was _e_2_d_3_f_9_6_a _c_6_5_a_e_a_0_1 _3_f_e_c_e_3_6_1 _e_d_f_f_9_e_c_f The
+     total number of recipients of messages with this body checksum known in
+     the database is _6, which implies this checksum had been previously
+     reported with a target count of 1.  The previous report in the database
+     of a message with this body checksum is at _0_x_2_f_2_1_3_6_4.  The hash table
+     entry for this body checksum is at _0_x_7_7_2_d_2.  This report included two
+     fuzzy checksums.  Both have been previously reported as having been sent
+     to _m_a_n_y targets.
+
+     An asterisk (*) before the name of the checksum would indicate that a
+     later record in the database makes this checksum redundant.  A report of
+     _m_a_n_y addressees makes all preceding reports redundant.
+
+     The string _t_r_i_m_m_e_d after the server-ID marks older reports that have had
+     uninteresting checksums removed.  The string _c_o_m_p_r_e_s_s_e_d after the server-
+     ID would indicate that this older report has been trimmed and compressed
+     with older reports.
+
+     With two --vv arguments, records added to the database by dbclean(8) from
+     the server whitelist are also displayed.
+
+     Three --vv arguments cause the hash table to be displayed.  Three typical
+     hash table entries look like:
+
+           19b8:   19ee   19b7
+           19b9:   19c0      0    90120 Fuz1
+           19ba:      0      0  1b72300 Fuz1
+
+     The entry in slot number _0_x_1_9_b_8 is unused or free.  Slot number _0_x_1_9_b_9 is
+     the start of a chain of collisions or entries with the same hash value of
+     0x19b9.  The next slot in this chain is at _0_x_1_9_c_0.  The corresponding
+     _F_u_z_1
+      checksum is at _0_x_9_0_1_2 in the database.  The third slot at _0_x_1_9_b_a is also
+     that of a _F_u_z_1 checksum, but it is not part of a hash chain and its data-
+     base record is at _0_x_1_b_7_2_3_0_0.
+
+FFIILLEESS
+     /var/dcc     is the DCC home directory containing data and control files.
+     dcc_db grey_dcc_db
+                  main file of checksums.
+     dcc_db.hash grey_dcc_db.hash
+                  database hash table.
+     flod.map grey_flod.map
+                  memory mapped flooding positions.
+
+SSEEEE AALLSSOO
+     cdcc(8), dcc(8), dbclean(8), dccd(8), dccifd(8), dccm(8), dccproc(8).
+
+HHIISSTTOORRYY
+     Implementation of ddbblliisstt was started at Rhyolite Software, in 2000.  This
+     document describes version 1.3.103.
+
+                               February 26, 2009
diff -r 000000000000 -r c7f6b056b673 dblist.8.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dblist.8.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,300 @@
+.\" Copyright (c) 2008 by Rhyolite Software, LLC
+.\"
+.\" This agreement is not applicable to any entity which sells anti-spam
+.\" solutions to others or provides an anti-spam solution as part of a
+.\" security solution sold to other entities, or to a private network
+.\" which employs the DCC or uses data provided by operation of the DCC
+.\" but does not provide corresponding data to other users.
+.\"
+.\" Permission to use, copy, modify, and distribute this software without
+.\" changes for any purpose with or without fee is hereby granted, provided
+.\" that the above copyright notice and this permission notice appear in all
+.\" copies and any distributed versions or copies are either unchanged
+.\" or not called anything similar to "DCC" or "Distributed Checksum
+.\" Clearinghouse".
+.\"
+.\" Parties not eligible to receive a license under this agreement can
+.\" obtain a commercial license to use DCC by contacting Rhyolite Software
+.\" at sales@rhyolite.com.
+.\"
+.\" A commercial license would be for Distributed Checksum and Reputation
+.\" Clearinghouse software.  That software includes additional features.  This
+.\" free license for Distributed ChecksumClearinghouse Software does not in any
+.\" way grant permision to use Distributed Checksum and Reputation Clearinghouse
+.\" software
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+.\" 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.
+.\"
+.\" Rhyolite Software DCC 1.3.103-1.39 $Revision$
+.\"
+.Dd February 26, 2009
+.ds volume-ds-DCC Distributed Checksum Clearinghouse
+.Dt dblist 8 DCC
+.Os " "
+.Sh NAME
+.Nm dblist
+.Nd Database List Distributed Checksum Clearinghouse
+.Sh SYNOPSIS
+.Nm dblist
+.Op Fl vVHD
+.Op Fl G Ar on | off
+.Op Fl h Ar homedir
+.br
+.Oo
+.Fl s Xo
+.Sm off
+.Op Ar server-ID
+.Op Ar ,server-addr
+.Op Ar ,server-port
+.Xc
+.Sm on
+.Oc
+.br
+.Op Fl C Ar 'type h1 h2 h3 h4'
+.Op Fl I Ar server-ID
+.Op Fl A Ar dbaddr
+.Op Fl L Ar pathlen
+.br
+.Op Fl P Ar pages
+.Op Fl T Ar timestamp
+.Op Ar file1 file2 ...
+.Sh DESCRIPTION
+.Nm Dblist
+lists the contents of a DCC database as it does some consistency
+checking.
+.Bl -tag -width 3n
+.It Fl v
+lists more of the database.
+Additional information is produced with additional
+.Fl v
+arguments.
+.It Fl V
+displays the version of the DCC database lister.
+.It Fl H
+turns off the listing of the hash table as well as the analysis
+of the hash table.
+Determining the worst case and average lengths of chains in the
+hash table can take a long time for a large database on a small computer.
+.It Fl D
+turns off the listing of the data or checksum records.
+.It Fl G Ar on
+lists a greylist database.
+.It Fl h Ar homedir
+overrides the default DCC home directory,
+.Pa @prefix@ .
+.It Fl s Xo
+.Sm off
+.Op Ar server-ID
+.Op Ar ,server-addr
+.Op Ar ,server-port
+.Sm on
+.Xc
+somewhat quiets the DCC server process,
+.Xr dccd 8 ,
+to get somewhat more consistent results.
+.Ar server-ID
+must be in the
+.Pa ids
+file.
+.Ar server-addr
+and
+.Ar server-port
+are the IP address and UDP port at which the server process listens.
+.It Fl C Ar 'type h1 h2 h3 h4'
+limits the listing to records containing that checksum or one of
+the other checksums specified with
+.Fl C .
+If the four hexadecimal values
+.Ar h1 h2 h3 h4
+are absent,
+records with the matching
+.Ar type
+will be listed.
+If
+.Ar type
+is absent, any checksum with the four hexadecimal values will be listed.
+As many as 16 checksums can be specified.
+.It Fl I Ar server-ID
+limits the listing to records with that server-ID or one of the other
+server-IDs specified with
+.Fl I .
+As many as 16 server-IDs can be specified.
+.It Fl A Ar dbaddr
+excludes database records before
+.Ar dbaddr .
+.It Fl L Ar pathlen
+excludes records with path lengths shorter than
+.Ar pathlen .
+.It Fl P Ar pages
+ignores all but the last
+.Ar pages
+of the database.
+.It Fl T Ar timetamp
+excludes records with other timestamps.
+A timestamp with a missing microsecond value matches
+any record with that second.
+As many as 16
+timestamps can be specified.
+.It Ar file1 file2 ...
+are names of databases to be listed.
+The default is
+.Pa dcc_db
+and its companion,
+.Pa dcc_db.hash
+in the DCC home directory.
+.El
+.Pp
+By default, the sizes of the main file and the hash table as well
+as how much they contain and values related to the performance of
+the hash are displayed.
+.Pp
+With a single
+.Fl v ,
+most of the mail database file and the contents of memory
+mapped server flooding positions in the
+.Pa flod.map
+file  are listed.
+The listing starts with the serial number of the database file
+which is when old entries were last removed from it by
+.Xr dbclean 8
+That is followed by similar lines showing the oldest timestamp
+of checksums not expired by dbclean
+and of mail that is not "spam."
+.Pp
+The flooding positions from the
+.Pa flod.map
+file are record offsets or addresses in the main database file.
+.Pp
+A typical record in the main database file looks like:
+.Bd -literal -offset 2
+02/07/02 20:25:12.497032    5	 auth 1601		2fe5b94
+     path: 103<-101<-1601
+  Body	    6	    e2d3f96a c65aea01 3fece361 edff9ecf  2f21364 772d2
+  Fuz1	    many    6ff56fe8 ffc312d7 a5fe8f13 12a537ae  2f21364 200a9
+  Fuz2	    many    fac882b8 03eea34f bd792c40 2fe6fd54  2f21364 72816
+.Ed
+.Pp
+That example
+was received by a DCC server with server-ID
+.Em 1601
+at about 8:25 UTC on the evening of February 7, 2000.
+The report was about a mail message set to
+.Em 5
+addressees.
+The report was from a client that presented a client-ID and matching
+password that the server recognized or authenticated.
+The report was then sent or
+.Sq flooded
+to the server with server-ID
+.Em 101
+which in turn sent it to a server with server-ID
+.Em 103 .
+That server sent it to the local DCC server.
+The record is at the address
+.Em 0x2fe5b94
+in the database.
+The record contains 3 checksums.
+The simple checksum of the body of the message was
+.Em e2d3f96a c65aea01 3fece361 edff9ecf
+The total number of recipients of messages with this body checksum
+known in the database is
+.Em 6 ,
+which implies this checksum had been previously reported with a target
+count of 1.
+The previous report in the database of a message with this body checksum
+is at
+.Em 0x2f21364 .
+The hash table entry for this body checksum is at
+.Em 0x772d2 .
+This report included two fuzzy checksums.
+Both have been previously reported as having been sent to
+.Em many
+targets.
+.Pp
+An asterisk (*) before the name of the checksum
+would indicate that a later record in the database makes this
+checksum redundant.
+A report of
+.Em many
+addressees makes all preceding reports redundant.
+.Pp
+The string
+.Em trimmed
+after the server-ID
+marks older reports that have had uninteresting checksums removed.
+The string
+.Em compressed
+after the server-ID
+would indicate that this older report has been trimmed and compressed with
+older reports.
+.Pp
+With two
+.Fl v
+arguments,
+records added to the database by
+.Xr dbclean 8
+from the server whitelist are also displayed.
+.Pp
+Three
+.Fl v
+arguments cause the hash table to be displayed.
+Three typical hash table entries look like:
+.Bd -literal -offset 2
+      19b8:   19ee   19b7
+      19b9:   19c0      0    90120 Fuz1
+      19ba:      0      0  1b72300 Fuz1
+.Ed
+.Pp
+The entry in slot number
+.Em 0x19b8
+is unused or free.
+Slot number
+.Em 0x19b9
+is the start of a chain of collisions or entries
+with the same hash value of 0x19b9.
+The next slot in this chain is at
+.Em 0x19c0 .
+The corresponding
+.Em Fuz1
+ checksum is at
+.Em 0x9012
+in the database.
+The third slot at
+.Em 0x19ba
+is also that of a
+.Em Fuz1
+checksum,
+but it is not part of a hash chain and its database record
+is at
+.Em 0x1b72300 .
+.Sh FILES
+.Bl -tag -width dcc_db.hash -compact
+.It Pa @prefix@
+is the DCC home directory containing data and control files.
+.It Pa dcc_db grey_dcc_db
+main file of checksums.
+.It Pa dcc_db.hash grey_dcc_db.hash
+database hash table.
+.It Pa flod.map grey_flod.map
+memory mapped flooding positions.
+.El
+.Sh SEE ALSO
+.Xr cdcc 8 ,
+.Xr dcc 8 ,
+.Xr dbclean 8 ,
+.Xr dccd 8 ,
+.Xr dccifd 8 ,
+.Xr dccm 8 ,
+.Xr dccproc 8 .
+.Sh HISTORY
+Implementation of
+.Nm
+was started at Rhyolite Software, in 2000.
+This document describes version 1.3.103.
diff -r 000000000000 -r c7f6b056b673 dblist.html.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dblist.html.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,198 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<HTML>
+<HEAD>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <TITLE>dblist.0.8</TITLE>
+    <META http-equiv="Content-Style-Type" content="text/css">
+    <STYLE type="text/css">
+	BODY {background-color:white; color:black}
+	ADDRESS {font-size:smaller}
+        IMG.logo {width:6em; vertical-align:middle}
+    </STYLE>
+</HEAD>
+<BODY>
+<PRE>
+<!-- Manpage converted by man2html 3.0.1 -->
+<B><A HREF="dblist.html">dblist(8)</A></B>             Distributed Checksum Clearinghouse             <B><A HREF="dblist.html">dblist(8)</A></B>
+
+
+</PRE>
+<H2><A NAME="NAME">NAME</A></H2><PRE>
+     <B>dblist</B> -- Database List Distributed Checksum Clearinghouse
+
+
+</PRE>
+<H2><A NAME="SYNOPSIS">SYNOPSIS</A></H2><PRE>
+     <B>dblist</B> [<B>-vVHD</B>] [<B>-G</B> <I>on</I> | <I>off</I>] [<B>-h</B> <I>homedir</I>]
+            [<B>-s</B> [<I>server-ID</I>][<I>,server-addr</I>][<I>,server-port</I>]]
+            [<B>-C</B> <I>'type</I> <I>h1</I> <I>h2</I> <I>h3</I> <I>h4'</I>] [<B>-I</B> <I>server-ID</I>] [<B>-A</B> <I>dbaddr</I>] [<B>-L</B> <I>pathlen</I>]
+            [<B>-P</B> <I>pages</I>] [<B>-T</B> <I>timestamp</I>] [<I>file1</I> <I>file2</I> <I>...</I>]
+
+
+</PRE>
+<H2><A NAME="DESCRIPTION">DESCRIPTION</A></H2><PRE>
+     <B>Dblist</B> lists the contents of a DCC database as it does some consistency
+     checking.
+
+     <A NAME="OPTION-v"><B>-v</B></A>   lists more of the database.  Additional information is produced with
+          additional <B>-v</B> arguments.
+
+     <A NAME="OPTION-V"><B>-V</B></A>   displays the version of the DCC database lister.
+
+     <A NAME="OPTION-H"><B>-H</B></A>   turns off the listing of the hash table as well as the analysis of
+          the hash table.  Determining the worst case and average lengths of
+          chains in the hash table can take a long time for a large database
+          on a small computer.
+
+     <A NAME="OPTION-D"><B>-D</B></A>   turns off the listing of the data or checksum records.
+
+     <A NAME="OPTION-G"><B>-G</B></A> <I>on</I>
+          lists a greylist database.
+
+     <A NAME="OPTION-h"><B>-h</B></A> <I>homedir</I>
+          overrides the default DCC home directory, <I>@prefix@</I>.
+
+     <A NAME="OPTION-s"><B>-s</B></A> [<I>server-ID</I>][<I>,server-addr</I>][<I>,server-port</I>]
+          somewhat quiets the DCC server process, <B><A HREF="dccd.html">dccd(8)</A></B>, to get somewhat
+          more consistent results.  <I>server-ID</I> must be in the <I>ids</I> file.
+          <I>server-addr</I> and <I>server-port</I> are the IP address and UDP port at which
+          the server process listens.
+
+     <A NAME="OPTION-C"><B>-C</B></A> <I>'type</I> <I>h1</I> <I>h2</I> <I>h3</I> <I>h4'</I>
+          limits the listing to records containing that checksum or one of the
+          other checksums specified with <B>-C</B>.  If the four hexadecimal values
+          <I>h1</I> <I>h2</I> <I>h3</I> <I>h4</I> are absent, records with the matching <I>type</I> will be
+          listed.  If <I>type</I> is absent, any checksum with the four hexadecimal
+          values will be listed.  As many as 16 checksums can be specified.
+
+     <A NAME="OPTION-I"><B>-I</B></A> <I>server-ID</I>
+          limits the listing to records with that server-ID or one of the
+          other server-IDs specified with <B>-I</B>.  As many as 16 server-IDs can be
+          specified.
+
+     <A NAME="OPTION-A"><B>-A</B></A> <I>dbaddr</I>
+          excludes database records before <I>dbaddr</I>.
+
+     <A NAME="OPTION-L"><B>-L</B></A> <I>pathlen</I>
+          excludes records with path lengths shorter than <I>pathlen</I>.
+
+     <A NAME="OPTION-P"><B>-P</B></A> <I>pages</I>
+          ignores all but the last <I>pages</I> of the database.
+
+     <A NAME="OPTION-T"><B>-T</B></A> <I>timetamp</I>
+          excludes records with other timestamps.  A timestamp with a missing
+          microsecond value matches any record with that second.  As many as
+          16 timestamps can be specified.
+
+     <I>file1</I> <I>file2</I> <I>...</I>
+          are names of databases to be listed.  The default is <I>dcc</I><B>_</B><I>db</I> and its
+          companion, <I>dcc</I><B>_</B><I>db.hash</I> in the DCC home directory.
+
+     By default, the sizes of the main file and the hash table as well as how
+     much they contain and values related to the performance of the hash are
+     displayed.
+
+     With a single <B>-v</B>, most of the mail database file and the contents of mem-
+     ory mapped server flooding positions in the <I>flod.map</I> file  are listed.
+     The listing starts with the serial number of the database file which is
+     when old entries were last removed from it by <B><A HREF="dbclean.html">dbclean(8)</A></B> That is followed
+     by similar lines showing the oldest timestamp of checksums not expired by
+     dbclean and of mail that is not "spam."
+
+     The flooding positions from the <I>flod.map</I> file are record offsets or
+     addresses in the main database file.
+
+     A typical record in the main database file looks like:
+
+     02/07/02 20:25:12.497032    5    auth 1601              2fe5b94
+          path: 103&lt;-101&lt;-1601
+       Body      6       e2d3f96a c65aea01 3fece361 edff9ecf  2f21364 772d2
+       Fuz1      many    6ff56fe8 ffc312d7 a5fe8f13 12a537ae  2f21364 200a9
+       Fuz2      many    fac882b8 03eea34f bd792c40 2fe6fd54  2f21364 72816
+
+     That example was received by a DCC server with server-ID <I>1601</I> at about
+     8:25 UTC on the evening of February 7, 2000.  The report was about a mail
+     message set to <I>5</I> addressees.  The report was from a client that presented
+     a client-ID and matching password that the server recognized or authenti-
+     cated.  The report was then sent or `flooded' to the server with server-
+     ID <I>101</I> which in turn sent it to a server with server-ID <I>103</I>.  That server
+     sent it to the local DCC server.  The record is at the address <I>0x2fe5b94</I>
+     in the database.  The record contains 3 checksums.  The simple checksum
+     of the body of the message was <I>e2d3f96a</I> <I>c65aea01</I> <I>3fece361</I> <I>edff9ecf</I> The
+     total number of recipients of messages with this body checksum known in
+     the database is <I>6</I>, which implies this checksum had been previously
+     reported with a target count of 1.  The previous report in the database
+     of a message with this body checksum is at <I>0x2f21364</I>.  The hash table
+     entry for this body checksum is at <I>0x772d2</I>.  This report included two
+     fuzzy checksums.  Both have been previously reported as having been sent
+     to <I>many</I> targets.
+
+     An asterisk (*) before the name of the checksum would indicate that a
+     later record in the database makes this checksum redundant.  A report of
+     <I>many</I> addressees makes all preceding reports redundant.
+
+     The string <I>trimmed</I> after the server-ID marks older reports that have had
+     uninteresting checksums removed.  The string <I>compressed</I> after the server-
+     ID would indicate that this older report has been trimmed and compressed
+     with older reports.
+
+     With two <B>-v</B> arguments, records added to the database by <B><A HREF="dbclean.html">dbclean(8)</A></B> from
+     the server whitelist are also displayed.
+
+     Three <B>-v</B> arguments cause the hash table to be displayed.  Three typical
+     hash table entries look like:
+
+           19b8:   19ee   19b7
+           19b9:   19c0      0    90120 Fuz1
+           19ba:      0      0  1b72300 Fuz1
+
+     The entry in slot number <I>0x19b8</I> is unused or free.  Slot number <I>0x19b9</I> is
+     the start of a chain of collisions or entries with the same hash value of
+     0x19b9.  The next slot in this chain is at <I>0x19c0</I>.  The corresponding
+     <I>Fuz1</I>
+      checksum is at <I>0x9012</I> in the database.  The third slot at <I>0x19ba</I> is also
+
+     that of a <I>Fuz1</I> checksum, but it is not part of a hash chain and its data-
+     base record is at <I>0x1b72300</I>.
+
+
+</PRE>
+<H2><A NAME="FILES">FILES</A></H2><PRE>
+     <A NAME="FILE-@prefix@">@prefix@</A>     is the DCC home directory containing data and control files.
+     <A NAME="FILE-dcc_db">dcc_db</A> grey_dcc_db
+                  main file of checksums.
+     <A NAME="FILE-dcc_db.hash">dcc_db.hash</A> grey_dcc_db.hash
+                  database hash table.
+     <A NAME="FILE-flod.map">flod.map</A> grey_flod.map
+                  memory mapped flooding positions.
+
+
+</PRE>
+<H2><A NAME="SEE-ALSO">SEE ALSO</A></H2><PRE>
+     <B><A HREF="cdcc.html">cdcc(8)</A></B>, <B><A HREF="dcc.html">dcc(8)</A></B>, <B><A HREF="dbclean.html">dbclean(8)</A></B>, <B><A HREF="dccd.html">dccd(8)</A></B>, <B><A HREF="dccifd.html">dccifd(8)</A></B>, <B><A HREF="dccm.html">dccm(8)</A></B>, <B><A HREF="dccproc.html">dccproc(8)</A></B>.
+
+
+</PRE>
+<H2><A NAME="HISTORY">HISTORY</A></H2><PRE>
+     Implementation of <B>dblist</B> was started at Rhyolite Software, in 2000.  This
+     document describes version 1.3.103.
+
+                               February 26, 2009
+</PRE>
+<HR>
+<ADDRESS>
+Man(1) output converted with
+<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
+modified for the DCC $Date 2001/04/29 03:22:18 $
+<BR>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG SRC="http://logos.dcc-servers.net/border.png"
+            class=logo ALT="DCC logo">
+    </A>
+<A HREF="http://validator.w3.org/check?uri=referer">
+    <IMG class=logo ALT="Valid HTML 4.01 Strict"
+        SRC="http://www.w3.org/Icons/valid-html401">
+    </A>
+</ADDRESS>
+</BODY>
+</HTML>
diff -r 000000000000 -r c7f6b056b673 dblist/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dblist/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,3 @@
+Makefile.in
+dblist.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 dblist/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dblist/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,50 @@
+# make the Distributed Checksum Clearinghouse database lister
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.13 $Revision$
+# @configure_input@
+
+DEPTH	=..
+PROG	=dblist
+SRCS	=$(PROG).c
+
+CFLAGS	+=$(SRVRINC)
+LDADD	+=$(SRVRLIBS)
+DPADD	+=$(SRVRLIBS)
+
+DCC_BINDIR=@installroot@@libexecdir@
+@MAKE_PROG@
+@MAKE_INC2@
diff -r 000000000000 -r c7f6b056b673 dblist/dblist.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dblist/dblist.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,1521 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * database lister
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.137 $Revision$
+ */
+
+#include "srvr_defs.h"
+#include "dcc_xhdr.h"
+#include "dcc_ck.h"
+#include <signal.h>
+#if HAVE_BOOTTIME
+#include <sys/sysctl.h>
+#endif
+
+static DCC_EMSG dcc_emsg;
+
+static int verbose;
+#define VERBOSE_HASH 3
+static u_char no_hash;
+static u_char no_data;
+static u_char matching;
+
+static DCC_CLNT_CTXT *ctxt;
+static DCC_OP_RESP aop_resp;
+static DCC_SRVR_NM srvr;
+static const ID_TBL *srvr_clnt_tbl;
+
+static struct {
+    DCC_CK_TYPES type;
+    DCC_SUM	sum;
+    u_char	type_only;
+} search_cksums[16];
+static int num_search_cksums;
+
+static struct {
+    DCC_TS	lo;
+    DCC_TS	hi;
+} search_ts[16];
+static int num_search_ts;
+
+DCC_SRVR_ID search_ids[16];
+static int num_search_ids;
+
+static DB_PTR page_offset;
+static DB_PTR dbaddr;
+static int max_pathlen;
+
+static DB_HOFF hash_fsize;
+static char dcc_db_nm[] = DB_DCC_NAME;
+static char grey_db_nm[] = DB_GREY_NAME;
+static	DCC_PATH hash_nm;
+static char *def_argv[2];
+static const char *homedir;
+
+static const DB_VERSION_BUF version_buf = DB_VERSION_STR;
+static const u_char hash_magic[sizeof(((HASH_CTL*)0)->s.magic)
+			       ] = HASH_MAGIC_STR;
+
+static void rel_db(void);
+static void sigterm(int);
+static int save_cksum(DCC_EMSG, DCC_WF *, DCC_CK_TYPES, DCC_SUM, DCC_TGTS);
+static void list_cleaned(const DB_PARMS *);
+static void list_flod(void);
+static int fd_hash = -1;
+static int fd_db = -1;
+static struct stat hash_sb, db_sb;
+static void list_db(void);
+static u_char open_db(void);
+static void open_hash(void);
+static void list_hash(void);
+
+
+static void NRATTRIB
+usage(void)
+{
+	dcc_logbad(EX_USAGE, "usage: [-vVHD] [-G on | off] [-h homedir]\n"
+		   "   [-s server-ID[,server-addr][,server-port]]\n"
+		   "   [-C '[type] [h1 h2 h3 h4]'] [-I server-Id] [-A dbptr]"
+		   " [-L pathlen]\n"
+		   "   [-P pages] [-T timestamp] [file file2 ...]");
+}
+
+
+
+int NRATTRIB
+main(int argc, char **argv)
+{
+	u_char print_version = 0;
+	char hostname[DCC_MAXDOMAINLEN];
+	int file_num;
+	DCC_CK_TYPES type;
+	char tbuf[80];
+	const char *cp, *cp0;
+	struct timeval tv1, tv2;
+	int us;
+	struct tm tm;
+	char *p;
+	u_long l;
+	int i;
+
+	dcc_syslog_init(0, argv[0], 0);
+
+	while ((i = getopt(argc, argv, "vVHDG:h:s:C:I:A:L:P:T:")) != -1) {
+		switch (i) {
+		case 'v':
+			++verbose;
+			break;
+
+		case 'V':
+			fprintf(stderr, DCC_VERSION"\n");
+			print_version = 1;
+			break;
+
+		case 'G':
+			if (!strcasecmp(optarg, "on")) {
+				grey_on = 1;
+			} else if (!strcasecmp(optarg, "off")) {
+				grey_on = 0;
+			} else {
+				usage();
+			}
+			break;
+
+		case 'h':
+			homedir = optarg;
+			break;
+
+		case 's':
+			l = strtoul(optarg, &p, 10);
+			if ((*p != '\0' && *p != ',')
+			    || l < DCC_SRVR_ID_MIN
+			    || l > DCC_SRVR_ID_MAX)
+				dcc_logbad(EX_USAGE, "invalid DCC ID \"-s %s\"",
+					   optarg);
+			srvr.clnt_id = l;
+			if (*p != '\0') {
+				++p;
+				p += strspn(p, DCC_WHITESPACE);
+			}
+			hostname[0] = '\0';
+			srvr.port = 0;
+			if (*p == '\0')
+				break;
+			cp = dcc_parse_nm_port(dcc_emsg, p, srvr.port,
+					       hostname, sizeof(hostname),
+					       &srvr.port, 0, 0, 0, 0);
+			if (!cp)
+				dcc_logbad(EX_USAGE, "%s", dcc_emsg);
+			cp += strspn(cp, DCC_WHITESPACE);
+			if (*cp != '\0')
+				dcc_logbad(EX_USAGE,
+					   "unrecognized port number in"
+					   "\"-s %s\"", optarg);
+			if (hostname[0] != '\0')
+				BUFCPY(srvr.hostname, hostname);
+			break;
+
+		case 'H':
+			no_hash = 1;
+			break;
+
+		case 'D':
+			no_data = 1;
+			break;
+
+		case 'C':
+			if (num_search_cksums >= DIM(search_cksums)) {
+				dcc_error_msg("too many -C checksums");
+				break;
+			}
+			matching = 1;
+			cp0 = optarg;
+			cp = dcc_parse_word(0, tbuf, sizeof(tbuf),
+					    optarg, "checksum type", 0, 0);
+			if (!cp)
+				exit(1);
+			if (!strcasecmp(tbuf, "hex")) {
+				/* ignore "hex" */
+				cp0 = cp;
+				cp = dcc_parse_word(0, tbuf, sizeof(tbuf),
+						    cp, "checksum type",
+						    0, 0);
+				if (!cp)
+					dcc_logbad(EX_USAGE,
+						   "unrecognized checksum"
+						   " \"-C %s\"", optarg);
+			}
+			if (*cp == '\0') {
+				/* allow bare checksum type */
+				type = dcc_str2type_del(tbuf, -1);
+				if (type == DCC_CK_INVALID)
+					dcc_logbad(EX_USAGE,
+						   "unrecognized checksum type"
+						   " \"-C %s\"", optarg);
+				search_cksums[num_search_cksums].type = type;
+				memset(search_cksums[num_search_cksums].sum, 0,
+				       sizeof(DCC_SUM));
+				search_cksums[num_search_cksums].type_only = 1;
+				++num_search_cksums;
+				break;
+			}
+			/* allow missing checksum type */
+			strtoul(tbuf, &p, 16);
+			if (*p == '\0') {
+				if (0 >= dcc_parse_hex_ck(dcc_emsg, 0,
+							"-", DCC_CK_FLOD_PATH,
+							cp0, 0, save_cksum))
+					dcc_logbad(EX_USAGE, "%s", dcc_emsg);
+			} else {
+				type = dcc_str2type_del(tbuf, -1);
+				if (type == DCC_CK_FLOD_PATH)
+					dcc_logbad(EX_USAGE,
+						   "unrecognized checksum type"
+						   " \"-C %s\"", optarg);
+				if (0 >= dcc_parse_hex_ck(dcc_emsg, 0,
+							tbuf, type,
+							cp, 0, save_cksum))
+					dcc_logbad(EX_USAGE, "%s", dcc_emsg);
+			}
+			break;
+
+		case 'I':
+			if (num_search_ids >= DIM(search_ids)) {
+				dcc_error_msg("too many -I IDs");
+				break;
+			}
+			search_ids[num_search_ids] = strtoul(optarg, &p, 10);
+			if (search_ids[num_search_ids] > DCC_SRVR_ID_MAX
+			    || *p != '\0')
+				dcc_logbad(EX_USAGE,
+					   "invalid server-ID \"-I %s\"",
+					   optarg);
+			++num_search_ids;
+			matching = 1;
+			break;
+
+		case 'A':
+			dbaddr = strtoul(optarg, &p, 16);
+			if (*p != '\0')
+				dcc_logbad(EX_USAGE,
+					   "invalid database address \"%s\"",
+					   optarg);
+			matching = 1;
+			break;
+
+		case 'L':
+			max_pathlen = strtoul(optarg, &p, 10);
+			if (*p != '\0')
+				dcc_logbad(EX_USAGE,
+					   "invalid path length \"%s\"",
+					   optarg);
+			matching = 1;
+			break;
+
+		case 'P':
+			page_offset = strtoul(optarg, &p, 10);
+			if (*p != '\0')
+				dcc_logbad(EX_USAGE,
+					   "invalid number of pages \"%s\"",
+					   optarg);
+			matching = 1;
+			break;
+
+		case 'T':
+			if (num_search_ts >= DIM(search_ts)) {
+				dcc_error_msg("too many -T timestamps");
+				break;
+			}
+			memset(&tm, 0, sizeof(tm));
+			i = sscanf(optarg, "%d/%d/%d %d:%d:%d.%d%c",
+				   &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+				   &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
+				   &us, tbuf);
+			if (i < 6 || i > 7
+			    || tm.tm_mon <= 0)
+				dcc_logbad(EX_USAGE,"bad timestamp \"%s\"",
+					   optarg);
+			--tm.tm_mon;
+			tm.tm_year += 100;
+			tv1.tv_sec = DCC_TIMEGM(&tm);
+			if (tv1.tv_sec < 0)
+				dcc_logbad(EX_USAGE, "invalid timestamp \"%s\"",
+					   optarg);
+			tv2.tv_sec = tv1.tv_sec;
+			if (i == 7) {
+				if (us >= DCC_US)
+					dcc_logbad(EX_USAGE,
+						   "invalid microseconds"
+						   " in \"%s\"",
+						   optarg);
+				tv1.tv_usec = us;
+				tv2.tv_usec = us;
+			} else {
+				tv1.tv_usec = 0;
+				tv2.tv_usec = DCC_US-1;
+			}
+			dcc_timeval2ts(&search_ts[num_search_ts].lo, &tv1, 0);
+			dcc_timeval2ts(&search_ts[num_search_ts].hi, &tv2, 0);
+			++num_search_ts;
+			matching = 1;
+			break;
+
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	def_argv[0] = grey_on ? grey_db_nm : dcc_db_nm;
+	if (argc == 0) {
+		if (print_version)
+			exit(EX_OK);
+		argv = def_argv;
+		argc = 1;
+	}
+
+	dcc_clnt_unthread_init();
+	if (!dcc_cdhome(dcc_emsg, homedir, 1))
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+
+	flod_mmap_path_set();
+
+	if (matching) {
+		if (no_data && no_hash)
+			dcc_logbad(EX_USAGE,
+				   "patterns need data or hash table");
+		if (!no_data && !no_hash)
+			no_hash = 1;
+	}
+
+	if (dbaddr != 0 && page_offset != 0)
+		dcc_logbad(EX_USAGE, "-P and -A are incompatible");
+
+	if (srvr.clnt_id != 0) {
+		if (argc != 1)
+			dcc_logbad(EX_USAGE, "lock only one file");
+
+		i = load_ids(dcc_emsg, srvr.clnt_id, &srvr_clnt_tbl, 1);
+		if (i <= 0)
+			dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+		memcpy(srvr.passwd, srvr_clnt_tbl->cur_passwd,
+		       sizeof(srvr.passwd));
+		if (hostname[0] == '\0')
+			strcpy(srvr.hostname, DCC_SRVR_NM_DEF_HOST);
+		if (srvr.port == 0)
+			srvr.port = DCC_GREY2PORT(grey_on);
+
+		i = DCC_CLNT_FG_SLOW;
+		if (grey_on)
+			i |= DCC_CLNT_FG_GREY;
+		ctxt = dcc_tmp_clnt_init(dcc_emsg, 0, &srvr, 0, i, 0);
+		if (!ctxt)
+			dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+		if (!lock_dbclean(dcc_emsg, *argv))
+			dcc_logbad(dcc_ex_code, "%s: dbclean running?",
+				   dcc_emsg);
+
+		atexit(rel_db);
+		signal(SIGALRM, sigterm);
+		signal(SIGHUP, sigterm);
+		signal(SIGTERM, sigterm);
+		signal(SIGINT, sigterm);
+		if (!dcc_aop_persist(dcc_emsg, ctxt,
+				     grey_on ? DCC_CLNT_FG_GREY : 0,
+				     verbose != 0,
+			     DCC_AOP_DB_UNLOAD, 0, 60*5, &aop_resp))
+			dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+	}
+
+	for (file_num = 1; *argv != 0; ++argv, ++file_num) {
+		if (fd_db >= 0)
+			close(fd_db);
+		if (fd_hash >= 0)
+			close(fd_hash);
+
+		BUFCPY(db_nm, *argv);
+		snprintf(hash_nm, sizeof(hash_nm), "%s"DB_HASH_SUFFIX, db_nm);
+
+		if (file_num != 1)
+			fputc('\n', stdout);
+		if (verbose || argc > 1)
+			printf("  %s\n", db_nm);
+
+		/* try to open the hash table and the database
+		 * fail only if we cannot open the database */
+		open_hash();
+		if (!open_db())
+			continue;
+
+		/* print the header of the database followed by its contents */
+		list_db();
+		list_hash();
+	}
+
+	exit(EX_OK);
+}
+
+
+
+static void
+rel_db(void)
+{
+	if (!ctxt)
+		return;
+	if (!dcc_aop_persist(dcc_emsg, ctxt, grey_on ? DCC_CLNT_FG_GREY : 0,
+			     1, DCC_AOP_DB_UNLOAD, 1, 60*5, &aop_resp))
+		dcc_error_msg("%s", dcc_emsg);
+	unlock_dbclean();
+	ctxt = 0;
+}
+
+
+
+static void
+sigterm(int sig UATTRIB)
+{
+	rel_db();
+}
+
+
+
+static int
+save_cksum(DCC_EMSG emsg UATTRIB, DCC_WF *wf UATTRIB,
+	   DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts UATTRIB)
+{
+	search_cksums[num_search_cksums].type = type;
+	memcpy(search_cksums[num_search_cksums].sum, sum, sizeof(DCC_SUM));
+	search_cksums[num_search_cksums].type_only = 0;
+	++num_search_cksums;
+	return 1;
+}
+
+
+
+#define RCD_PAT "%-27s %-8.8s %-10.10s %7s "L_HWPAT(8)"\n"
+#define RCD_PAT1(s) RCD_PAT, s,  "", "", ""
+
+static DB_HDR hdr_buf;
+
+static enum {NO_LB,			/* no label */
+	WHITE_LB,			/* whitelist section labelled */
+	DATE_LB				/* normal section labelled */
+} last_lb = NO_LB;
+static u_char printed_rcd;
+static int rcds, white_rcds, sums, white_sums;
+
+
+static u_char
+open_db(void)
+{
+	int i;
+
+	fd_db = open(db_nm, O_RDONLY, 0);
+	if (fd_db < 0) {
+		dcc_error_msg("open(%s): %s", db_nm, ERROR_STR());
+		return 0;
+	}
+
+	i = read_db(dcc_emsg, &hdr_buf, sizeof(hdr_buf), fd_db, 0, db_nm);
+	if (i != sizeof(hdr_buf)) {
+		if (i < 0)
+			dcc_error_msg("%s", dcc_emsg);
+		else
+			dcc_error_msg("found only %d bytes of magic in %s",
+				      i, db_nm);
+		return 0;
+	}
+
+	if (memcmp(hdr_buf.p.version, version_buf,
+		   sizeof(hdr_buf.p.version))) {
+		dcc_error_msg("%s contains the wrong magic \"%.*s\"",
+			      db_nm, ISZ(version_buf), hdr_buf.p.version);
+	}
+	if (0 > fstat(fd_db, &db_sb)) {
+		dcc_error_msg("stat(%s): %s", db_nm, ERROR_STR());
+		return 0;
+	}
+
+	if (db_sb.st_size == sizeof(hdr_buf)) {
+		dcc_error_msg("%s contains no checksums",db_nm);
+		return 0;
+	}
+
+	if ((DB_PTR)db_sb.st_size < hdr_buf.p.db_csize) {
+		dcc_error_msg("%s says it contains "L_DPAT
+			      " bytes instead of "OFF_DPAT,
+			      db_nm, hdr_buf.p.db_csize, db_sb.st_size);
+	}
+
+	db_pagesize = hdr_buf.p.pagesize;
+	db_hash_page_len = db_pagesize/sizeof(HASH_ENTRY);
+
+	return 1;
+}
+
+
+
+static void
+list_db_entry(DB_PTR rcd_link, const DB_RCD *rcd)
+{
+	const DB_RCD_CK *rcd_ck;
+	DB_PTR rcd_prev;
+	DCC_TGTS tgts;
+	DCC_CK_TYPES type;
+	char ts_buf[40], id_buf[30];
+	char tgts_buf[20];
+	char ck_buf[sizeof(DCC_SUM)*3+2];
+	u_char rpt_match, kept;
+	int i;
+
+	/* usually skip padding */
+	if (rcd->fgs_num_cks == 0) {
+		if (verbose > 1) {
+			printf(RCD_PAT1("    page padding"), rcd_link);
+			printed_rcd = 1;
+		}
+		return;
+	}
+
+	rpt_match = 0;
+
+	/* skip until the desired first address */
+	if (dbaddr != 0) {
+		if (rcd_link < dbaddr)
+			return;
+		rpt_match = 1;
+	}
+
+	/* if we have target server-IDs, display only their reports */
+	if (num_search_ids > 0) {
+		for (i = 0; i < num_search_ids; ++i) {
+			if (search_ids[i] == DB_RCD_ID(rcd)) {
+				rpt_match = 1;
+				goto got_id;
+			}
+		}
+		return;
+got_id:;
+	}
+
+	/* if we have target checksums, display only reports containing them */
+	if (num_search_cksums > 0) {
+		for (i = 0; i < num_search_cksums; ++i) {
+			for (rcd_ck = rcd->cks;
+			     rcd_ck < &rcd->cks[DB_NUM_CKS(rcd)];
+			     ++rcd_ck) {
+				type = search_cksums[i].type;
+				if ((DB_CK_TYPE(rcd_ck) == type
+				     || type == DCC_CK_FLOD_PATH)
+				    && (search_cksums[i].type_only
+					|| !memcmp(search_cksums[i].sum,
+						   rcd_ck->sum,
+						   sizeof(DCC_SUM)))) {
+					rpt_match = 1;
+					goto got_ck;
+				}
+			}
+		}
+		return;
+got_ck:;
+	}
+
+	if (num_search_ts > 0
+	    && DB_RCD_ID(rcd) != DCC_ID_WHITE) {
+		for (i = 0; i < num_search_ts; ++i) {
+			if (!dcc_ts_older_ts(&rcd->ts,
+					     &search_ts[i].lo)
+			    && !dcc_ts_newer_ts(&rcd->ts,
+						&search_ts[i].hi)) {
+				rpt_match = 1;
+				goto got_ts;
+			}
+		}
+		return;
+got_ts:;
+	}
+
+	if (max_pathlen != 0
+	    && DB_RCD_ID(rcd) != DCC_ID_WHITE) {
+		DCC_FLOD_PATH_ID *id;
+		DCC_SRVR_ID psrvr;
+		int pathlen = 0;
+
+		for (rcd_ck = rcd->cks;
+		     rcd_ck < &rcd->cks[DB_NUM_CKS(rcd)]
+		     && pathlen < max_pathlen;
+		     ++rcd_ck) {
+			if (DB_CK_TYPE(rcd_ck) != DCC_CK_FLOD_PATH)
+				break;
+			id = (DCC_FLOD_PATH_ID *)&rcd_ck->sum;
+			for (i = 0; i < DCC_NUM_FLOD_PATH; ++i, ++id) {
+				psrvr = ((id->hi<<8) | id->lo);
+				if (psrvr == DCC_ID_INVALID)
+					break;
+				++pathlen;
+			}
+		}
+		if (pathlen < max_pathlen)
+			return;
+		rpt_match = 1;
+	}
+
+	++rcds;
+	if (DB_RCD_ID(rcd) == DCC_ID_WHITE) {
+		++white_rcds;
+		if (last_lb != WHITE_LB) {
+			last_lb = WHITE_LB;
+			strcpy(ts_buf, "\n"DCC_XHDR_ID_WHITE);
+		} else {
+			ts_buf[0] = '\0';
+		}
+	} else {
+		if (last_lb != DATE_LB) {
+			last_lb = DATE_LB;
+			if (rpt_match || verbose > 0)
+				putchar('\n');
+		}
+		if (rpt_match || verbose > 0)
+			ts2str(ts_buf, sizeof(ts_buf), &rcd->ts);
+	}
+
+	/* display separator between whitelist and ordinary entries
+	 * along with the timestamp and the rest of the first line
+	 * of a report */
+	if (rpt_match
+	    || verbose >= 2
+	    || (verbose > 0 && DB_RCD_ID(rcd) != DCC_ID_WHITE)) {
+		if (last_lb == DATE_LB) {
+			tgts = DB_TGTS_RCD_RAW(rcd);
+			printf(RCD_PAT, ts_buf,
+			       (tgts == 0)
+			       ? "deleted"
+			       : dcc_tgts2str(tgts_buf, sizeof(tgts_buf),
+					      tgts, grey_on),
+			       id2str(id_buf, sizeof(id_buf),
+				      rcd->srvr_id_auth),
+			       DB_RCD_TRIMMED(rcd) ? "trimmed"
+			       : DB_RCD_SUMRY(rcd) ? "summary"
+			       : DB_RCD_DELAY(rcd) ? "delayed"
+			       : "",
+			       rcd_link);
+		} else {
+			printf(RCD_PAT1(ts_buf), rcd_link);
+		}
+		printed_rcd = 1;
+	}
+
+	/* display a report */
+	for (rcd_ck = rcd->cks;
+	     rcd_ck < &rcd->cks[DB_NUM_CKS(rcd)];
+	     ++rcd_ck) {
+		++sums;
+		/* always count whitelist entries,
+		 * but display only as requested */
+		if (DB_RCD_ID(rcd) == DCC_ID_WHITE) {
+			++white_sums;
+			if (verbose < 2 && !rpt_match)
+				continue;
+		} else {
+			if (verbose < 1 && !rpt_match)
+				continue;
+		}
+
+		/* decode the special checksum that is a path */
+		if (DB_CK_TYPE(rcd_ck)== DCC_CK_FLOD_PATH) {
+			if (DB_RCD_ID(rcd) == DCC_ID_WHITE) {
+				int lno, fno;
+				memcpy(&lno, rcd_ck->sum, sizeof(lno));
+				fno = rcd_ck->sum[sizeof(lno)];
+				if (fno == 0) {
+					printf("     line #%d\n", lno);
+				} else {
+					printf("     line #%d"
+					       " included file #%d\n",
+					       lno, fno);
+				}
+
+			} else {
+				DCC_SRVR_ID psrvr;
+				DCC_FLOD_PATH_ID *path_id, *path_id_lim;
+				const char *s;
+
+				path_id=(DCC_FLOD_PATH_ID *)rcd_ck->sum;
+				path_id_lim = path_id+DCC_NUM_FLOD_PATH;
+				s = "     path: ";
+				do {
+					psrvr = ((path_id->hi<<8)
+						 | path_id->lo);
+					if (psrvr == DCC_ID_INVALID)
+					    break;
+					printf("%s%d", s, psrvr);
+					s = "<-";
+				} while (++path_id < path_id_lim);
+				printf("%s\n", s);
+			}
+			continue;
+		}
+
+		kept = (!DB_TEST_NOKEEP(hdr_buf.p.nokeep_cks,
+					DB_CK_TYPE(rcd_ck))
+			|| DB_RCD_ID(rcd) == DCC_ID_WHITE);
+
+		printf(" %c%-12.12s %-10.10s %-31s",
+		       DB_CK_OBS(rcd_ck) ? '*' : ' ',
+		       DB_TYPE2STR(DB_CK_TYPE(rcd_ck)),
+		       !kept
+		       ? "" : dcc_tgts2str(tgts_buf, sizeof(tgts_buf),
+					   DB_TGTS_CK(rcd_ck), grey_on),
+		       dcc_ck2str(ck_buf, sizeof(ck_buf),
+				  DB_CK_TYPE(rcd_ck), rcd_ck->sum,
+				  DB_RCD_ID(rcd)));
+		rcd_prev = DB_PTR_EX(rcd_ck->prev);
+		if (rcd_prev == DB_PTR_NULL)
+			printf(" %8s", "");
+		else if (DB_PTR_IS_BAD(rcd_prev))
+			printf(" bogus "L_HWPAT(8), rcd_prev);
+		else
+			printf(" "L_HWPAT(8), rcd_prev);
+		if (db_hash_len != 0
+		    && kept)
+			printf(" %x", db_hash(DB_CK_TYPE(rcd_ck), rcd_ck->sum));
+		putchar('\n');
+	}
+}
+
+
+
+static void
+list_db(void)
+{
+	DB_RCD rcd;
+	int rcd_len;
+	DB_PTR rcd_lim, rcd_link;
+
+	if (fd_db < 0)
+		return;
+
+	/* print the header of the database */
+	if (verbose > 0) {
+		list_cleaned(&hdr_buf.p);
+		list_flod();
+	}
+
+	if (no_data)
+		return;
+
+	last_lb = NO_LB;
+	printed_rcd = 0;
+	rcds = 0;
+	white_rcds = 0;
+	sums = 0;
+	white_sums = 0;
+
+	/* list the records in the database */
+	if (dbaddr != 0) {
+		if ((DB_PTR)db_sb.st_size <= dbaddr) {
+			page_offset = 0;
+		} else {
+			page_offset = ((db_sb.st_size - dbaddr + db_pagesize -1)
+				       / db_pagesize);
+		}
+	}
+	if (page_offset == 0) {
+		rcd_link = DB_PTR_BASE;
+	} else {
+		rcd_link = db_sb.st_size / hdr_buf.p.pagesize;
+		if (rcd_link < page_offset)
+			rcd_link = 0;
+		else
+			rcd_link -= page_offset;
+		rcd_link *= hdr_buf.p.pagesize;
+		if (rcd_link < DB_PTR_BASE)
+			rcd_link = DB_PTR_BASE;
+	}
+	rcd_lim = ((verbose > 2)
+		   ? (DB_PTR)db_sb.st_size : hdr_buf.p.db_csize);
+	read_rcd_invalidate(0);
+	while (rcd_link < rcd_lim) {
+		rcd_len = read_rcd(dcc_emsg, &rcd, fd_db, rcd_link, db_nm);
+		if (rcd_len <= 0) {
+			if (rcd_len == 0)
+				break;
+			/* ignore fragmentary reports at the end */
+			if (rcd_link > hdr_buf.p.db_csize - DB_RCD_HDR_LEN) {
+				printf(RCD_PAT1("    page padding"), rcd_link);
+				printed_rcd = 1;
+				break;
+			}
+			dcc_error_msg("%s", dcc_emsg);
+			read_rcd_invalidate(0);
+			return;
+		}
+
+
+		list_db_entry(rcd_link, &rcd);
+		rcd_link += rcd_len;
+	}
+
+	if (verbose || matching) {
+		/* print address after the last record,
+		 * but only if we printed a record */
+		if (printed_rcd)
+			printf(RCD_PAT1(""), rcd_link);
+		putchar('\n');
+	}
+	if (!matching) {
+		printf("%7d records containing %d checksums\n",
+		       rcds, sums);
+		if (!grey_on && rcds != white_rcds)
+			printf("%7d non-whitelist records containing"
+			       " %d checksums\n",
+			       rcds-white_rcds, sums-white_sums);
+	}
+	read_rcd_invalidate(0);
+}
+
+
+
+static const char *
+print_rate(char *buf, u_int buf_len,
+	   const DB_PARMS *parms, u_char hash_or_db)
+{
+	double rate;
+
+	rate = db_add_rate(parms, hash_or_db);
+
+	if (rate <= 0.0)
+		return "?";
+
+	return size2str(buf, buf_len, rate * (24*60*60*1.0), !hash_or_db);
+}
+
+
+
+static const char *
+secs2str(char *buf, u_int buf_len, u_int32_t secs)
+{
+	int days, minutes, hours;
+
+	days = secs / (24*60*60);
+	secs %= (24*60*60);
+	hours = secs / (60*60);
+	secs %= (60*60);
+	minutes = secs / 60;
+	secs %= 60;
+
+	if (hours == 0 && minutes == 0
+	    && (secs == 0 || (days != 0 && secs < 15 && verbose < 3))) {
+		snprintf(buf, buf_len, "%d day%s",
+			 days, (days > 1) ? "s" : " ");
+		return buf;
+	}
+
+	if (days == 0 && minutes == 0 && secs == 0) {
+		snprintf(buf, buf_len, "%d hour%s",
+			 hours, (hours > 1) ? "s" : " ");
+		return buf;
+	}
+
+	if (days == 0 && hours == 0) {
+		snprintf(buf, buf_len, "%02d:%02d",
+			 minutes, secs);
+		return buf;
+	}
+
+	if (days == 0) {
+		snprintf(buf, buf_len, "%d:%02d:%02d",
+			 hours, minutes, secs);
+		return buf;
+	}
+
+	snprintf(buf, buf_len, "%d %d:%02d:%02d",
+		 days, hours, minutes, secs);
+	return buf;
+}
+
+
+
+static const char *
+ex_ts2str(char *buf, u_int buf_len, const DCC_TS *ts)
+{
+	static DCC_TS never;
+
+	if (!memcmp(&ts, &never, sizeof(never))) {
+		STRLCPY(buf, "never    ", buf_len);
+		return buf;
+	}
+	return ts2str(buf, buf_len, ts);
+}
+
+
+
+/* display the expiration information in the database header */
+static void
+list_cleaned(const DB_PARMS *parms)
+{
+#define CLEANED_PAT	" %12s %c %17.17s %17.17s %10s %10s"
+	struct tm tm;
+	char time_buf[32];
+	char db_rate[10], hash_rate[10], entries_buf[10];
+	DCC_CK_TYPES type;
+	char spam_ts_buf[18];
+	char all_ts_buf[18];
+	char allsecs_buf[20];
+	char spamsecs_buf[20];
+
+	printf("     %s%s%spage size %#-8x  s/n %s\n",
+	       (parms->flags & DB_PARM_FG_GREY) ? "greylist  " : "",
+	       (parms->flags & DB_PARM_FG_CLEARED) ? "cleared  ": "",
+	       (parms->flags & DB_PARM_EXP_SET) ? "dbclean -e/-E  ": "",
+	       parms->pagesize, ts2str_err(&parms->sn));
+
+	DCC_GMTIME_R(&parms->cleared, &tm);
+	strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S UTC", &tm);
+	printf("     created %s", time_buf);
+	if (parms->cleaned_cron == 0) {
+		printf("; never properly cleaned");
+	} else {
+		DCC_GMTIME_R(&parms->cleaned_cron, &tm);
+		strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S", &tm);
+		printf("; cleaned %s", time_buf);
+	}
+	putchar('\n');
+	if (parms->cleaned > parms->cleaned_cron) {
+		DCC_GMTIME_R(&parms->cleaned, &tm);
+		strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S", &tm);
+		printf("     failsafe cleaned %s\n", time_buf);
+	}
+
+	if (verbose > 3) {
+		printf("     db_csize="L_DPAT"  old="L_DPAT"  added="L_DPAT"\n",
+		       parms->db_csize, parms->old_db_csize, parms->db_added);
+		printf("     hash_used=%d  old=%d  added=%d  old_kept_cks=%d\n",
+		       parms->hash_used, parms->old_hash_used,
+		       parms->hash_added, parms->old_kept_cks);
+		printf("     rate_secs=%d  \"%.*s\"\n",
+		       (int)parms->rate_secs,
+		       ISZ(parms->version), parms->version);
+	}
+
+	printf("     added %s database bytes/day and %s hash entries/day\n",
+	       print_rate(db_rate, sizeof(db_rate), parms, 0),
+	       print_rate(hash_rate, sizeof(hash_rate), parms, 1));
+
+	if (db_hash_len > 0
+	    && parms->hash_used >= DB_HADDR_BASE)
+		printf("     %.0f%% of %s hash entries used\n",
+		       HADDR2LEN(parms->hash_used) * 100.0
+		       / HADDR2LEN(db_hash_len),
+		       size2str(entries_buf, sizeof(entries_buf),
+				HADDR2LEN(db_hash_len), 0));
+
+	if (parms->flags & DB_PARM_FG_GREY)
+		printf(CLEANED_PAT,
+		       "", ' ', "", "",
+		       "window", "white");
+	else
+		printf(CLEANED_PAT,
+		       "", ' ', "non-bulk expired", "bulk expired   ",
+		       "non ", "bulk");
+	for (type = DCC_CK_TYPE_FIRST; type <= DCC_CK_TYPE_LAST; ++type) {
+		if ((type == DCC_CK_SRVR_ID
+		     || DB_TEST_NOKEEP(parms->nokeep_cks, type))
+		    && verbose < 3)
+			continue;
+		if (parms->ex_secs[type].all == DB_EXPIRE_SECS_MAX) {
+			STRLCPY(allsecs_buf, "never", sizeof(allsecs_buf));
+			STRLCPY(all_ts_buf, "-    ", sizeof(all_ts_buf));
+		} else {
+			secs2str(allsecs_buf, sizeof(allsecs_buf),
+				 parms->ex_secs[type].all);
+			ex_ts2str(all_ts_buf, sizeof(all_ts_buf),
+				  &parms->ex_all[type]);
+		}
+		if (parms->ex_secs[type].spam == DB_EXPIRE_SECS_MAX) {
+			STRLCPY(spamsecs_buf, "never", sizeof(spamsecs_buf));
+			STRLCPY(spam_ts_buf, "-        ", sizeof(spam_ts_buf));
+		} else {
+			secs2str(spamsecs_buf, sizeof(spamsecs_buf),
+				 parms->ex_secs[type].spam);
+			ex_ts2str(spam_ts_buf, sizeof(spam_ts_buf),
+				  &parms->ex_spam[type]);
+		}
+		printf("\n"CLEANED_PAT,
+		       DB_TYPE2STR(type),
+		       DB_TEST_NOKEEP(parms->nokeep_cks, type) ? '*' : ' ',
+		       all_ts_buf, spam_ts_buf,
+		       allsecs_buf, spamsecs_buf);
+	}
+#undef CLEANED_PAT
+}
+
+
+
+static void
+list_flod(void)
+{
+	FLOD_MMAP *mp;
+	DCC_PATH path;
+	char hostname[40], fg_buf[60];
+	u_char first;
+
+	/* display the flood map only for default database */
+	if (strcmp(fnm2abs_err(path, db_nm), DB_NM2PATH_ERR(def_argv[0]))) {
+		putchar('\n');
+	} else if (!flod_mmap(dcc_emsg, 0, 0, 0, 1)) {
+		dcc_error_msg("\n\n%s", dcc_emsg);
+	} else if (strcmp(flod_mmaps->magic, FLOD_MMAP_MAGIC)) {
+		dcc_error_msg("\n\n%s contains the wrong magic \"%.*s\"",
+			      flod_mmap_path,
+			      ISZ(flod_mmaps->magic), flod_mmaps->magic);
+		if (!flod_unmap(dcc_emsg, 0))
+			dcc_error_msg("%s", dcc_emsg);
+	} else {
+		first = 1;
+		fputs("\n\n  ", stdout);
+		fputs(flod_mmap_path, stdout);
+		printf("  s/n %s\n       delay position "L_HWPAT(8)"\n",
+		       ts2str_err(&flod_mmaps->sn), flod_mmaps->delay_pos);
+		for (mp = flod_mmaps->mmaps;
+		     mp <= LAST(flod_mmaps->mmaps);
+		     ++mp) {
+			if (mp->rem_hostname[0] == '\0')
+				continue;
+			if (first) {
+				first = 0;
+				printf("%32s %5s %9s %s\n",
+				       "peer", "", "ID", "position");
+			}
+			printf("%38s %9d "L_HWPAT(8)"%s\n",
+			       dcc_host_portname(hostname, sizeof(hostname),
+						 mp->rem_hostname,
+						 mp->rem_portname),
+			       mp->rem_id,
+			       mp->confirm_pos,
+			       flodmap_fg(fg_buf, sizeof(fg_buf), "  ", mp));
+			if (mp->rem_su.sa.sa_family != AF_UNSPEC
+			    && verbose > 1)
+				printf("%40s\n",
+				       dcc_su2str3(hostname, sizeof(hostname),
+						   &mp->rem_su,
+						   DCC_GREY2PORT(grey_on)));
+		}
+		if (!flod_unmap(dcc_emsg, 0))
+			dcc_error_msg("%s", dcc_emsg);
+	}
+}
+
+
+
+static void
+open_hash(void)
+{
+	db_hash_len = 0;
+	fd_hash = open(hash_nm, O_RDONLY, 0);
+	if (0 > fd_hash) {
+		dcc_error_msg("open(%s): %s", hash_nm, ERROR_STR());
+		return;
+	}
+	if (0 > fstat(fd_hash, &hash_sb)) {
+		dcc_error_msg("stat(%s): %s", hash_nm, ERROR_STR());
+		close(fd_hash);
+		fd_hash = -1;
+		return;
+	}
+	hash_fsize = hash_sb.st_size;
+	db_hash_len = hash_fsize/sizeof(HASH_ENTRY);
+	if ((hash_fsize % sizeof(HASH_ENTRY)) != 0) {
+		dcc_error_msg("%s has size "L_DPAT", not a multiple of %d",
+			      hash_nm, hash_fsize, ISZ(HASH_ENTRY));
+		db_hash_len = 0;
+		close(fd_hash);
+		fd_hash = -1;
+		return;
+	}
+	if (db_hash_len < MIN_HASH_ENTRIES) {
+		dcc_error_msg("%s has too few records, "L_DPAT" bytes",
+			      hash_nm, hash_fsize);
+		db_hash_len = 0;
+		close(fd_hash);
+		fd_hash = -1;
+		return;
+	}
+
+	db_hash_divisor = get_db_hash_divisor(db_hash_len);
+}
+
+
+
+#define HASH_MAP_LEN	(1024*1024)
+#define HASH_MAP_NUM	16
+typedef struct hash_map {
+    struct hash_map *fwd, *bak;
+    HASH_ENTRY	*buf;
+    DB_HADDR	base;
+    DB_HADDR	lim;
+    DB_HOFF	offset;
+    DB_HOFF	size;
+} HASH_MAP;
+static HASH_MAP hash_maps[HASH_MAP_NUM];
+static HASH_MAP *hash_map_newest;
+
+
+static u_char
+hash_munmap(HASH_MAP *mp)
+{
+	if (!mp->buf)
+		return 1;
+
+	if (0 > munmap((void *)mp->buf, mp->size)) {
+		dcc_error_msg("munmap(%s,"L_DPAT"): %s",
+			      hash_nm, mp->size, ERROR_STR());
+		return 0;
+	}
+	mp->buf = 0;
+	return 1;
+}
+
+
+
+static u_char
+hash_map_clear(void)
+{
+	HASH_MAP *mp;
+	int i;
+
+	mp = hash_maps;
+	for (i = 0; i < DIM(hash_maps); ++i, ++mp) {
+		if (i == DIM(hash_maps)-1)
+			mp->fwd = hash_maps;
+		else
+			mp->fwd = mp+1;
+		if (i == 0)
+			mp->bak = LAST(hash_maps);
+		else
+			mp->bak = mp-1;
+	}
+	hash_map_newest = hash_maps;
+
+	for (mp = hash_maps; mp <= LAST(hash_maps); ++mp) {
+		if (!hash_munmap(mp))
+			return 0;
+	}
+
+	return 1;
+}
+
+
+
+static void
+hash_map_ref(HASH_MAP *mp)
+{
+	if (hash_map_newest != mp) {
+		mp->fwd->bak = mp->bak;
+		mp->bak->fwd = mp->fwd;
+		mp->fwd = hash_map_newest;
+		mp->bak = hash_map_newest->bak;
+		mp->fwd->bak = mp;
+		mp->bak->fwd = mp;
+		hash_map_newest = mp;
+	}
+}
+
+
+
+static const void *
+haddr_mmap(DB_HADDR haddr)
+{
+	HASH_MAP *mp;
+	void *p;
+	int i;
+
+	for (i = 0, mp = hash_map_newest;
+	     i < DIM(hash_maps);
+	     ++i, mp = mp->fwd) {
+		if (!mp->buf)
+			continue;
+		if (haddr >= mp->base
+		    && haddr < mp->lim) {
+			hash_map_ref(mp);
+			return mp->buf + (haddr - mp->base);
+		}
+	}
+
+	mp = hash_map_newest->bak;
+	hash_munmap(mp);
+
+	mp->base = haddr -  haddr%HASH_MAP_LEN;
+	mp->offset = mp->base*sizeof(HASH_ENTRY);
+	mp->size = hash_fsize - mp->offset;
+	if (mp->size > HASH_MAP_LEN*ISZ(HASH_ENTRY))
+		mp->size = HASH_MAP_LEN*ISZ(HASH_ENTRY);
+	mp->lim = mp->base + mp->size/sizeof(HASH_ENTRY);
+	p = mmap(0, mp->size, PROT_READ, MAP_SHARED, fd_hash, mp->offset);
+	if (p != MAP_FAILED) {
+		mp->buf = p;
+		hash_map_ref(mp);
+		return mp->buf + (haddr - mp->base);
+	}
+	dcc_error_msg("mmap(%s,%d,%d): %s",
+		      hash_nm, (int)mp->size, (int)mp->offset,
+		      ERROR_STR());
+	return 0;
+}
+
+
+
+static void
+list_hash(void)
+{
+#define HEAD() (headed ? 1 : (headed = 1, printf("\n %s\n", hash_nm)))
+	const HASH_ENTRY *entry;
+	const HASH_CTL *ctl;
+	time_t secs;
+	struct tm tm;
+	char time_buf[30];
+	DB_HADDR collisions, chains, chain_lens;
+	int max_chain_len, chain_len;
+	DB_HADDR free_fwd, free_bak;
+	DB_HADDR fwd, bak, haddr;
+	DB_HADDR db_hash_used_stored;
+	DB_PTR rcd_link;
+	DCC_CK_TYPES type;
+	DB_RCD rcd;
+	int rcd_len;
+	u_char headed, clean;
+	int i;
+
+	if (fd_hash < 0)
+		return;
+
+	headed = 0;
+
+	if (!hash_map_clear())
+		return;
+
+	read_rcd_invalidate(DB_RCD_LEN_MAX);
+
+	ctl = haddr_mmap(0);
+	if (!ctl)
+		return;
+	if (memcmp(ctl->s.magic, &hash_magic, sizeof(hash_magic))) {
+		HEAD();
+		dcc_error_msg("     contains the wrong magic");
+		return;
+	}
+
+	if (verbose > VERBOSE_HASH) {
+		HEAD();
+		printf("     magic: \"%.*s\"\n",
+		       ISZ(ctl->s.magic), ctl->s.magic);
+	}
+
+	if (srvr.clnt_id != 0) {
+		clean = 0;
+	} else {
+		clean = (ctl->s.flags & HASH_CTL_FG_CLEAN) != 0;
+		if (!clean) {
+			HEAD();
+			printf("     not closed\n");
+		}
+	}
+	secs = ctl->s.synced;
+	if (verbose >= VERBOSE_HASH) {
+		DCC_GMTIME_R(&secs, &tm);
+		strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S", &tm);
+		printf("     synced %s\n", time_buf);
+		if (ctl->s.flags & HASH_CTL_FG_NOSYNC)
+			printf("     unsafe after next system reboot\n");
+	}
+
+	free_fwd = ctl->s.free_fwd;
+	free_bak = ctl->s.free_bak;
+	if (DB_HADDR_INVALID(ctl->s.free_fwd)
+	    && (ctl->s.free_fwd != FREE_HADDR_END
+		|| ctl->s.free_fwd != ctl->s.free_bak)) {
+		HEAD();
+		dcc_error_msg("     broken free list head of %#x",
+			      ctl->s.free_fwd);
+	}
+	if (DB_HADDR_INVALID(ctl->s.free_bak)
+	    && (ctl->s.free_bak != FREE_HADDR_END
+		|| ctl->s.free_fwd != ctl->s.free_bak)) {
+		HEAD();
+		dcc_error_msg("     broken free list tail of %#x",
+			      ctl->s.free_bak);
+	}
+	if (verbose > VERBOSE_HASH)
+		printf("     free: %x, %x\n", free_fwd, free_bak);
+
+	if (db_hash_len != ctl->s.len
+	    && (ctl->s.len != 0 || verbose >= VERBOSE_HASH)) {
+		HEAD();
+		dcc_error_msg("     has %d entries but claims %d",
+			      HADDR2LEN(db_hash_len), HADDR2LEN(ctl->s.len));
+	}
+	db_hash_used_stored = ctl->s.used;
+	if (ctl->s.used > db_hash_len) {
+		HEAD();
+		dcc_error_msg("     contains only %d entries but %d used",
+			      HADDR2LEN(ctl->s.len), HADDR2LEN((ctl->s.used)));
+	}
+	if (ctl->s.used == db_hash_len) {
+		HEAD();
+		dcc_error_msg("     overflows with %d entries",
+			      HADDR2LEN(db_hash_len));
+	}
+	if (ctl->s.db_csize != hdr_buf.p.db_csize
+	    && (clean || verbose >= VERBOSE_HASH)) {
+		HEAD();
+		dcc_error_msg("     claims %s contains "L_DPAT
+			      " bytes instead of "L_DPAT,
+			      db_nm, ctl->s.db_csize, hdr_buf.p.db_csize);
+	}
+	if (ctl->s.divisor != get_db_hash_divisor(db_hash_len)) {
+		HEAD();
+		dcc_error_msg("     built with hash divisor %d instead of %d",
+			      ctl->s.divisor, get_db_hash_divisor(db_hash_len));
+	}
+	if (verbose >= VERBOSE_HASH) {
+		printf("     hash length=%#x=%d used=%#x=%d\n",
+		       ctl->s.len, ctl->s.len,
+		       ctl->s.used, ctl->s.used);
+		printf("     db_csize="L_HPAT"="L_DPAT"\n",
+		       ctl->s.db_csize, ctl->s.db_csize);
+	}
+
+	if (no_hash) {
+		hash_map_clear();
+		return;
+	}
+
+	db_hash_used = DB_HADDR_BASE;
+	collisions = 0;
+	chains = 0;
+	chain_lens = 0;
+	max_chain_len = 1;
+	for (haddr = DB_HADDR_BASE; haddr < db_hash_len; ++haddr) {
+		entry = haddr_mmap(haddr);
+		if (!entry)
+			break;
+
+		fwd = DB_HADDR_EX(entry->fwd);
+		bak = DB_HADDR_EX(entry->bak);
+		rcd_link = DB_HPTR_EX(entry->rcd);
+
+		/* deal with a free entry */
+		if (HE_IS_FREE(entry)) {
+			if (rcd_link != DB_PTR_NULL)
+				dcc_error_msg("free hash table data link at"
+					      " %x to "L_HPAT,
+					      haddr, rcd_link);
+			if (haddr == free_fwd
+			    && bak != FREE_HADDR_END)
+				dcc_error_msg("bad 1st free hash bak link %x",
+					      bak);
+			else if (haddr != free_fwd
+				 && (DB_HADDR_INVALID(bak) || bak >= haddr))
+				dcc_error_msg("bad hash bak link at %x",
+					      haddr);
+			if (haddr == free_bak
+			    && fwd != FREE_HADDR_END)
+				dcc_error_msg("bad last free hash fwd link %x",
+					      fwd);
+			else if (haddr != free_bak
+				 && (DB_HADDR_INVALID(fwd) || fwd <= haddr))
+				dcc_error_msg("bad hash fwd link at %x",
+					      haddr);
+			if (verbose >= VERBOSE_HASH)
+				printf("    %6x: %6x %6x\n", haddr, fwd, bak);
+			continue;
+		}
+
+		if (haddr == free_fwd && clean)
+			dcc_error_msg("start of free list at %x not free",
+				      haddr);
+		if (haddr == free_bak && clean)
+			dcc_error_msg("end of free list at %x not free",
+				      haddr);
+
+		/* deal with a used entry */
+		++db_hash_used;
+		if (DB_PTR_IS_BAD(rcd_link))
+			dcc_error_msg("bad hash table data link at"
+				      " %x to "L_HPAT,
+				      haddr, rcd_link);
+		if (DB_HADDR_INVALID(fwd) && fwd != DB_HADDR_NULL)
+			dcc_error_msg("bad hash fwd link at %x to %x",
+				      haddr, fwd);
+		if (DB_HADDR_INVALID(bak) && bak != DB_HADDR_NULL)
+			dcc_error_msg("bad hash bak link at %x to %x",
+				      haddr, bak);
+		if (verbose >= VERBOSE_HASH)
+			printf("    %6x: %6x %6x "L_HWPAT(8)" %s\n",
+			       haddr, fwd, bak, rcd_link,
+			       DB_TYPE2STR(HE_TYPE(entry)));
+
+		if (bak != DB_HADDR_NULL) {
+			++collisions;
+		} else {
+			++chains;
+			bak = haddr;
+			chain_len = 1;
+			while (!DB_HADDR_INVALID(fwd)) {
+				if (++chain_len > 500) {
+					dcc_error_msg("possible hash chain loop"
+						      " starting at %x"
+						      " continuing through %x",
+						      haddr, fwd);
+					break;
+				}
+				entry = haddr_mmap(fwd);
+				if (!entry)
+					break;
+				if (HE_IS_FREE(entry)
+				    || DB_HADDR_EX(entry->bak) != bak) {
+					dcc_error_msg("broken hash chain"
+						      " starting at %x at %x",
+						      haddr, fwd);
+					break;
+				}
+				bak = fwd;
+				fwd = DB_HADDR_EX(entry->fwd);
+			}
+			chain_lens += chain_len;
+			if (max_chain_len < chain_len)
+				max_chain_len = chain_len;
+		}
+
+		if (matching) {
+			if (num_search_cksums > 0) {
+				for (i = 0; i < num_search_cksums; ++i) {
+					type = search_cksums[i].type;
+					if (type == HE_TYPE(entry)
+					    || type == DCC_CK_FLOD_PATH)
+					    break;
+				}
+				if (i >= num_search_cksums)
+					continue;
+			}
+			rcd_len = read_rcd(dcc_emsg, &rcd,
+					   fd_db, rcd_link, db_nm);
+			if (rcd_len <= 0) {
+				if (rcd_len == 0)
+					dcc_error_msg("bogus hash table data"
+						      " link at %x to "L_HPAT,
+						      haddr, rcd_link);
+				else
+					dcc_error_msg("%s", dcc_emsg);
+			} else {
+				list_db_entry(rcd_link, &rcd);
+			}
+		}
+	}
+
+	hash_map_clear();
+
+	if (db_hash_used_stored > db_hash_used) {
+		dcc_error_msg("%s should have %d entries but has only %d",
+			      hash_nm,
+			      HADDR2LEN(db_hash_used_stored),
+			      HADDR2LEN(db_hash_used));
+	} else if (db_hash_used_stored < db_hash_used
+		   && (clean || verbose >= VERBOSE_HASH)) {
+		dcc_error_msg("%s should have %d filled entries but has %d",
+			      hash_nm,
+			      HADDR2LEN(db_hash_used_stored),
+			      HADDR2LEN(db_hash_used));
+	}
+
+	if (verbose >= VERBOSE_HASH)
+		putchar('\n');
+	printf("%7d hash entries total  %d or %.0f%% used  %d free\n"
+	       "%7d modulus  %.2f%% collisions",
+	       HADDR2LEN(db_hash_len),
+	       HADDR2LEN(db_hash_used),
+	       (HADDR2LEN(db_hash_used)*100.0) / HADDR2LEN(db_hash_len),
+	       HADDR2LEN(db_hash_len) - HADDR2LEN(db_hash_used),
+	       db_hash_divisor,
+	       collisions*100.0/HADDR2LEN(db_hash_len));
+	if (chains != 0)
+		printf("  %7d hash chains\n"
+		       " %d max length   %.2f average length",
+		       chains, max_chain_len, chain_lens*1.0/chains);
+	fputc('\n', stdout);
+}
diff -r 000000000000 -r c7f6b056b673 dcc.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcc.0	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,609 @@
+DCC(8)                Distributed Checksum Clearinghouse                DCC(8)
+
+NNAAMMEE
+     DDCCCC -- Distributed Checksum Clearinghouse
+
+DDEESSCCRRIIPPTTIIOONN
+     The Distributed Checksum Clearinghouse or DDCCCC is a cooperative, distrib-
+     uted system intended to detect "bulk" mail or mail sent to many people.
+     It allows individuals receiving a single mail message to determine that
+     many other people have received essentially identical copies of the mes-
+     sage and so reject or discard the message.
+
+     Source for the server, client, and utilities is available at Rhyolite
+     Software, LLC, http://www.rhyolite.com/dcc/ It is free for organizations
+     that do not sell spam or virus filtering services.
+
+   HHooww tthhee DDCCCC IIss UUsseedd
+     The DCC can be viewed as a tool for end users to enforce their right to
+     "opt-in" to streams of bulk mail by refusing bulk mail except from
+     sources in a "whitelist."  Whitelists are the responsibility of DCC
+     clients, since only they know which bulk mail they solicited.
+
+     False positives or mail marked as bulk by a DCC server that is not bulk
+     occur only when a recipient of a message reports it to a DCC server as
+     having been received many times or when the "fuzzy" checksums of differ-
+     ing messages are the same.  The fuzzy checksums ignore aspects of mes-
+     sages in order to compute identical checksums for substantially identical
+     messages.  The fuzzy checksums are designed to ignore only differences
+     that do not affect meanings.  So in practice, you do not need to worry
+     about DCC false positive indications of "bulk," but not all bulk mail is
+     unsolicited bulk mail or spam.  You must either use whitelists to distin-
+     guish solicited from unsolicited bulk mail or only use DCC indications of
+     "bulk" as part of a scoring system such as SpamAssassin.  Besides unso-
+     licited bulk email or spam, bulk messages include legitimate mail such as
+     order confirmations from merchants, legitimate mailing lists, and empty
+     or test messages.
+
+     A DCC server estimates the number copies of a message by counting check-
+     sums reported by DCC clients.  Each client must decide which bulk mes-
+     sages are unsolicited and what degree of "bulkiness" is objectionable.
+     Client DCC software marks, rejects, or discards mail that is bulk accord-
+     ing to local thresholds on target addresses from DCC servers and unso-
+     licited according to local whitelists.
+
+     DCC servers are usually configured to receive reports from as many tar-
+     gets as possible, including sources that cannot be trusted to not exag-
+     gerate the number of copies of a message they see.  A user of a DCC
+     client angry about receiving a message could report it with 1,000,000
+     separate DCC reports or with a single report claiming 1,000,000 targets.
+     An unprincipled user could subscribe a "spam trap" to mailing lists such
+     as those of the IETF or CERT.  Such abuses of the system area not prob-
+     lems, because much legitimate mail is "bulk."  You cannot reject bulk
+     mail unless you have a whitelist of sources of legitimate bulk mail.
+
+     DCC can also be used by an Internet service provider to detect bulk mail
+     coming from its own customers.  In such circumstances, the DCC client
+     might be configured to only log bulk mail from unexpected (not
+     whitelisted) customers.
+
+   WWhhaatt tthhee DDCCCC IIss
+     A DCC server accumulates counts of cryptographic checksums of messages
+     but not the messages themselves.  It exchanges reports of frequently seen
+     checksums with other servers.  DCC clients send reports of checksums
+     related to incoming mail to a nearby DCC server running dccd(8).  Each
+     report from a client includes the number of recipients for the message.
+     A DCC server accumulates the reports and responds to clients the the cur-
+     rent total number of recipients for each checksum.  The client adds an
+     SMTP header to incoming mail containing the total counts.  It then dis-
+     cards or rejects mail that is not whitelisted and has counts that exceed
+     local thresholds.
+
+     A special value of the number of addressees is "MANY" and means it is
+     certain that this message was bulk and might be unsolicited, perhaps
+     because it came from a locally blacklisted source or was addressed to an
+     invalid address or "spam trap."  The special value "MANY" is merely the
+     largest value that fits in the fixed sized field containing the count of
+     addressees.  That "infinity" accumulated total can be reached with mil-
+     lions of independent reports as well as with one or two.
+
+     DCC servers _f_l_o_o_d or send reports of checksums of bulk mail to neighbor-
+     ing servers.
+
+     To keep a server's database of checksums from growing without bound,
+     checksums are forgotten when they become old.  Checksums of bulk mail are
+     kept longer.  See dbclean(8).
+
+     DCC clients pick the nearest working DCC server using a small shared or
+     memory mapped file, _/_v_a_r_/_d_c_c_/_m_a_p.  It contains server names, port num-
+     bers, passwords, recent performance measures, and so forth.  This file
+     allows clients to use quick retransmission timeouts and to waste little
+     time on servers that have temporarily stopped working or become unreach-
+     able.  The utility program cdcc(8) is used to maintain this file as well
+     as to check the health of servers.
+
+   XX--DDCCCC HHeeaaddeerrss
+     The DCC software includes several programs used by clients.  Dccm(8) uses
+     the sendmail "milter" interface to query a DCC server, add header lines
+     to incoming mail, and reject mail whose total checksum counts are high.
+     Dccm is intended to be run with SMTP servers using sendmail.
+
+     Dccproc(8) adds header lines to mail presented by file name or _s_t_d_i_n, but
+     relies on other programs such as procmail to deal with mail with large
+     counts.  Dccsight(8) is similar but deals with previously computed check-
+     sums.
+
+     Dccifd(8) is similar to dccproc but is not run separately for each mail
+     message and so is far more efficient.  It receives mail messages via a
+     socket somewhat like dccm, but with a simpler protocol that can be used
+     by Perl scripts or other programs.
+
+     DCC SMTP header lines are of one of the forms:
+
+       X-DCC-brand-Metrics: client server-ID; bulk cknm1=count cknm2=count ...
+       X-DCC-brand-Metrics: client; whitelist
+     where
+        _w_h_i_t_e_l_i_s_t appears if the global or per-user _w_h_i_t_e_c_l_n_t file marks the
+                message as good.
+        _b_r_a_n_d   is the "brand name" of the DCC server, such as "RHYOLITE".
+        _c_l_i_e_n_t  is the name or IP address of the DCC client that added the
+                header line to the SMTP message.
+        _s_e_r_v_e_r_-_I_D is the numeric ID of the DCC server that the DCC client con-
+                tacted.
+        _b_u_l_k    is present if one or more checksum counts exceeded the DCC
+                client's thresholds to make the message "bulky."
+        _b_u_l_k _r_e_p is present if the DCC reputation of the IP address of the
+                sender is bad.
+        _c_k_n_m_1,_c_k_n_m_2,... are types of checksums:
+                  _I_P           address of SMTP client
+                  _e_n_v___F_r_o_m     SMTP envelope value
+                  _F_r_o_m         SMTP header line
+                  _M_e_s_s_a_g_e_-_I_D   SMTP header line
+                  _R_e_c_e_i_v_e_d     last Received: header line in the SMTP message
+                  _s_u_b_s_t_i_t_u_t_e   SMTP header line chosen by the DCC client, pre-
+                               fixed with the name of the header
+                  _B_o_d_y         SMTP body ignoring white-space
+                  _F_u_z_1         filtered or "fuzzy" body checksum
+                  _F_u_z_2         another filtered or "fuzzy" body checksum
+                  _r_e_p          DCC reputation of the mail sender or the esti-
+                               mated probability that the message is bulk.
+                Counts for _I_P, _e_n_v___F_r_o_m, _F_r_o_m, _M_e_s_s_a_g_e_-_I_d, _R_e_c_e_i_v_e_d, and
+                _s_u_b_s_t_i_t_u_t_e checksums are omitted by the DCC client if the
+                server says it has no information.  Counts for _F_u_z_1 and _F_u_z_2
+                are omitted if the message body is empty or contains too lit-
+                tle of the right kind of information for the checksum to be
+                computed.
+        _c_o_u_n_t   is the total number of recipients of messages with that check-
+                sum reported directly or indirectly to the DCC server.  The
+                special count "MANY" means that DCC client have claimed that
+                the message is directed at millions of recipients.  "MANY"
+                imples the message is definitely bulk, but not necessarily
+                unsolicited.  The special counts "OK" and "OK2" mean the
+                checksum has been marked "good" or "half-good" by DCC servers.
+
+   MMaaiilliinngg lliissttss
+     Legitimate mailing list traffic differs from spam only in being solicited
+     by recipients.  Each client should have a private whitelist.
+
+     DCC whitelists can also mark mail as unsolicited bulk using blacklist
+     entries for commonly forged values such as "From: user@public.com".
+
+   WWhhiittee aanndd BBllaacckklliissttss
+     DCC server and client whitelist files share a common format.  Server
+     files are always named _w_h_i_t_e_l_i_s_t and one is required to be in the DCC
+     home directory with the other server files.  Client whitelist files are
+     named _w_h_i_t_e_c_l_n_t in the DCC home directory or a subdirectory specified
+     with the --UU option for dccm(8).  They specify mail that should not be
+     reported to a DCC server or that is always unsolicited and almost cer-
+     tainly bulk.
+
+     A DCC whitelist file contains blank lines, comments starting with "#",
+     and lines of the following forms:
+       _i_n_c_l_u_d_e _f_i_l_e
+             Copies the contents of _f_i_l_e into the whitelist.  It can occur
+             only in the main whitelist or whiteclnt file and not in an
+             included file.  The file name should be absolute or relative to
+             the DCC home directory.
+
+       _c_o_u_n_t _v_a_l_u_e
+             lines specify checksums that should be white- or blacklisted.
+               _c_o_u_n_t _e_n_v___F_r_o_m _8_2_1_-_p_a_t_h
+               _c_o_u_n_t _e_n_v___T_o _d_e_s_t_-_m_a_i_l_b_o_x
+               _c_o_u_n_t _F_r_o_m _8_2_2_-_m_a_i_l_b_o_x
+               _c_o_u_n_t _M_e_s_s_a_g_e_-_I_D _<_s_t_r_i_n_g_>
+               _c_o_u_n_t _R_e_c_e_i_v_e_d _s_t_r_i_n_g
+               _c_o_u_n_t _S_u_b_s_t_i_t_u_t_e _h_e_a_d_e_r _s_t_r_i_n_g
+               _c_o_u_n_t _H_e_x _c_t_y_p_e _c_k_s_u_m
+               _c_o_u_n_t _i_p _I_P_-_a_d_d_r_e_s_s
+
+               _M_A_N_Y _v_a_l_u_e
+                     indicates that millions of targets have received messages
+                     with the header, IP address, or checksum _v_a_l_u_e.
+               _O_K _v_a_l_u_e
+               _O_K_2 _v_a_l_u_e
+                     say that messages with the header, IP address, or check-
+                     sum _v_a_l_u_e are OK and should not reported to DCC servers
+                     or be greylisted.  _O_K_2 says that the message is "half
+                     OK."  Two _O_K_2 checksums associated with a message are
+                     equivalent to one _O_K.
+                     A DCC server never shares or _f_l_o_o_d_s reports containing
+                     checksums marked in its whitelist with OK or OK2 to other
+                     servers.  A DCC client does not report or ask its server
+                     about messages with a checksum marked OK or OK2 in the
+                     client whitelist.  This is intended to allow a DCC client
+                     to keep private mail so private that even its checksums
+                     are not disclosed.
+               _M_X _I_P_-_a_d_d_r_e_s_s_-_o_r_-_h_o_s_t_n_a_m_e
+               _M_X_D_C_C _I_P_-_a_d_d_r_e_s_s_-_o_r_-_h_o_s_t_n_a_m_e
+                     mark an address or block of addresses of trust mail
+                     relays including MX servers, smart hosts, and bastion or
+                     DMZ relays.  The DCC clients dccm(8), dccifd(8), and
+                     dccproc(8) parse and skip initial Received: headers added
+                     by listed MX servers to determine the external sources of
+                     mail messages.  Unsolicited bulk mail that has been for-
+                     warded through listed addresses is discarded by dccm(8)
+                     and dccifd(8) as if with --aa _D_I_S_C_A_R_D instead of rejected.
+                     _M_X_D_C_C marks addresses that are MX servers that run DCC
+                     clients.  The checksums for a mail message that has been
+                     forwarded through an address listed as MXDCC queried
+                     instead of reported.
+               _S_U_B_M_I_T _I_P_-_a_d_d_r_e_s_s_-_o_r_-_h_o_s_t_n_a_m_e
+                     marks an IP address or block addresses of SMTP submission
+                     clients such as web browsers that cannot tolerate 4yz
+                     temporary rejections but that cannot be trusted to not
+                     send spam.  Since they are local addresses, DCC Reputa-
+                     tions are not computed for them.
+
+             _v_a_l_u_e in _c_o_u_n_t _v_a_l_u_e lines can be
+               _d_e_s_t_-_m_a_i_l_b_o_x
+                     is an RFC 821 address or a local user name.
+               _8_2_1_-_p_a_t_h
+                     is an RFC 821 address.
+               _8_2_2_-_m_a_i_l_b_o_x
+                     is an RFC 822 address with optional name.
+               _S_u_b_s_t_i_t_u_t_e _h_e_a_d_e_r
+                     is the name of an SMTP header such as "Sender" or the
+                     name of one of two SMTP envlope values, "HELO," or
+                     "Mail_Host" for the resolved host name from the _8_2_1_-_p_a_t_h
+                     in the message.
+               _H_e_x _c_t_y_p_e _c_k_s_u_m
+                     starts with the string _H_e_x followed a checksum type, and
+                     a string of four hexadecimal numbers obtained from a DCC
+                     log file or the dccproc(8) command using --CCQQ.  The check-
+                     sum type is _b_o_d_y, _F_u_z_1, or _F_u_z_2 or one of the preceding
+                     checksum types such as _e_n_v___F_r_o_m.
+               _I_P_-_a_d_d_r_e_s_s
+                     is a host name, IPv4 or IPv6 address, or a block of IP
+                     addresses in the standard xxx/mm from with mm limited for
+                     server whitelists to 16 for IPv4 or 112 for IPv6.  There
+                     can be at most 64 CIDR blocks in a client _w_h_i_t_e_c_l_n_t file.
+                     A host name is converted to IP addresses with DNS,
+                     _/_e_t_c_/_h_o_s_t_s or other mechanisms and one checksum for each
+                     addresses added to the whitelist.
+
+       _o_p_t_i_o_n _s_e_t_t_i_n_g
+             can only be in a DCC client _w_h_i_t_e_c_l_n_t file used by dccifd(8),
+             dccm(8) or dccproc(8).  Settings in per-user whiteclnt files
+             override settings in the global file.  _S_e_t_t_i_n_g can be any of the
+             following:
+               _o_p_t_i_o_n _l_o_g_-_a_l_l
+                   to log all mail messages.
+               _o_p_t_i_o_n _l_o_g_-_n_o_r_m_a_l
+                   to log only messages that meet the logging thresholds.
+               _o_p_t_i_o_n _l_o_g_-_s_u_b_d_i_r_e_c_t_o_r_y_-_d_a_y
+               _o_p_t_i_o_n _l_o_g_-_s_u_b_d_i_r_e_c_t_o_r_y_-_h_o_u_r
+               _o_p_t_i_o_n _l_o_g_-_s_u_b_d_i_r_e_c_t_o_r_y_-_m_i_n_u_t_e
+                   creates log files containing mail messages in subdirecto-
+                   ries of the form _J_J_J, _J_J_J_/_H_H, or _J_J_J_/_H_H_/_M_M where _J_J_J is the
+                   current julian day, _H_H is the current hour, and _M_M is the
+                   current minute.  See also the --ll _l_o_g_d_i_r option for dccm(8),
+                   dccifd(8), and dccproc(8).
+               _o_p_t_i_o_n _d_c_c_-_o_n
+               _o_p_t_i_o_n _d_c_c_-_o_f_f
+                   Control DCC filtering.  See the discussion of --WW for
+                   dccm(8) and dccifd(8).
+               _o_p_t_i_o_n _g_r_e_y_l_i_s_t_-_o_n
+               _o_p_t_i_o_n _g_r_e_y_l_i_s_t_-_o_f_f
+                   to control greylisting.  Greylisting for other recipients
+                   in the same SMTP transaction can still cause greylist tem-
+                   porary rejections.  _g_r_e_y_l_i_s_t_-_o_f_f in the main whiteclnt
+                   file.
+               _o_p_t_i_o_n _g_r_e_y_l_i_s_t_-_l_o_g_-_o_n
+               _o_p_t_i_o_n _g_r_e_y_l_i_s_t_-_l_o_g_-_o_f_f
+                   to control logging of greylisted mail messages.
+               _o_p_t_i_o_n _D_C_C_-_r_e_p_-_o_f_f
+               _o_p_t_i_o_n _D_C_C_-_r_e_p_-_o_n
+                   to honor or ignore DCC Reputations computed by the DCC
+                   server.
+               _o_p_t_i_o_n _D_N_S_B_L_1_-_o_f_f
+               _o_p_t_i_o_n _D_N_S_B_L_1_-_o_n
+               _o_p_t_i_o_n _D_N_S_B_L_2_-_o_f_f
+               _o_p_t_i_o_n _D_N_S_B_L_2_-_o_n
+               _o_p_t_i_o_n _D_N_S_B_L_3_-_o_f_f
+               _o_p_t_i_o_n _D_N_S_B_L_3_-_o_n
+                   honor or ignore results of DNS blacklist checks configured
+                   with --BB for dccm(8), dccifd(8), and dccproc(8).
+               _o_p_t_i_o_n _M_T_A_-_f_i_r_s_t
+               _o_p_t_i_o_n _M_T_A_-_l_a_s_t
+                   consider MTA determinations of spam or not-spam first so
+                   they can be overridden by _w_h_i_t_e_c_l_n_t files, or last so that
+                   they can override _w_h_i_t_e_c_l_n_t _f_i_l_e_s_.
+               _o_p_t_i_o_n _f_o_r_c_e_d_-_d_i_s_c_a_r_d_-_o_k
+               _o_p_t_i_o_n _n_o_-_f_o_r_c_e_d_-_d_i_s_c_a_r_d
+                   control whether dccm(8) and dccifd(8) are allowed to dis-
+                   card a message for one mailbox for which it is spam when it
+                   is not spam and must be delivered to another mailbox.  This
+                   can happen if a mail message is addressed to two or more
+                   mailboxes with differing whitelists.  Discarding can be
+                   undesirable because false positives are not communicated to
+                   mail senders.  To avoid discarding, dccm(8) and dccifd(8)
+                   running in proxy mode temporarily reject SMTP envelope _R_c_p_t
+                   _T_o values that involve differing _w_h_i_t_e_c_l_n_t files.
+               _o_p_t_i_o_n _t_h_r_e_s_h_o_l_d _t_y_p_e_,_r_e_j_-_t_h_o_l_d
+                   has the same effects as --cc _t_y_p_e_,_r_e_j_-_t_h_o_l_d for dccproc(8) or
+                   --tt _t_y_p_e_,_r_e_j_-_t_h_o_l_d for dccm(8) and dccifd(8).  It is useful
+                   only in per-user whiteclnt files to override the global DCC
+                   checksum thresholds.
+               _o_p_t_i_o_n _s_p_a_m_-_t_r_a_p_-_a_c_c_e_p_t
+               _o_p_t_i_o_n _s_p_a_m_-_t_r_a_p_-_r_e_j_e_c_t
+                   say that mail should be reported to the DCC server as
+                   extremely bulk or with target counts of _M_A_N_Y.  Greylisting,
+                   DNS blacklist (DNSBL), and other checks are turned off.
+                   _S_p_a_m_-_t_r_a_p_-_a_c_c_e_p_t tells the MTA to accept the message while
+                   _s_p_a_m_-_t_r_a_p_-_r_e_j_e_c_t tells the MTA to reject the message.  Use
+                   _S_p_a_m_-_t_r_a_p_-_a_c_c_e_p_t for spam traps that should not be dis-
+                   closed.  _S_p_a_m_-_t_r_a_p_-_r_e_j_e_c_t can be used  on _c_a_t_c_h_-_a_l_l mail-
+                   boxes that might receive legitimate mail by typographical
+                   errors and that senders should be told about.
+
+             In the absence of explicit settings, the default in the main
+             whiteclnt file is equivalent to
+                 _o_p_t_i_o_n _l_o_g_-_n_o_r_m_a_l
+                 _o_p_t_i_o_n _d_c_c_-_o_n
+                 _o_p_t_i_o_n _g_r_e_y_l_i_s_t_-_o_n
+                 _o_p_t_i_o_n _g_r_e_y_l_i_s_t_-_l_o_g_-_o_n
+                 _o_p_t_i_o_n _D_C_C_-_r_e_p_-_o_f_f
+                 _o_p_t_i_o_n _D_N_S_B_L_1_-_o_f_f
+                 _o_p_t_i_o_n _D_N_S_B_L_2_-_o_f_f
+                 _o_p_t_i_o_n _D_N_S_B_L_3_-_o_f_f
+                 _M_T_A_-_l_a_s_t
+                 _o_p_t_i_o_n _n_o_-_f_o_r_c_e_d_-_d_i_s_c_a_r_d
+             The defaults for individual recipient _w_h_i_t_e_c_l_n_t files are the
+             same except as change by explicit settings in the main file.
+
+     Checksums of the IP address of the SMTP client sending a mail message are
+     practically unforgeable, because it is impractical for an SMTP client to
+     "spoof" its address or pretend to use some other IP address.  That would
+     make the IP address of the sender useful for whitelisting, except that
+     the IP address of the SMTP client is often not available to users of
+     dccproc(8).  In addition, legitimate mail relays make whitelist entries
+     for IP addresses of little use.  For example, the IP address from which a
+     message arrived might be that of a local relay instead of the home
+     address of a whitelisted mailing list.
+
+     Envelope and header _F_r_o_m values can be forged, so whitelist entries for
+     their checksums are not entirely reliable.
+
+     Checksums of _e_n_v___T_o values are never sent to DCC servers.  They are valid
+     in only _w_h_i_t_e_c_l_n_t files and used only by dccm(8), dccifd(8), and
+     dccproc(8) when the envelope _R_c_p_t _T_o value is known.
+
+   GGrreeyylliissttss
+     The DCC server, dccd(8), can be used to maintain a greylist database for
+     some DCC clients including dccm(8) and dccifd(8).  Greylisting involves
+     temporarily refusing mail from unfamiliar SMTP clients and is unrelated
+     to filtering with a Distributed Checksum Clearinghouse.
+     See http://projects.puremagic.com/greylisting/
+
+   PPrriivvaaccyy
+     Because sending mail is a less private act than receiving it, and because
+     sending bulk mail is usually not private at all and cannot be very pri-
+     vate, the DCC tries first to protect the privacy of mail recipients, and
+     second the privacy of senders of mail that is not bulk.
+
+     DCC clients necessarily disclose some information about mail they have
+     received.  The DCC database contains checksums of mail bodies, header
+     lines, and source addresses.  While it contains significantly less infor-
+     mation than is available by "snooping" on Internet links, it is important
+     that the DCC database be treated as containing sensitive information and
+     to not put the most private information in the DCC database.  Given the
+     contents of a message, one might determine whether that message has been
+     received by a system that subscribes to the DCC.  Guesses about the
+     sender and addressee of a message can also be validated if the checksums
+     of the message have been sent to a DCC server.
+
+     Because the DCC is distributed, organizations can operate their own DCC
+     servers, and configure them to share or "flood" only the checksums of
+     bulk mail that is not in local whitelists.
+
+     DCC clients should not report the checksums of messages known to be pri-
+     vate to a DCC server.  For example, checksums of messages local to a sys-
+     tem or that are otherwise known a priori to not be unsolicited bulk
+     should not be sent to a remote DCC server.  This can accomplished by
+     adding entries for the sender to the client's local whitelist file.
+     Client whitelist files can also include entries for email recipients
+     whose mail should not be reported to a DCC server.
+
+   SSeeccuurriittyy
+     Whenever considering security, one must first consider the risks.  The
+     worst DCC security problems are unauthorized commands to a DCC service,
+     denial of the DCC service, and corruption of DCC data.  The worst that
+     can be done with remote commands to a DCC server is to turn it off or
+     otherwise cause it to stop responding.  The DCC is designed to fail
+     gracefully, so that a denial of service attack would at worst allow
+     delivery of mail that would otherwise be rejected.  Corruption of DCC
+     data might at worst cause mail that is already somewhat "bulk" by virtue
+     of being received by two or more people to appear have higher recipient
+     numbers.  Since DCC users _m_u_s_t whitelist all sources of legitimate bulk
+     mail, this is also not a concern.  Such security risks should be
+     addressed, but only with defenses that don't cost more than the possible
+     damage from an attack.
+
+     The DCC must contend with senders of unsolicited bulk mail who resort to
+     unlawful actions to express their displeasure at having their advertising
+     blocked.  Because the DCC protocol is based on UDP, an unhappy advertiser
+     could try to flood a DCC server with packets supposedly from subscribers
+     or non-subscribers.  DCC servers defend against that attack by rate-lim-
+     iting requests from anonymous users.
+
+     Also because of the use of UDP, clients must be protected against forged
+     answers to their queries.  Otherwise an unsolicited bulk mail advertiser
+     could send a stream of "not spam" answers to an SMTP client while simul-
+     taneously sending mail that would otherwise be rejected.  This is not a
+     problem for authenticated clients of the DCC because they share a secret
+     with the DCC.  Unauthenticated, anonymous DCC clients do not share any
+     secrets with the DCC, except for unique and unpredictable bits in each
+     query or report sent to the DCC.  Therefore, DCC servers cryptographi-
+     cally sign answers to unauthenticated clients with bits from the corre-
+     sponding queries.  This protects against attackers that do not have
+     access to the stream of packets from the DCC client.
+
+     The passwords or shared secrets used in the DCC client and server pro-
+     grams are "cleartext" for several reasons.  In any shared secret authen-
+     tication system, at least one party must know the secret or keep the
+     secret in cleartext.  You could encrypt the secrets in a file, but
+     because they are used by programs, you would need a cleartext copy of the
+     key to decrypt the file somewhere in the system, making such a scheme
+     more expensive but no more secure than a file of cleartext passwords.
+     Asymmetric systems such as that used in UNIX allow one party to not know
+     the secrets, but they must be and are designed to be computationally
+     expensive when used in applications like the DCC that involve thousands
+     or more authentication checks per second.  Moreover, because of "dictio-
+     nary attacks," asymmetric systems are now little more secure than keeping
+     passwords in cleartext.  An adversary can compare the hash values of com-
+     binations of common words with /etc/passwd hash values to look for bad
+     passwords.  Worse, by the nature of a client/server protocol like that
+     used in the DCC, clients must have the cleartext password.  Since it is
+     among the more numerous and much less secure clients that adversaries
+     would seek files of DCC passwords, it would be a waste to complicate the
+     DCC server with an asymmetric system.
+
+     The DCC protocol is vulnerable to dictionary attacks to recover pass-
+     words.  An adversary could capture some DCC packets, and then check to
+     see if any of the 100,000 to 1,000,000 passwords in so called "cracker
+     dictionaries" applied to a packet generated the same signature.  This is
+     a concern only if DCC passwords are poorly chosen, such as any combina-
+     tion of words in an English dictionary.  There are ways to prevent this
+     vulnerability regardless of how badly passwords are chosen, but they are
+     computationally expensive and require additional network round trips.
+     Since DCC passwords are created and typed into files once and do not need
+     to be remembered by people, it is cheaper and quite easy to simply choose
+     good passwords that are not in dictionaries.
+
+   RReelliiaabbiilliittyy
+     It is better to fail to filter unsolicited bulk mail than to fail to
+     deliver legitimate mail, so DCC clients fail in the direction of assuming
+     that mail is legitimate or even whitelisted.
+
+     A DCC client sends a report or other request and waits for an answer.  If
+     no answer arrives within a reasonable time, the client retransmits.
+     There are many things that might result in the client not receiving an
+     answer, but the most important is packet loss.  If the client's request
+     does not reach the server, it is easy and harmless for the client to
+     retransmit.  If the client's request reached the server but the server's
+     response was lost, a retransmission to the same server would be misunder-
+     stood as a new report of another copy of the same message unless it is
+     detected as a retransmission by the server.  The DCC protocol includes
+     transactions identifiers for this purpose.  If the client retransmitted
+     to a second server, the retransmission would be misunderstood by the sec-
+     ond server as a new report of the same message.
+
+     Each request from a client includes a timestamp to aid the client in mea-
+     suring the round trip time to the server and to let the client pick the
+     closest server.  Clients monitor the speed of all of the servers they
+     know including those they are not currently using, and use the quickest.
+
+   CClliieenntt aanndd SSeerrvveerr--IIDDss
+     Servers and clients use numbers or IDs to identify themselves.  ID 1 is
+     reserved for anonymous, unauthenticated clients.  All other IDs are asso-
+     ciated with a pair of passwords in the _i_d_s file, the current and next or
+     previous and current passwords.  Clients included their client IDs in
+     their messages.  When they are not using the anonymous ID, they sign
+     their messages to servers with the first password associated with their
+     client-ID.  Servers treat messages with signatures that match neither of
+     the passwords for the client-ID in their own _i_d_s file as if the client
+     had used the anonymous ID.
+
+     Each server has a unique _s_e_r_v_e_r_-_I_D less than 32768.  Servers use their
+     IDs to identify checksums that they _f_l_o_o_d to other servers.  Each server
+     expects local clients sending administrative commands to use the server's
+     ID and sign administrative commands with the associated password.
+
+     Server-IDs must be unique among all systems that share reports by "flood-
+     ing."  All servers must be told of the IDs all other servers whose
+     reports can be received in the local _/_v_a_r_/_d_c_c_/_f_l_o_d file described in
+     dccd(8).  However, server-IDs can be mapped during flooding between inde-
+     pendent DCC organizations.
+
+     _P_a_s_s_w_d_-_I_D_s are server-IDs that should not be assigned to servers.  They
+     appear in the often publicly readable _/_v_a_r_/_d_c_c_/_f_l_o_d and specify passwords
+     in the private _/_v_a_r_/_d_c_c_/_i_d_s file for the inter-server flooding protocol
+
+     The client identified by a _c_l_i_e_n_t_-_I_D might be a single computer with a
+     single IP address, a single but multi-homed computer, or many computers.
+     Client-IDs are not used to identify checksum reports, but the organiza-
+     tion operating the client.  A client-ID need only be unique among clients
+     using a single server.  A single client can use different client-IDs for
+     different servers, each client-ID authenticated with a separate password.
+
+     An obscure but important part of all of this is that the inter-server
+     flooding algorithm depends on server-IDs and timestamps attached to
+     reports of checksums.  The inter-server flooding mechanism requires coop-
+     erating DCC servers to maintain reasonable clocks ticking in UTC.
+     Clients include timestamps in their requests, but as long as their time-
+     stamps are unlikely to be repeated, they need not be very accurate.
+
+   IInnssttaallllaattiioonn CCoonnssiiddeerraattiioonnss
+     DCC clients on a computer share information about which servers are cur-
+     rently working and their speeds in a shared memory segment.  This segment
+     also contains server host names, IP addresses, and the passwords needed
+     to authenticate known clients to servers.  That generally requires that
+     dccm(8), dccproc(8), dccifd(8), and cdcc(8) execute with an UID that can
+     write to the DCC home directory and its files.  The sendmail interface,
+     dccm, is a daemon that can be started by an "rc" or other script already
+     running with the correct UID.  The other two, dccproc and cdcc need to be
+     set-UID because they are used by end users.  They relinquish set-UID
+     privileges when not needed.
+
+     Files that contain cleartext passwords including the shared file used by
+     clients must be readable only by "owner."
+
+     The data files required by a DCC can be in a single "home" directory,
+     _/_v_a_r_/_d_c_c.  Distinct DCC servers can run on a single computer, provided
+     they use distinct UDP port numbers and home directories.  It is possible
+     and convenient for the DCC clients using a server on the same computer to
+     use the same home directory as the server.
+
+     The DCC source distribution includes sample control files.  They should
+     be modified appropriately and then copied to the DCC home directory.
+     Files that contain cleartext passwords must not be publicly readable.
+
+     The DCC source includes "feature" m4 files to configure sendmail to use
+     dccm(8) to check a DCC server about incoming mail.
+
+     See also the INSTALL.html file.
+
+   CClliieenntt IInnssttaallllaattiioonn
+     Installing a DCC client starts with obtaining or compiling program bina-
+     ries for the client server data control tool, cdcc(8).  Installing the
+     sendmail DCC interface, dccm(8), or dccproc(8), the general or
+     procmail(1) interface is the main part of the client installation.  Con-
+     necting the DCC to sendmail with dccm is most powerful, but requires
+     administrative control of the system running sendmail.
+
+     As noted above, cdcc and dccproc should be set-UID to a suitable UID.
+     Root or 0 is thought to be safe for both, because they are careful to
+     release privileges except when they need them to read or write files in
+     the DCC home directory.  A DCC home directory, _/_v_a_r_/_d_c_c should be cre-
+     ated.  It must be owned and writable by the UID to which cdcc is set.
+
+     After the DCC client programs have been obtained, contact the operator(s)
+     of the chosen DCC server(s) to obtain each server's hostname, port num-
+     ber, and a _c_l_i_e_n_t_-_I_D and corresponding password.  No client-IDs or pass-
+     words are needed touse DCC servers that allow anonymous clients.  Use the
+     _l_o_a_d or _a_d_d commands of cdcc to create a _m_a_p file in the DCC home direc-
+     tory.  It is usually necessary to create a client whitelist file of the
+     format described above.  To accommodate users sharing a computer but not
+     ideas about what is solicited bulk mail, the client whitelist file can be
+     any valid path name and need not be in the DCC home directory.
+
+     If dccm is chosen, arrange to start it with suitable arguments before
+     sendmail is started.  See the _h_o_m_e_d_i_r_/_d_c_c___c_o_n_f file and the _m_i_s_c_/_r_c_D_C_C
+     script in the DCC source.  The procmail DCCM interface, dccproc(8), can
+     be run manually or by a procmailrc(5) rule.
+
+   SSeerrvveerr IInnssttaallllaattiioonn
+     The DCC server, dccd(8), also requires that the DCC home directory exist.
+     It does not use the client shared or memory mapped file of server
+     addresses, but it requires other files.  One is the _/_v_a_r_/_d_c_c_/_i_d_s file of
+     client-IDs,  server-IDs, and corresponding passwords.  Another is a _f_l_o_d
+     file of peers that send and receive floods of reports of checksums with
+     large counts.  Both files are described in dccd(8).
+
+     The server daemon should be started when the system is rebooted, probably
+     before sendmail.  See the _m_i_s_c_/_r_c_D_C_C and _m_i_s_c_/_s_t_a_r_t_-_d_c_c_d files in the DCC
+     source.
+
+     The database should be cleaned regularly with dbclean(8) such as by run-
+     ning the crontab job that is in the misc directory.
+
+SSEEEE AALLSSOO
+     cdcc(8), dbclean(8), dcc(8), dccd(8), dccifd(8), dccm(8), dccproc(8),
+     dblist(8), dccsight(8), sendmail(8).
+
+HHIISSTTOORRYY
+     Distributed Checksum Clearinghouses are based on an idea of Paul Vixie
+     with code designed and written at Rhyolite Software starting in 2000.
+     This document describes version 1.3.103.
+
+                               February 26, 2009
diff -r 000000000000 -r c7f6b056b673 dcc.8.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcc.8.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,967 @@
+.\" Copyright (c) 2008 by Rhyolite Software, LLC
+.\"
+.\" This agreement is not applicable to any entity which sells anti-spam
+.\" solutions to others or provides an anti-spam solution as part of a
+.\" security solution sold to other entities, or to a private network
+.\" which employs the DCC or uses data provided by operation of the DCC
+.\" but does not provide corresponding data to other users.
+.\"
+.\" Permission to use, copy, modify, and distribute this software without
+.\" changes for any purpose with or without fee is hereby granted, provided
+.\" that the above copyright notice and this permission notice appear in all
+.\" copies and any distributed versions or copies are either unchanged
+.\" or not called anything similar to "DCC" or "Distributed Checksum
+.\" Clearinghouse".
+.\"
+.\" Parties not eligible to receive a license under this agreement can
+.\" obtain a commercial license to use DCC by contacting Rhyolite Software
+.\" at sales@rhyolite.com.
+.\"
+.\" A commercial license would be for Distributed Checksum and Reputation
+.\" Clearinghouse software.  That software includes additional features.  This
+.\" free license for Distributed ChecksumClearinghouse Software does not in any
+.\" way grant permision to use Distributed Checksum and Reputation Clearinghouse
+.\" software
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+.\" 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.
+.\"
+.\" Rhyolite Software DCC 1.3.103-1.112 $Revision$
+.\"
+.Dd February 26, 2009
+.ds volume-ds-DCC Distributed Checksum Clearinghouse
+.Dt DCC 8 DCC
+.Os " "
+.Sh NAME
+.Nm DCC
+.Nd Distributed Checksum Clearinghouse
+.Sh DESCRIPTION
+The Distributed Checksum Clearinghouse or
+.Nm
+is a cooperative, distributed
+system intended to detect "bulk" mail or mail sent to many people.
+It allows individuals receiving a single mail message to determine
+that many
+other people have received essentially identical copies of the message
+and so reject or discard the message.
+.Pp
+Source for the server, client, and utilities
+is available at Rhyolite Software, LLC, http://www.rhyolite.com/dcc/
+It is free for organizations that do not sell spam or virus filtering
+services.
+.Ss How the DCC Is Used
+The DCC can be viewed as a tool for end users to enforce their
+right to "opt-in" to streams of bulk mail
+by refusing bulk mail except from sources in a "whitelist."
+Whitelists are the responsibility of DCC clients,
+since only they know which bulk mail they solicited.
+.Pp
+False positives or mail marked as bulk by a DCC server that
+is not bulk occur only when a recipient of a message reports it
+to a DCC server as having been received many times
+or when the "fuzzy" checksums of differing messages are the same.
+The fuzzy checksums ignore aspects of messages in order to compute
+identical checksums for substantially identical messages.
+The fuzzy checksums are designed to ignore only
+differences that do not affect meanings.
+So in practice, you do not need to worry about DCC false positive indications
+of "bulk," but not all bulk mail is unsolicited bulk mail or spam.
+You must either use whitelists to distinguish solicited from unsolicited bulk
+mail
+or only use DCC indications of "bulk" as part of a scoring system such
+as SpamAssassin.
+Besides unsolicited bulk email or spam,
+bulk messages include legitimate mail such as
+order confirmations from merchants,
+legitimate mailing lists,
+and empty or test messages.
+.Pp
+A DCC server estimates the number copies of a
+message by counting checksums reported by DCC clients.
+Each client must decide which
+bulk messages are unsolicited and what degree of "bulkiness" is objectionable.
+Client DCC software marks, rejects, or discards mail that is bulk
+according to local thresholds on target addresses from DCC servers
+and unsolicited according to local whitelists.
+.Pp
+DCC servers are usually configured to receive reports from as many targets
+as possible, including sources that cannot be trusted to not exaggerate the
+number of copies of a message they see.
+A user of a DCC client angry about receiving a message could report it with
+1,000,000 separate DCC reports
+or with a single report claiming 1,000,000 targets.
+An unprincipled user could subscribe a "spam trap" to mailing lists
+such as those of the IETF or CERT.
+Such abuses of the system area not problems,
+because much legitimate mail is "bulk."
+You cannot reject bulk mail unless you have a whitelist of sources
+of legitimate bulk mail.
+.Pp
+DCC can also be used by an Internet service provider to detect bulk
+mail coming from its own customers.
+In such circumstances, the DCC client might be configured to only log
+bulk mail from unexpected (not whitelisted) customers.
+.Ss What the DCC Is
+A DCC server accumulates counts of cryptographic checksums of
+messages but not the messages themselves.
+It exchanges reports of frequently seen checksums with other servers.
+DCC clients send reports of checksums related to incoming mail to
+a nearby DCC server running
+.Xr dccd 8 .
+Each report from a client includes the number of recipients for the message.
+A DCC server accumulates the reports and responds to clients the
+the current total number of recipients for each checksum.
+The client adds an SMTP header to incoming mail containing the total
+counts.
+It then discards or rejects mail that is not whitelisted and has
+counts that exceed local thresholds.
+.Pp
+A special value of the number of addressees is "MANY" and means
+it is certain that this message was bulk and might be unsolicited,
+perhaps because it came from a locally blacklisted source or was
+addressed to an invalid address or "spam trap."
+The special value "MANY" is merely the largest value
+that fits in the fixed sized field containing the count of addressees.
+That "infinity" accumulated total can be reached with millions of
+independent reports as well as with one or two.
+.Pp
+DCC servers
+.Em flood
+or send
+reports of checksums of bulk mail to neighboring servers.
+.Pp
+To keep a server's database of checksums from growing without bound,
+checksums are forgotten when they become old.
+Checksums of bulk mail are kept longer.
+See
+.Xr dbclean 8 .
+.Pp
+DCC clients pick the nearest working DCC server using a small shared
+or memory mapped file,
+.Pa @prefix@/map .
+It contains server names, port numbers, passwords, recent performance
+measures, and so forth.
+This file allows clients to use quick retransmission timeouts
+and to waste little time on servers that have temporarily
+stopped working or become unreachable.
+The utility program
+.Xr cdcc 8
+is used to maintain this file as well as to check the health of servers.
+.Ss X-DCC Headers
+The DCC software includes several programs used by clients.
+.Xr Dccm 8
+uses the sendmail "milter" interface to query a DCC server,
+add header lines to incoming mail,
+and reject mail whose total checksum counts are high.
+Dccm is intended to be run with SMTP servers using sendmail.
+.Pp
+.Xr Dccproc 8
+adds header lines to mail presented by file name or
+.Pa stdin ,
+but relies on other programs
+such as procmail to deal with mail with large counts.
+.Xr Dccsight 8
+is similar but deals with previously computed checksums.
+.Pp
+.Xr Dccifd 8
+is similar to dccproc but is not run separately for each mail message
+and so is far more efficient.
+It receives mail messages via a socket somewhat like dccm,
+but with a simpler protocol that can be used by Perl scripts
+or other programs.
+.Pp
+DCC SMTP header lines are of one of the forms:
+.Bd -literal -offset 2n
+X-DCC-brand-Metrics: client server-ID; bulk cknm1=count cknm2=count ...
+X-DCC-brand-Metrics: client; whitelist
+.Ed
+where
+.Bl -hang -offset 3n -compact
+.It Em whitelist
+appears if the global or per-user
+.Pa whiteclnt
+file marks the message as good.
+.It Em brand
+is the "brand name" of the DCC server, such as "RHYOLITE".
+.It Em client
+is the name or IP address of the DCC client that added the
+header line to the SMTP message.
+.It Em server-ID
+is the numeric ID of the DCC server that the DCC client contacted.
+.It Em bulk
+is present if one or more checksum counts exceeded the DCC client's
+thresholds to make the message "bulky."
+.It Em bulk rep
+is present if the DCC reputation of the IP address of the sender is bad.
+.It Em cknm1 , Ns Em cknm2 , Ns ...
+are types of checksums:
+.Bl -hang -offset 2n -width "Message-IDx" -compact
+.It Em IP
+address of SMTP client
+.It Em env_From
+SMTP envelope value
+.It Em From
+SMTP header line
+.It Em Message-ID
+SMTP header line
+.It Em Received
+last Received: header line in the SMTP message
+.It Em substitute
+SMTP header line chosen by the DCC client, prefixed with the name of
+the header
+.It Em Body
+SMTP body ignoring white-space
+.It Em Fuz1
+filtered or "fuzzy" body checksum
+.It Em Fuz2
+another filtered or "fuzzy" body checksum
+.It Em rep
+DCC reputation of the mail sender or the estimated
+probability that the message is bulk.
+.El
+Counts for
+.Em IP , env_From , From ,
+.Em Message-Id , Received ,
+and
+.Em substitute
+checksums are omitted by the DCC client if the server
+says it has no information.
+Counts for
+.Em Fuz1
+and
+.Em Fuz2
+are omitted if the message body is empty or
+contains too little of the right kind of information
+for the checksum to be computed.
+.It Em count
+is the total number of recipients of messages with that
+checksum reported directly or indirectly to the DCC server.
+The special count "MANY" means that DCC client have claimed that
+the message is directed at millions of recipients.
+"MANY" imples the message is definitely bulk, but not necessarily unsolicited.
+The special counts "OK" and "OK2" mean the checksum has been
+marked "good" or "half-good" by DCC servers.
+.El
+.Pp
+.Ss Mailing lists
+Legitimate mailing list traffic differs from spam only in being solicited
+by recipients.
+Each client should have a private whitelist.
+.Pp
+DCC whitelists can also mark mail as unsolicited bulk using
+blacklist entries for commonly forged values such as "From: user@public.com".
+.Ss White and Blacklists
+DCC server and client whitelist files share a common format.
+Server files are always named
+.Pa whitelist
+and one is required to be in the DCC home directory
+with the other server files.
+Client whitelist files are
+named
+.Pa whiteclnt
+in the DCC home directory or a subdirectory specified with the
+.Fl U
+option for
+.Xr dccm 8 .
+They specify mail that should not be reported to a DCC server or that is
+always unsolicited and almost certainly bulk.
+.Pp
+A DCC whitelist file contains blank lines, comments starting
+with "#",
+and lines of the following forms:
+.Bl -tag -offset 2n -width 4n -compact
+.It Ar include file
+Copies the contents of
+.Ar file
+into the whitelist.
+It can occur only in the main whitelist or whiteclnt file and not in an
+included file.
+The file name should be absolute or relative to the DCC home directory.
+.Pp
+.It Ar count Em value
+lines specify checksums that should be white- or blacklisted.
+.Bl -inset -offset 2n -compact
+.It Ar count Em env_From Ar 821-path
+.It Ar count Em env_To Ar dest-mailbox
+.It Ar count Em From Ar 822-mailbox
+.It Ar count Em Message-ID Ar <string>
+.It Ar count Em Received Ar string
+.It Ar count Em Substitute Ar header string
+.It Ar count Ar Hex ctype cksum
+.It Ar count Em ip Ar IP-address
+.El
+.Pp
+.Bl -tag -offset 2n -width 4n -compact
+.It Ar MANY Em value
+indicates that millions of targets have received messages with
+the header, IP address, or checksum
+.Em value .
+.It Ar OK Em value
+.It Ar OK2 Em value
+say that messages with
+the header, IP address, or checksum
+.Em value
+are OK and should not reported to DCC servers
+or be greylisted.
+.Ar OK2
+says that the message is "half OK."
+Two
+.Ar OK2
+checksums associated with a message are equivalent to one
+.Ar OK .
+.br
+A DCC server never shares or
+.Em floods
+reports containing checksums
+marked in its whitelist with OK or OK2 to other servers.
+A DCC client does not report or ask its server about messages
+with a checksum marked OK or OK2 in the client whitelist.
+This is intended to allow a DCC client to keep private mail
+so private that even its checksums are not disclosed.
+.It Ar MX Em IP-address-or-hostname
+.It Ar MXDCC Em IP-address-or-hostname
+mark an address or block of addresses of trust mail relays including
+MX servers, smart hosts, and bastion or DMZ relays.
+The DCC clients
+.Xr dccm 8 ,
+.Xr dccifd 8 ,
+and
+.Xr dccproc 8
+parse and skip initial Received: headers added by listed MX servers to
+determine the external sources of mail messages.
+Unsolicited bulk mail that has been forwarded through listed addresses
+is discarded by
+.Xr dccm 8
+and
+.Xr dccifd 8
+as if with
+.Fl a Ar DISCARD
+instead of rejected.
+.Ar MXDCC
+marks addresses that are MX servers that run DCC clients.
+The checksums for a mail message that has been forwarded through
+an address listed as MXDCC
+queried instead of reported.
+.It Ar SUBMIT Em IP-address-or-hostname
+marks an IP address or block addresses of SMTP submission clients
+such as web browsers
+that cannot tolerate 4yz temporary rejections
+but that cannot be trusted to not send spam.
+Since they are local addresses, DCC Reputations are not computed for them.
+.El
+.Pp
+.Ar value
+in
+.Ar count Em value
+lines can be
+.Bl -tag -offset 2n -width 4n -compact
+.It Ar dest-mailbox
+is an RFC\ 821 address or a local user name.
+.It Ar 821-path
+is an RFC\ 821 address.
+.It Ar 822-mailbox
+is an RFC\ 822 address with optional name.
+.It Em Substitute Ar header
+is the name of an SMTP header such as "Sender" or
+the name of one of two SMTP envlope values, "HELO," or
+"Mail_Host" for the resolved host name from the
+.Ar 821-path
+in
+the message.
+.It Ar Hex ctype cksum
+starts with the string
+.Em Hex
+followed a checksum type, and
+a string of four hexadecimal numbers obtained from a DCC log file
+or the
+.Xr dccproc 8
+command using
+.Fl CQ .
+The checksum type is
+.Em body , Fuz1 ,
+or
+.Em Fuz2
+or one of the preceding checksum types such as
+.Em env_From .
+.It Ar IP-address
+is a host name, IPv4 or IPv6 address, or a block
+of IP addresses in the standard xxx/mm from with
+mm limited for server whitelists to 16 for IPv4 or 112 for IPv6.
+There can be at most 64 CIDR blocks in a client
+.Pa whiteclnt
+file.
+A host name is converted to IP addresses with DNS,
+.Pa /etc/hosts
+or other mechanisms
+and one checksum for each addresses added to the whitelist.
+.El
+.Pp
+.It Ar option setting
+can only be in a DCC client
+.Pa whiteclnt
+file used by
+.Xr dccifd 8 ,
+.Xr dccm 8
+or
+.Xr dccproc 8 .
+Settings in per-user whiteclnt files override settings
+in the global file.
+.Ar Setting
+can be any of the following:
+.Bl -tag -offset 2n -width 2n -compact
+.It Ar option log-all
+to log all mail messages.
+.It Ar option log-normal
+to log only messages that meet the logging thresholds.
+.It Ar option log-subdirectory-day
+.It Ar option log-subdirectory-hour
+.It Ar option log-subdirectory-minute
+creates log files containing mail messages in subdirectories
+of the form
+.Ar JJJ ,
+.Ar JJJ/HH ,
+or
+.Ar JJJ/HH/MM
+where
+.Ar JJJ
+is the current julian day,
+.Ar HH
+is the current hour, and
+.Ar MM
+is the current minute.
+See also the
+.Fl l Ar logdir
+option for
+.Xr dccm 8 ,
+.Xr dccifd 8 ,
+and
+.Xr dccproc 8 .
+.It Ar option dcc-on
+.It Ar option dcc-off
+Control DCC filtering.
+See the discussion of
+.Fl W
+for
+.Xr dccm 8
+and
+.Xr dccifd 8 .
+.It Ar option greylist-on
+.It Ar option greylist-off
+to control greylisting.
+Greylisting for other recipients in the same SMTP transaction
+can still cause greylist temporary rejections.
+.Ar greylist-off
+in the main whiteclnt file.
+.It Ar option greylist-log-on
+.It Ar option greylist-log-off
+to control logging of greylisted mail messages.
+.It Ar option DCC-rep-off
+.It Ar option DCC-rep-on
+to honor or ignore DCC Reputations computed by the DCC server.
+.It Ar option DNSBL1-off
+.It Ar option DNSBL1-on
+.It Ar option DNSBL2-off
+.It Ar option DNSBL2-on
+.It Ar option DNSBL3-off
+.It Ar option DNSBL3-on
+honor or ignore results of DNS blacklist checks configured with
+.Fl B
+for
+.Xr dccm 8 ,
+.Xr dccifd 8 ,
+and
+.Xr dccproc 8 .
+.It Ar option MTA-first
+.It Ar option MTA-last
+consider MTA determinations of spam or not-spam first so they can be overridden
+by
+.Pa whiteclnt
+files, or last so that they can override
+.Pa whiteclnt files.
+.It Ar option forced-discard-ok
+.It Ar option no-forced-discard
+control whether
+.Xr dccm 8
+and
+.Xr dccifd 8
+are allowed to discard a message for one mailbox for which
+it is spam when it is not spam and must be delivered to another mailbox.
+This can happen if a mail message is addressed to two or more mailboxes with
+differing whitelists.
+Discarding can be undesirable because false positives are not communicated
+to mail senders.
+To avoid discarding,
+.Xr dccm 8
+and
+.Xr dccifd 8
+running in proxy mode temporarily reject SMTP envelope
+.Em Rcpt To
+values that involve differing
+.Pa whiteclnt
+files.
+.It Ar option threshold type,rej-thold
+has the same effects as
+.Fl c Ar type,rej-thold
+for
+.Xr dccproc 8
+or
+.Fl t Ar type,rej-thold
+for
+.Xr dccm 8
+and
+.Xr dccifd 8 .
+It is useful only in per-user whiteclnt files to override the global
+DCC checksum thresholds.
+.It Ar option spam-trap-accept
+.It Ar option spam-trap-reject
+say that mail should be reported to the DCC server as extremely
+bulk or with target counts of
+.Ar MANY .
+Greylisting, DNS blacklist (DNSBL), and other checks are turned off.
+.Ar Spam-trap-accept
+tells the MTA to accept the message while
+.Ar spam-trap-reject
+tells the MTA to reject the message.
+Use
+.Ar Spam-trap-accept
+for spam traps that should not be disclosed.
+.Ar Spam-trap-reject
+can be used  on
+.Em catch-all
+mailboxes that might receive legitimate mail by typographical errors
+and that senders should be told about.
+.El
+.Pp
+In the absence of explicit settings,
+the default in the main whiteclnt file is equivalent to
+.Bl -hang -offset 4n -width 4n -compact
+.It Ar option log-normal
+.It Ar option dcc-on
+.It Ar option greylist-on
+.It Ar option greylist-log-on
+.It Ar option DCC-rep-off
+.It Ar option DNSBL1-off
+.It Ar option DNSBL2-off
+.It Ar option DNSBL3-off
+.It Ar MTA-last
+.It Ar option no-forced-discard
+.El
+The defaults for individual recipient
+.Pa whiteclnt
+files are the same except as change by explicit settings
+in the main file.
+.El
+.Pp
+Checksums of the IP address of the SMTP client sending a mail message
+are practically unforgeable, because it is impractical for
+an SMTP client to "spoof" its address or pretend to use some other IP address.
+That would make the IP address of the sender useful for whitelisting,
+except that the IP address of the SMTP client
+is often not available to users of
+.Xr dccproc 8 .
+In addition, legitimate mail relays make whitelist entries for IP
+addresses of little use.
+For example,
+the IP address from which a message arrived might be that of a
+local relay instead of the home address of a whitelisted mailing list.
+.Pp
+Envelope and header
+.Ar From
+values can be forged,
+so whitelist entries for their checksums are not entirely reliable.
+.Pp
+Checksums of
+.Ar env_To
+values are never sent to DCC servers.
+They are valid in only
+.Pa whiteclnt
+files
+and used only by
+.Xr dccm 8 ,
+.Xr dccifd 8 ,
+and
+.Xr dccproc 8
+when the envelope
+.Em Rcpt To
+value is known.
+.Ss Greylists
+The DCC server,
+.Xr dccd 8 ,
+can be used to maintain a greylist database for some DCC clients
+including
+.Xr dccm 8
+and
+.Xr dccifd 8 .
+Greylisting involves temporarily refusing mail from unfamiliar
+SMTP clients and is unrelated to filtering with a
+Distributed Checksum Clearinghouse.
+.br
+See http://projects.puremagic.com/greylisting/
+.Ss Privacy
+Because sending mail is a less private act than receiving it,
+and because sending bulk mail is usually not private at all
+and cannot be very private,
+the DCC tries first to protect the privacy of mail recipients,
+and second the privacy of senders of mail that is not bulk.
+.Pp
+DCC clients necessarily disclose some information about mail they have
+received.
+The DCC database contains checksums of mail bodies,
+header lines, and source addresses.
+While it contains significantly less information than is
+available by "snooping" on Internet links,
+it is important that the DCC database be treated as containing
+sensitive information and to not put the most private information
+in the DCC database.
+Given the contents of a message, one might determine
+whether that message has been received
+by a system that subscribes to the DCC.
+Guesses about the sender and addressee of a message can also be
+validated if the checksums of the message have been sent to a DCC server.
+.Pp
+Because the DCC is distributed,
+organizations can operate their own DCC servers, and configure
+them to share or "flood" only the checksums of bulk mail that is not
+in local whitelists.
+.Pp
+DCC clients should not report the checksums of messages known to be
+private to a DCC server.
+For example, checksums of messages local to
+a system or that are otherwise known a priori to not be unsolicited bulk
+should not be sent to a remote DCC server.
+This can accomplished by adding entries for the sender to the
+client's local whitelist file.
+Client whitelist files can also include entries for email recipients
+whose mail should not be reported to a DCC server.
+.Ss Security
+Whenever considering security,
+one must first consider the risks.
+The worst DCC security problems are
+unauthorized commands to a DCC service,
+denial of the DCC service,
+and corruption of DCC data.
+The worst that can be done with remote commands to a DCC server is
+to turn it off or otherwise cause it to stop responding.
+The DCC is designed to fail gracefully,
+so that a denial of service attack
+would at worst allow delivery of mail that would otherwise be rejected.
+Corruption of DCC data might at worst cause mail that is already
+somewhat "bulk" by virtue of being received by two or more people
+to appear have higher recipient numbers.
+Since DCC users
+.Em must
+whitelist all sources of legitimate bulk mail,
+this is also not a concern.
+Such security risks should be addressed,
+but only with defenses that don't cost more than the possible damage from
+an attack.
+.Pp
+The DCC must contend with senders of unsolicited bulk mail who
+resort to unlawful actions
+to express their displeasure at having their advertising blocked.
+Because the DCC protocol is based
+on UDP, an unhappy advertiser could try to
+flood a DCC server with
+packets supposedly from subscribers or non-subscribers.
+DCC servers defend against that attack by rate-limiting requests
+from anonymous users.
+.Pp
+Also because of the use of UDP, clients must be protected
+against forged answers to their queries.
+Otherwise an unsolicited bulk mail advertiser could send
+a stream of "not spam" answers to an SMTP
+client while simultaneously sending mail that would otherwise be
+rejected.
+This is not a problem for authenticated clients of the
+DCC because they share a secret with the DCC.
+Unauthenticated, anonymous DCC
+clients do not share any secrets with the DCC, except for unique and
+unpredictable bits in each query or report sent to the DCC.
+Therefore, DCC servers cryptographically sign answers to
+unauthenticated clients with bits from the corresponding queries.
+This protects against attackers that do not
+have access to the stream of packets from the DCC client.
+.Pp
+The passwords or shared secrets used in the DCC client and server programs
+are "cleartext" for several reasons.
+In any shared secret authentication system,
+at least one party must know the secret or keep the secret in cleartext.
+You could encrypt the secrets in a file, but because they are used
+by programs, you would need a cleartext copy of the key to decrypt
+the file somewhere in the system, making such a scheme more expensive
+but no more secure than a file of cleartext passwords.
+Asymmetric systems such as that used in UNIX allow one party to not
+know the secrets, but they must be and are
+designed to be computationally expensive when used in applications
+like the DCC that involve thousands or more authentication checks per second.
+Moreover, because of "dictionary attacks,"
+asymmetric systems are now little more secure than
+keeping passwords in cleartext.
+An adversary can compare the hash values of combinations of common words
+with /etc/passwd hash values to look for bad passwords.
+Worse, by the nature of a client/server protocol like that used in
+the DCC, clients must have the cleartext password.
+Since it is among the more numerous and much less secure clients
+that adversaries would seek files of DCC passwords,
+it would be a waste to complicate the DCC server with an asymmetric
+system.
+.Pp
+The DCC protocol is vulnerable to dictionary attacks to recover passwords.
+An adversary could capture some DCC packets, and then check to see
+if any of the 100,000 to 1,000,000 passwords in so called
+"cracker dictionaries"
+applied to a packet generated the same signature.
+This is a concern only if DCC passwords are poorly chosen, such
+as any combination of words in an English dictionary.
+There are ways to prevent this vulnerability regardless of
+how badly passwords are chosen, but they are computationally expensive
+and require additional network round trips.
+Since DCC passwords are created and typed into files once
+and do not need to be remembered by people,
+it is cheaper and quite easy to simply choose good passwords
+that are not in dictionaries.
+.Ss Reliability
+It is better to fail to filter unsolicited bulk mail than to fail
+to deliver legitimate mail, so DCC clients fail in the direction of
+assuming that mail is legitimate or even whitelisted.
+.Pp
+A DCC client sends a report or other request and waits for an answer.
+If no answer arrives within a reasonable time,
+the client retransmits.
+There are many things that
+might result in the client not receiving an answer,
+but the most important is packet loss.
+If the client's request does not reach the server,
+it is easy and harmless for the client to retransmit.
+If the client's request reached the server but the server's response was lost,
+a retransmission to the same server would be misunderstood as
+a new report of another copy of the same message unless it is detected
+as a retransmission by the server.
+The DCC protocol includes transactions identifiers for this purpose.
+If the client retransmitted to a second server,
+the retransmission would be misunderstood by the second server as
+a new report of the same message.
+.Pp
+Each request from a client includes a timestamp to aid the client in
+measuring the round trip time to the server and to let the client pick
+the closest server.
+Clients monitor the speed of all of the servers they know including
+those they are not currently using,
+and use the quickest.
+.Ss Client and Server-IDs
+Servers and clients use numbers or IDs to identify themselves.
+ID 1 is reserved for anonymous, unauthenticated clients.
+All other IDs are associated with a pair of passwords in the
+.Pa ids
+file, the
+current and next or previous and current passwords.
+Clients included their client IDs in their messages.
+When they are not using the anonymous ID,
+they sign their messages to servers with the first password
+associated with their client-ID.
+Servers treat messages with signatures that match neither of the passwords
+for the client-ID in their own
+.Pa ids
+file as if the client had used the anonymous ID.
+.Pp
+Each server has a unique
+.Em server-ID
+less than 32768.
+Servers use their IDs to identify checksums that they
+.Em flood
+to other servers.
+Each server expects local clients sending administrative
+commands to use the server's ID and sign administrative commands
+with the associated password.
+.Pp
+Server-IDs must be unique among all systems that share reports
+by "flooding."
+All servers must be told of the IDs all other servers whose
+reports can be received in the local
+.Pa @prefix@/flod
+file described in
+.Xr dccd 8 .
+However, server-IDs can be mapped during flooding between
+independent DCC organizations.
+.Pp
+.Em Passwd-IDs
+are server-IDs that should not be assigned to servers.
+They appear in the often publicly readable
+.Pa @prefix@/flod
+and specify passwords in the private
+.Pa @prefix@/ids
+file for the inter-server flooding protocol
+.Pp
+The client identified by a
+.Em client-ID
+might be a single computer with a
+single IP address, a single but multi-homed computer, or many computers.
+Client-IDs are not used to identify checksum reports, but
+the organization operating the client.
+A client-ID need only be unique among clients using a single server.
+A single client can use different client-IDs for different servers,
+each client-ID authenticated with a separate password.
+.Pp
+An obscure but important part of all of this is that the
+inter-server flooding algorithm
+depends on server-IDs and timestamps attached to reports of checksums.
+The inter-server flooding mechanism
+requires cooperating DCC servers to maintain reasonable clocks
+ticking in UTC.
+Clients include timestamps in their requests, but as long as their
+timestamps are unlikely to be repeated, they need not be very accurate.
+.Ss Installation Considerations
+DCC clients on a computer share information about which servers
+are currently working and their speeds in a shared memory segment.
+This segment also contains server host names, IP addresses, and
+the passwords needed to authenticate known clients to servers.
+That generally requires that
+.Xr dccm 8 ,
+.Xr dccproc 8 ,
+.Xr dccifd 8 ,
+and
+.Xr cdcc 8
+execute with an UID that
+can write to the DCC home directory and its files.
+The sendmail interface, dccm,
+is a daemon that can be started by an "rc" or other script already
+running with the correct UID.
+The other two, dccproc and cdcc need to be set-UID because they are
+used by end users.
+They relinquish set-UID privileges when not needed.
+.Pp
+Files that contain cleartext passwords including the shared file used by clients
+must be readable only by "owner."
+.Pp
+The data files required by a DCC can be in a single "home" directory,
+.Pa @prefix@ .
+Distinct DCC servers can run on a single computer, provided they use
+distinct UDP port numbers and home directories.
+It is possible and convenient for the DCC clients using a server
+on the same computer to use the same home directory as the server.
+.Pp
+The DCC source distribution includes sample control files.
+They should be modified appropriately and then copied to the DCC
+home directory.
+Files that contain cleartext passwords must not be publicly readable.
+.Pp
+The DCC source includes "feature" m4 files to configure
+sendmail to use
+.Xr dccm 8
+to check a DCC server about incoming mail.
+.Pp
+See also the INSTALL.html file.
+.Ss Client Installation
+Installing a DCC client starts with obtaining or compiling program binaries
+for the client server data control tool,
+.Xr cdcc 8 .
+Installing the sendmail DCC interface,
+.Xr dccm 8 ,
+or
+.Xr dccproc 8 ,
+the general or
+.Xr procmail 1
+interface
+is the main part of the client installation.
+Connecting the DCC to sendmail with dccm is most powerful,
+but requires administrative control of the system running sendmail.
+.Pp
+As noted above, cdcc and dccproc should be
+set-UID to a suitable UID.
+Root or 0 is thought to be safe for both, because they are
+careful to release privileges except when they need them to
+read or write files in the DCC home directory.
+A DCC home directory,
+.Pa @prefix@
+should be created.
+It must be owned and writable by the UID to which cdcc is set.
+.Pp
+After the DCC client programs have been obtained,
+contact the operator(s) of the chosen DCC server(s)
+to obtain
+each server's
+hostname,
+port number,
+and a
+.Em client-ID
+and corresponding password.
+No client-IDs or passwords are needed touse
+DCC servers that allow anonymous clients.
+Use the
+.Em load
+or
+.Em add
+commands
+of cdcc to create a
+.Pa map
+file in the DCC home directory.
+It is usually necessary to create a client whitelist file of
+the format described above.
+To accommodate users sharing a computer but not ideas about what
+is solicited bulk mail,
+the client whitelist file can be any valid path name
+and need not be in the DCC home directory.
+.Pp
+If dccm is chosen,
+arrange to start it with suitable arguments
+before sendmail is started.
+See the
+.Pa homedir/dcc_conf
+file and the
+.Pa misc/rcDCC
+script in the DCC source.
+The procmail DCCM interface,
+.Xr dccproc 8 ,
+can be run manually or by a
+.Xr procmailrc 5
+rule.
+.Ss Server Installation
+The DCC server,
+.Xr dccd 8 ,
+also requires that the DCC home directory exist.
+It does not use the client shared or memory mapped file of server
+addresses,
+but it requires other files.
+One is the
+.Pa @prefix@/ids
+file of client-IDs,  server-IDs, and corresponding passwords.
+Another is a
+.Pa flod
+file of peers that send and receive floods of reports of checksums
+with large counts.
+Both files are described
+in
+.Xr dccd 8 .
+.Pp
+The server daemon should be started when the system is rebooted,
+probably before sendmail.
+See the
+.Pa misc/rcDCC
+and
+.Pa misc/start-dccd
+files in the DCC source.
+.Pp
+The database should be cleaned regularly with
+.Xr dbclean 8
+such as by running the crontab job that is in the misc directory.
+.Sh SEE ALSO
+.Xr cdcc 8 ,
+.Xr dbclean 8 ,
+.Xr dcc 8 ,
+.Xr dccd 8 ,
+.Xr dccifd 8 ,
+.Xr dccm 8 ,
+.Xr dccproc 8 ,
+.Xr dblist 8 ,
+.Xr dccsight 8 ,
+.Xr sendmail 8 .
+.Sh HISTORY
+Distributed Checksum Clearinghouses are based on an idea of Paul Vixie
+with code designed and written at Rhyolite Software starting in 2000.
+This document describes version 1.3.103.
diff -r 000000000000 -r c7f6b056b673 dcc.html.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcc.html.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,650 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<HTML>
+<HEAD>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <TITLE>dcc.0.8</TITLE>
+    <META http-equiv="Content-Style-Type" content="text/css">
+    <STYLE type="text/css">
+	BODY {background-color:white; color:black}
+	ADDRESS {font-size:smaller}
+        IMG.logo {width:6em; vertical-align:middle}
+    </STYLE>
+</HEAD>
+<BODY>
+<PRE>
+<!-- Manpage converted by man2html 3.0.1 -->
+<B><A HREF="dcc.html">DCC(8)</A></B>                Distributed Checksum Clearinghouse                <B><A HREF="dcc.html">DCC(8)</A></B>
+
+
+</PRE>
+<H2><A NAME="NAME">NAME</A></H2><PRE>
+     <B>DCC</B> -- Distributed Checksum Clearinghouse
+
+
+</PRE>
+<H2><A NAME="DESCRIPTION">DESCRIPTION</A></H2><PRE>
+     The Distributed Checksum Clearinghouse or <B>DCC</B> is a cooperative, distrib-
+     uted system intended to detect "bulk" mail or mail sent to many people.
+     It allows individuals receiving a single mail message to determine that
+     many other people have received essentially identical copies of the mes-
+     sage and so reject or discard the message.
+
+     Source for the server, client, and utilities is available at Rhyolite
+     Software, LLC, http://www.rhyolite.com/dcc/ It is free for organizations
+     that do not sell spam or virus filtering services.
+
+   <A NAME="How-the-DCC-Is-Used"><B>How the DCC Is Used</B></A>
+     The DCC can be viewed as a tool for end users to enforce their right to
+     "opt-in" to streams of bulk mail by refusing bulk mail except from
+     sources in a "whitelist."  Whitelists are the responsibility of DCC
+     clients, since only they know which bulk mail they solicited.
+
+     False positives or mail marked as bulk by a DCC server that is not bulk
+     occur only when a recipient of a message reports it to a DCC server as
+     having been received many times or when the "fuzzy" checksums of differ-
+     ing messages are the same.  The fuzzy checksums ignore aspects of mes-
+     sages in order to compute identical checksums for substantially identical
+     messages.  The fuzzy checksums are designed to ignore only differences
+     that do not affect meanings.  So in practice, you do not need to worry
+     about DCC false positive indications of "bulk," but not all bulk mail is
+     unsolicited bulk mail or spam.  You must either use whitelists to distin-
+     guish solicited from unsolicited bulk mail or only use DCC indications of
+     "bulk" as part of a scoring system such as SpamAssassin.  Besides unso-
+     licited bulk email or spam, bulk messages include legitimate mail such as
+     order confirmations from merchants, legitimate mailing lists, and empty
+     or test messages.
+
+     A DCC server estimates the number copies of a message by counting check-
+     sums reported by DCC clients.  Each client must decide which bulk mes-
+     sages are unsolicited and what degree of "bulkiness" is objectionable.
+     Client DCC software marks, rejects, or discards mail that is bulk accord-
+     ing to local thresholds on target addresses from DCC servers and unso-
+     licited according to local whitelists.
+
+     DCC servers are usually configured to receive reports from as many tar-
+     gets as possible, including sources that cannot be trusted to not exag-
+     gerate the number of copies of a message they see.  A user of a DCC
+     client angry about receiving a message could report it with 1,000,000
+     separate DCC reports or with a single report claiming 1,000,000 targets.
+     An unprincipled user could subscribe a "spam trap" to mailing lists such
+     as those of the IETF or CERT.  Such abuses of the system area not prob-
+     lems, because much legitimate mail is "bulk."  You cannot reject bulk
+     mail unless you have a whitelist of sources of legitimate bulk mail.
+
+     DCC can also be used by an Internet service provider to detect bulk mail
+     coming from its own customers.  In such circumstances, the DCC client
+     might be configured to only log bulk mail from unexpected (not
+     whitelisted) customers.
+
+   <A NAME="What-the-DCC-Is"><B>What the DCC Is</B></A>
+     A DCC server accumulates counts of cryptographic checksums of messages
+     but not the messages themselves.  It exchanges reports of frequently seen
+     checksums with other servers.  DCC clients send reports of checksums
+     related to incoming mail to a nearby DCC server running <B><A HREF="dccd.html">dccd(8)</A></B>.  Each
+     report from a client includes the number of recipients for the message.
+     A DCC server accumulates the reports and responds to clients the the cur-
+     rent total number of recipients for each checksum.  The client adds an
+     SMTP header to incoming mail containing the total counts.  It then dis-
+     cards or rejects mail that is not whitelisted and has counts that exceed
+     local thresholds.
+
+     A special value of the number of addressees is "MANY" and means it is
+     certain that this message was bulk and might be unsolicited, perhaps
+     because it came from a locally blacklisted source or was addressed to an
+     invalid address or "spam trap."  The special value "MANY" is merely the
+     largest value that fits in the fixed sized field containing the count of
+     addressees.  That "infinity" accumulated total can be reached with mil-
+     lions of independent reports as well as with one or two.
+
+     DCC servers <I>flood</I> or send reports of checksums of bulk mail to neighbor-
+     ing servers.
+
+     To keep a server's database of checksums from growing without bound,
+     checksums are forgotten when they become old.  Checksums of bulk mail are
+     kept longer.  See <B><A HREF="dbclean.html">dbclean(8)</A></B>.
+
+     DCC clients pick the nearest working DCC server using a small shared or
+     memory mapped file, <I>@prefix@/map</I>.  It contains server names, port num-
+     bers, passwords, recent performance measures, and so forth.  This file
+     allows clients to use quick retransmission timeouts and to waste little
+     time on servers that have temporarily stopped working or become unreach-
+     able.  The utility program <B><A HREF="cdcc.html">cdcc(8)</A></B> is used to maintain this file as well
+     as to check the health of servers.
+
+   <A NAME="X-DCC-Headers"><B>X-DCC Headers</B></A>
+     The DCC software includes several programs used by clients.  <B><A HREF="dccm.html">Dccm(8)</A></B> uses
+     the sendmail "milter" interface to query a DCC server, add header lines
+     to incoming mail, and reject mail whose total checksum counts are high.
+     Dccm is intended to be run with SMTP servers using sendmail.
+
+     <B><A HREF="dccproc.html">Dccproc(8)</A></B> adds header lines to mail presented by file name or <I>stdin</I>, but
+     relies on other programs such as procmail to deal with mail with large
+     counts.  <B><A HREF="dccsight.html">Dccsight(8)</A></B> is similar but deals with previously computed check-
+     sums.
+
+     <B><A HREF="dccifd.html">Dccifd(8)</A></B> is similar to dccproc but is not run separately for each mail
+     message and so is far more efficient.  It receives mail messages via a
+     socket somewhat like dccm, but with a simpler protocol that can be used
+     by Perl scripts or other programs.
+
+     DCC SMTP header lines are of one of the forms:
+
+       X-DCC-brand-Metrics: client server-ID; bulk cknm1=count cknm2=count ...
+       X-DCC-brand-Metrics: client; whitelist
+     where
+        <I>whitelist</I> appears if the global or per-user <I>whiteclnt</I> file marks the
+                message as good.
+        <I>brand</I>   is the "brand name" of the DCC server, such as "RHYOLITE".
+        <I>client</I>  is the name or IP address of the DCC client that added the
+                header line to the SMTP message.
+        <I>server-ID</I> is the numeric ID of the DCC server that the DCC client con-
+                tacted.
+        <I>bulk</I>    is present if one or more checksum counts exceeded the DCC
+                client's thresholds to make the message "bulky."
+        <I>bulk</I> <I>rep</I> is present if the DCC reputation of the IP address of the
+                sender is bad.
+        <I>cknm1</I>,<I>cknm2</I>,... are types of checksums:
+                  <I>IP</I>           address of SMTP client
+                  <I>env</I><B>_</B><I>From</I>     SMTP envelope value
+                  <I>From</I>         SMTP header line
+                  <I>Message-ID</I>   SMTP header line
+                  <I>Received</I>     last Received: header line in the SMTP message
+                  <I>substitute</I>   SMTP header line chosen by the DCC client, pre-
+                               fixed with the name of the header
+                  <I>Body</I>         SMTP body ignoring white-space
+                  <I>Fuz1</I>         filtered or "fuzzy" body checksum
+                  <I>Fuz2</I>         another filtered or "fuzzy" body checksum
+                  <I>rep</I>          DCC reputation of the mail sender or the esti-
+                               mated probability that the message is bulk.
+                Counts for <I>IP</I>, <I>env</I><B>_</B><I>From</I>, <I>From</I>, <I>Message-Id</I>, <I>Received</I>, and
+                <I>substitute</I> checksums are omitted by the DCC client if the
+                server says it has no information.  Counts for <I>Fuz1</I> and <I>Fuz2</I>
+                are omitted if the message body is empty or contains too lit-
+                tle of the right kind of information for the checksum to be
+                computed.
+        <I>count</I>   is the total number of recipients of messages with that check-
+                sum reported directly or indirectly to the DCC server.  The
+                special count "MANY" means that DCC client have claimed that
+                the message is directed at millions of recipients.  "MANY"
+                imples the message is definitely bulk, but not necessarily
+                unsolicited.  The special counts "OK" and "OK2" mean the
+                checksum has been marked "good" or "half-good" by DCC servers.
+
+   <A NAME="Mailing-lists"><B>Mailing lists</B></A>
+     Legitimate mailing list traffic differs from spam only in being solicited
+     by recipients.  Each client should have a private whitelist.
+
+     DCC whitelists can also mark mail as unsolicited bulk using blacklist
+     entries for commonly forged values such as "From: user@public.com".
+
+   <A NAME="White-and-Blacklists"><B>White and Blacklists</B></A>
+     DCC server and client whitelist files share a common format.  Server
+     files are always named <I>whitelist</I> and one is required to be in the DCC
+     home directory with the other server files.  Client whitelist files are
+     named <I>whiteclnt</I> in the DCC home directory or a subdirectory specified
+     with the <B>-U</B> option for <B><A HREF="dccm.html">dccm(8)</A></B>.  They specify mail that should not be
+     reported to a DCC server or that is always unsolicited and almost cer-
+     tainly bulk.
+
+     A DCC whitelist file contains blank lines, comments starting with "#",
+     and lines of the following forms:
+       <I>include</I> <I>file</I>
+             Copies the contents of <I>file</I> into the whitelist.  It can occur
+             only in the main whitelist or whiteclnt file and not in an
+             included file.  The file name should be absolute or relative to
+             the DCC home directory.
+
+       <I>count</I> <I>value</I>
+             lines specify checksums that should be white- or blacklisted.
+               <I>count</I> <I>env</I><B>_</B><I>From</I> <I>821-path</I>
+               <I>count</I> <I>env</I><B>_</B><I>To</I> <I>dest-mailbox</I>
+               <I>count</I> <I>From</I> <I>822-mailbox</I>
+               <I>count</I> <I>Message-ID</I> <I>&lt;string&gt;</I>
+               <I>count</I> <I>Received</I> <I>string</I>
+               <I>count</I> <I>Substitute</I> <I>header</I> <I>string</I>
+               <I>count</I> <I>Hex</I> <I>ctype</I> <I>cksum</I>
+               <I>count</I> <I>ip</I> <I>IP-address</I>
+
+               <I>MANY</I> <I>value</I>
+                     indicates that millions of targets have received messages
+                     with the header, IP address, or checksum <I>value</I>.
+               <I>OK</I> <I>value</I>
+               <I>OK2</I> <I>value</I>
+                     say that messages with the header, IP address, or check-
+                     sum <I>value</I> are OK and should not reported to DCC servers
+                     or be greylisted.  <I>OK2</I> says that the message is "half
+                     OK."  Two <I>OK2</I> checksums associated with a message are
+                     equivalent to one <I>OK</I>.
+                     A DCC server never shares or <I>floods</I> reports containing
+                     checksums marked in its whitelist with OK or OK2 to other
+                     servers.  A DCC client does not report or ask its server
+                     about messages with a checksum marked OK or OK2 in the
+                     client whitelist.  This is intended to allow a DCC client
+                     to keep private mail so private that even its checksums
+                     are not disclosed.
+               <I>MX</I> <I>IP-address-or-hostname</I>
+               <I>MXDCC</I> <I>IP-address-or-hostname</I>
+                     mark an address or block of addresses of trust mail
+                     relays including MX servers, smart hosts, and bastion or
+                     DMZ relays.  The DCC clients <B><A HREF="dccm.html">dccm(8)</A></B>, <B><A HREF="dccifd.html">dccifd(8)</A></B>, and
+                     <B><A HREF="dccproc.html">dccproc(8)</A></B> parse and skip initial Received: headers added
+                     by listed MX servers to determine the external sources of
+                     mail messages.  Unsolicited bulk mail that has been for-
+                     warded through listed addresses is discarded by <B><A HREF="dccm.html">dccm(8)</A></B>
+                     and <B><A HREF="dccifd.html">dccifd(8)</A></B> as if with <B>-a</B> <I>DISCARD</I> instead of rejected.
+                     <I>MXDCC</I> marks addresses that are MX servers that run DCC
+                     clients.  The checksums for a mail message that has been
+                     forwarded through an address listed as MXDCC queried
+                     instead of reported.
+               <I>SUBMIT</I> <I>IP-address-or-hostname</I>
+                     marks an IP address or block addresses of SMTP submission
+                     clients such as web browsers that cannot tolerate 4yz
+                     temporary rejections but that cannot be trusted to not
+                     send spam.  Since they are local addresses, DCC Reputa-
+                     tions are not computed for them.
+
+             <I>value</I> in <I>count</I> <I>value</I> lines can be
+               <I>dest-mailbox</I>
+                     is an RFC 821 address or a local user name.
+               <I>821-path</I>
+                     is an RFC 821 address.
+               <I>822-mailbox</I>
+                     is an RFC 822 address with optional name.
+               <I>Substitute</I> <I>header</I>
+                     is the name of an SMTP header such as "Sender" or the
+                     name of one of two SMTP envlope values, "HELO," or
+                     "Mail_Host" for the resolved host name from the <I>821-path</I>
+                     in the message.
+               <I>Hex</I> <I>ctype</I> <I>cksum</I>
+                     starts with the string <I>Hex</I> followed a checksum type, and
+                     a string of four hexadecimal numbers obtained from a DCC
+                     log file or the <B><A HREF="dccproc.html">dccproc(8)</A></B> command using <B>-CQ</B>.  The check-
+                     sum type is <I>body</I>, <I>Fuz1</I>, or <I>Fuz2</I> or one of the preceding
+                     checksum types such as <I>env</I><B>_</B><I>From</I>.
+               <I>IP-address</I>
+                     is a host name, IPv4 or IPv6 address, or a block of IP
+                     addresses in the standard xxx/mm from with mm limited for
+                     server whitelists to 16 for IPv4 or 112 for IPv6.  There
+                     can be at most 64 CIDR blocks in a client <I>whiteclnt</I> file.
+                     A host name is converted to IP addresses with DNS,
+                     <I>/etc/hosts</I> or other mechanisms and one checksum for each
+                     addresses added to the whitelist.
+
+       <I>option</I> <I>setting</I>
+             can only be in a DCC client <I>whiteclnt</I> file used by <B><A HREF="dccifd.html">dccifd(8)</A></B>,
+             <B><A HREF="dccm.html">dccm(8)</A></B> or <B><A HREF="dccproc.html">dccproc(8)</A></B>.  Settings in per-user whiteclnt files
+             override settings in the global file.  <I>Setting</I> can be any of the
+             following:
+               <I>option</I> <I>log-all</I>
+                   to log all mail messages.
+               <I>option</I> <I>log-normal</I>
+                   to log only messages that meet the logging thresholds.
+               <I>option</I> <I>log-subdirectory-day</I>
+               <I>option</I> <I>log-subdirectory-hour</I>
+               <I>option</I> <I>log-subdirectory-minute</I>
+                   creates log files containing mail messages in subdirecto-
+                   ries of the form <I>JJJ</I>, <I>JJJ/HH</I>, or <I>JJJ/HH/MM</I> where <I>JJJ</I> is the
+                   current julian day, <I>HH</I> is the current hour, and <I>MM</I> is the
+                   current minute.  See also the <B>-l</B> <I>logdir</I> option for <B><A HREF="dccm.html">dccm(8)</A></B>,
+                   <B><A HREF="dccifd.html">dccifd(8)</A></B>, and <B><A HREF="dccproc.html">dccproc(8)</A></B>.
+               <I>option</I> <I>dcc-on</I>
+               <I>option</I> <I>dcc-off</I>
+                   Control DCC filtering.  See the discussion of <B>-W</B> for
+                   <B><A HREF="dccm.html">dccm(8)</A></B> and <B><A HREF="dccifd.html">dccifd(8)</A></B>.
+               <I>option</I> <I>greylist-on</I>
+               <I>option</I> <I>greylist-off</I>
+                   to control greylisting.  Greylisting for other recipients
+                   in the same SMTP transaction can still cause greylist tem-
+                   porary rejections.  <I>greylist-off</I> in the main whiteclnt
+                   file.
+               <I>option</I> <I>greylist-log-on</I>
+               <I>option</I> <I>greylist-log-off</I>
+                   to control logging of greylisted mail messages.
+               <I>option</I> <I>DCC-rep-off</I>
+               <I>option</I> <I>DCC-rep-on</I>
+                   to honor or ignore DCC Reputations computed by the DCC
+                   server.
+               <I>option</I> <I>DNSBL1-off</I>
+               <I>option</I> <I>DNSBL1-on</I>
+               <I>option</I> <I>DNSBL2-off</I>
+               <I>option</I> <I>DNSBL2-on</I>
+               <I>option</I> <I>DNSBL3-off</I>
+               <I>option</I> <I>DNSBL3-on</I>
+                   honor or ignore results of DNS blacklist checks configured
+                   with <B>-B</B> for <B><A HREF="dccm.html">dccm(8)</A></B>, <B><A HREF="dccifd.html">dccifd(8)</A></B>, and <B><A HREF="dccproc.html">dccproc(8)</A></B>.
+               <I>option</I> <I>MTA-first</I>
+               <I>option</I> <I>MTA-last</I>
+                   consider MTA determinations of spam or not-spam first so
+                   they can be overridden by <I>whiteclnt</I> files, or last so that
+                   they can override <I>whiteclnt</I> <I>files.</I>
+               <I>option</I> <I>forced-discard-ok</I>
+               <I>option</I> <I>no-forced-discard</I>
+                   control whether <B><A HREF="dccm.html">dccm(8)</A></B> and <B><A HREF="dccifd.html">dccifd(8)</A></B> are allowed to dis-
+                   card a message for one mailbox for which it is spam when it
+                   is not spam and must be delivered to another mailbox.  This
+                   can happen if a mail message is addressed to two or more
+                   mailboxes with differing whitelists.  Discarding can be
+                   undesirable because false positives are not communicated to
+                   mail senders.  To avoid discarding, <B><A HREF="dccm.html">dccm(8)</A></B> and <B><A HREF="dccifd.html">dccifd(8)</A></B>
+                   running in proxy mode temporarily reject SMTP envelope <I>Rcpt</I>
+                   <I>To</I> values that involve differing <I>whiteclnt</I> files.
+               <I>option</I> <I>threshold</I> <I>type,rej-thold</I>
+                   has the same effects as <B>-c</B> <I>type,rej-thold</I> for <B><A HREF="dccproc.html">dccproc(8)</A></B> or
+                   <B>-t</B> <I>type,rej-thold</I> for <B><A HREF="dccm.html">dccm(8)</A></B> and <B><A HREF="dccifd.html">dccifd(8)</A></B>.  It is useful
+                   only in per-user whiteclnt files to override the global DCC
+                   checksum thresholds.
+               <I>option</I> <I>spam-trap-accept</I>
+               <I>option</I> <I>spam-trap-reject</I>
+                   say that mail should be reported to the DCC server as
+                   extremely bulk or with target counts of <I>MANY</I>.  Greylisting,
+                   DNS blacklist (DNSBL), and other checks are turned off.
+                   <I>Spam-trap-accept</I> tells the MTA to accept the message while
+                   <I>spam-trap-reject</I> tells the MTA to reject the message.  Use
+                   <I>Spam-trap-accept</I> for spam traps that should not be dis-
+                   closed.  <I>Spam-trap-reject</I> can be used  on <I>catch-all</I> mail-
+                   boxes that might receive legitimate mail by typographical
+                   errors and that senders should be told about.
+
+             In the absence of explicit settings, the default in the main
+             whiteclnt file is equivalent to
+                 <I>option</I> <I>log-normal</I>
+                 <I>option</I> <I>dcc-on</I>
+                 <I>option</I> <I>greylist-on</I>
+                 <I>option</I> <I>greylist-log-on</I>
+                 <I>option</I> <I>DCC-rep-off</I>
+                 <I>option</I> <I>DNSBL1-off</I>
+                 <I>option</I> <I>DNSBL2-off</I>
+                 <I>option</I> <I>DNSBL3-off</I>
+                 <I>MTA-last</I>
+                 <I>option</I> <I>no-forced-discard</I>
+             The defaults for individual recipient <I>whiteclnt</I> files are the
+             same except as change by explicit settings in the main file.
+
+     Checksums of the IP address of the SMTP client sending a mail message are
+     practically unforgeable, because it is impractical for an SMTP client to
+     "spoof" its address or pretend to use some other IP address.  That would
+     make the IP address of the sender useful for whitelisting, except that
+     the IP address of the SMTP client is often not available to users of
+     <B><A HREF="dccproc.html">dccproc(8)</A></B>.  In addition, legitimate mail relays make whitelist entries
+     for IP addresses of little use.  For example, the IP address from which a
+     message arrived might be that of a local relay instead of the home
+     address of a whitelisted mailing list.
+
+     Envelope and header <I>From</I> values can be forged, so whitelist entries for
+     their checksums are not entirely reliable.
+
+     Checksums of <I>env</I><B>_</B><I>To</I> values are never sent to DCC servers.  They are valid
+     in only <I>whiteclnt</I> files and used only by <B><A HREF="dccm.html">dccm(8)</A></B>, <B><A HREF="dccifd.html">dccifd(8)</A></B>, and
+     <B><A HREF="dccproc.html">dccproc(8)</A></B> when the envelope <I>Rcpt</I> <I>To</I> value is known.
+
+   <A NAME="Greylists"><B>Greylists</B></A>
+     The DCC server, <B><A HREF="dccd.html">dccd(8)</A></B>, can be used to maintain a greylist database for
+     some DCC clients including <B><A HREF="dccm.html">dccm(8)</A></B> and <B><A HREF="dccifd.html">dccifd(8)</A></B>.  Greylisting involves
+     temporarily refusing mail from unfamiliar SMTP clients and is unrelated
+     to filtering with a Distributed Checksum Clearinghouse.
+     See http://projects.puremagic.com/greylisting/
+
+   <A NAME="Privacy"><B>Privacy</B></A>
+     Because sending mail is a less private act than receiving it, and because
+     sending bulk mail is usually not private at all and cannot be very pri-
+     vate, the DCC tries first to protect the privacy of mail recipients, and
+     second the privacy of senders of mail that is not bulk.
+
+     DCC clients necessarily disclose some information about mail they have
+     received.  The DCC database contains checksums of mail bodies, header
+     lines, and source addresses.  While it contains significantly less infor-
+     mation than is available by "snooping" on Internet links, it is important
+     that the DCC database be treated as containing sensitive information and
+     to not put the most private information in the DCC database.  Given the
+     contents of a message, one might determine whether that message has been
+     received by a system that subscribes to the DCC.  Guesses about the
+     sender and addressee of a message can also be validated if the checksums
+     of the message have been sent to a DCC server.
+
+     Because the DCC is distributed, organizations can operate their own DCC
+     servers, and configure them to share or "flood" only the checksums of
+     bulk mail that is not in local whitelists.
+
+     DCC clients should not report the checksums of messages known to be pri-
+     vate to a DCC server.  For example, checksums of messages local to a sys-
+     tem or that are otherwise known a priori to not be unsolicited bulk
+     should not be sent to a remote DCC server.  This can accomplished by
+     adding entries for the sender to the client's local whitelist file.
+     Client whitelist files can also include entries for email recipients
+     whose mail should not be reported to a DCC server.
+
+   <A NAME="Security"><B>Security</B></A>
+     Whenever considering security, one must first consider the risks.  The
+     worst DCC security problems are unauthorized commands to a DCC service,
+     denial of the DCC service, and corruption of DCC data.  The worst that
+     can be done with remote commands to a DCC server is to turn it off or
+     otherwise cause it to stop responding.  The DCC is designed to fail
+     gracefully, so that a denial of service attack would at worst allow
+     delivery of mail that would otherwise be rejected.  Corruption of DCC
+     data might at worst cause mail that is already somewhat "bulk" by virtue
+     of being received by two or more people to appear have higher recipient
+     numbers.  Since DCC users <I>must</I> whitelist all sources of legitimate bulk
+     mail, this is also not a concern.  Such security risks should be
+     addressed, but only with defenses that don't cost more than the possible
+     damage from an attack.
+
+     The DCC must contend with senders of unsolicited bulk mail who resort to
+     unlawful actions to express their displeasure at having their advertising
+     blocked.  Because the DCC protocol is based on UDP, an unhappy advertiser
+     could try to flood a DCC server with packets supposedly from subscribers
+     or non-subscribers.  DCC servers defend against that attack by rate-lim-
+     iting requests from anonymous users.
+
+     Also because of the use of UDP, clients must be protected against forged
+     answers to their queries.  Otherwise an unsolicited bulk mail advertiser
+     could send a stream of "not spam" answers to an SMTP client while simul-
+     taneously sending mail that would otherwise be rejected.  This is not a
+     problem for authenticated clients of the DCC because they share a secret
+     with the DCC.  Unauthenticated, anonymous DCC clients do not share any
+     secrets with the DCC, except for unique and unpredictable bits in each
+     query or report sent to the DCC.  Therefore, DCC servers cryptographi-
+     cally sign answers to unauthenticated clients with bits from the corre-
+     sponding queries.  This protects against attackers that do not have
+     access to the stream of packets from the DCC client.
+
+     The passwords or shared secrets used in the DCC client and server pro-
+     grams are "cleartext" for several reasons.  In any shared secret authen-
+     tication system, at least one party must know the secret or keep the
+     secret in cleartext.  You could encrypt the secrets in a file, but
+     because they are used by programs, you would need a cleartext copy of the
+     key to decrypt the file somewhere in the system, making such a scheme
+     more expensive but no more secure than a file of cleartext passwords.
+     Asymmetric systems such as that used in UNIX allow one party to not know
+     the secrets, but they must be and are designed to be computationally
+     expensive when used in applications like the DCC that involve thousands
+     or more authentication checks per second.  Moreover, because of "dictio-
+     nary attacks," asymmetric systems are now little more secure than keeping
+     passwords in cleartext.  An adversary can compare the hash values of com-
+     binations of common words with /etc/passwd hash values to look for bad
+     passwords.  Worse, by the nature of a client/server protocol like that
+     used in the DCC, clients must have the cleartext password.  Since it is
+     among the more numerous and much less secure clients that adversaries
+     would seek files of DCC passwords, it would be a waste to complicate the
+     DCC server with an asymmetric system.
+
+     The DCC protocol is vulnerable to dictionary attacks to recover pass-
+     words.  An adversary could capture some DCC packets, and then check to
+     see if any of the 100,000 to 1,000,000 passwords in so called "cracker
+     dictionaries" applied to a packet generated the same signature.  This is
+     a concern only if DCC passwords are poorly chosen, such as any combina-
+     tion of words in an English dictionary.  There are ways to prevent this
+     vulnerability regardless of how badly passwords are chosen, but they are
+     computationally expensive and require additional network round trips.
+     Since DCC passwords are created and typed into files once and do not need
+     to be remembered by people, it is cheaper and quite easy to simply choose
+     good passwords that are not in dictionaries.
+
+   <A NAME="Reliability"><B>Reliability</B></A>
+     It is better to fail to filter unsolicited bulk mail than to fail to
+     deliver legitimate mail, so DCC clients fail in the direction of assuming
+     that mail is legitimate or even whitelisted.
+
+     A DCC client sends a report or other request and waits for an answer.  If
+     no answer arrives within a reasonable time, the client retransmits.
+     There are many things that might result in the client not receiving an
+     answer, but the most important is packet loss.  If the client's request
+     does not reach the server, it is easy and harmless for the client to
+     retransmit.  If the client's request reached the server but the server's
+     response was lost, a retransmission to the same server would be misunder-
+     stood as a new report of another copy of the same message unless it is
+     detected as a retransmission by the server.  The DCC protocol includes
+     transactions identifiers for this purpose.  If the client retransmitted
+     to a second server, the retransmission would be misunderstood by the sec-
+     ond server as a new report of the same message.
+
+     Each request from a client includes a timestamp to aid the client in mea-
+     suring the round trip time to the server and to let the client pick the
+     closest server.  Clients monitor the speed of all of the servers they
+     know including those they are not currently using, and use the quickest.
+
+   <A NAME="Client-and-Server-IDs"><B>Client and Server-IDs</B></A>
+     Servers and clients use numbers or IDs to identify themselves.  ID 1 is
+     reserved for anonymous, unauthenticated clients.  All other IDs are asso-
+     ciated with a pair of passwords in the <I>ids</I> file, the current and next or
+     previous and current passwords.  Clients included their client IDs in
+     their messages.  When they are not using the anonymous ID, they sign
+     their messages to servers with the first password associated with their
+     client-ID.  Servers treat messages with signatures that match neither of
+     the passwords for the client-ID in their own <I>ids</I> file as if the client
+     had used the anonymous ID.
+
+     Each server has a unique <I>server-ID</I> less than 32768.  Servers use their
+     IDs to identify checksums that they <I>flood</I> to other servers.  Each server
+     expects local clients sending administrative commands to use the server's
+     ID and sign administrative commands with the associated password.
+
+     Server-IDs must be unique among all systems that share reports by "flood-
+     ing."  All servers must be told of the IDs all other servers whose
+     reports can be received in the local <I>@prefix@/flod</I> file described in
+     <B><A HREF="dccd.html">dccd(8)</A></B>.  However, server-IDs can be mapped during flooding between inde-
+     pendent DCC organizations.
+
+     <I>Passwd-IDs</I> are server-IDs that should not be assigned to servers.  They
+     appear in the often publicly readable <I>@prefix@/flod</I> and specify passwords
+     in the private <I>@prefix@/ids</I> file for the inter-server flooding protocol
+
+     The client identified by a <I>client-ID</I> might be a single computer with a
+     single IP address, a single but multi-homed computer, or many computers.
+     Client-IDs are not used to identify checksum reports, but the organiza-
+     tion operating the client.  A client-ID need only be unique among clients
+     using a single server.  A single client can use different client-IDs for
+     different servers, each client-ID authenticated with a separate password.
+
+     An obscure but important part of all of this is that the inter-server
+     flooding algorithm depends on server-IDs and timestamps attached to
+     reports of checksums.  The inter-server flooding mechanism requires coop-
+     erating DCC servers to maintain reasonable clocks ticking in UTC.
+     Clients include timestamps in their requests, but as long as their time-
+     stamps are unlikely to be repeated, they need not be very accurate.
+
+   <A NAME="Installation-Considerations"><B>Installation Considerations</B></A>
+     DCC clients on a computer share information about which servers are cur-
+     rently working and their speeds in a shared memory segment.  This segment
+     also contains server host names, IP addresses, and the passwords needed
+     to authenticate known clients to servers.  That generally requires that
+     <B><A HREF="dccm.html">dccm(8)</A></B>, <B><A HREF="dccproc.html">dccproc(8)</A></B>, <B><A HREF="dccifd.html">dccifd(8)</A></B>, and <B><A HREF="cdcc.html">cdcc(8)</A></B> execute with an UID that can
+     write to the DCC home directory and its files.  The sendmail interface,
+     dccm, is a daemon that can be started by an "rc" or other script already
+     running with the correct UID.  The other two, dccproc and cdcc need to be
+     set-UID because they are used by end users.  They relinquish set-UID
+     privileges when not needed.
+
+     Files that contain cleartext passwords including the shared file used by
+     clients must be readable only by "owner."
+
+     The data files required by a DCC can be in a single "home" directory,
+     <I>@prefix@</I>.  Distinct DCC servers can run on a single computer, provided
+     they use distinct UDP port numbers and home directories.  It is possible
+     and convenient for the DCC clients using a server on the same computer to
+     use the same home directory as the server.
+
+     The DCC source distribution includes sample control files.  They should
+     be modified appropriately and then copied to the DCC home directory.
+     Files that contain cleartext passwords must not be publicly readable.
+
+     The DCC source includes "feature" m4 files to configure sendmail to use
+     <B><A HREF="dccm.html">dccm(8)</A></B> to check a DCC server about incoming mail.
+
+     See also the <A HREF="INSTALL.html">INSTALL.html</A> file.
+
+   <A NAME="Client-Installation"><B>Client Installation</B></A>
+     Installing a DCC client starts with obtaining or compiling program bina-
+     ries for the client server data control tool, <B><A HREF="cdcc.html">cdcc(8)</A></B>.  Installing the
+     sendmail DCC interface, <B><A HREF="dccm.html">dccm(8)</A></B>, or <B><A HREF="dccproc.html">dccproc(8)</A></B>, the general or
+     <B>procmail(1)</B> interface is the main part of the client installation.  Con-
+     necting the DCC to sendmail with dccm is most powerful, but requires
+     administrative control of the system running sendmail.
+
+     As noted above, cdcc and dccproc should be set-UID to a suitable UID.
+     Root or 0 is thought to be safe for both, because they are careful to
+     release privileges except when they need them to read or write files in
+     the DCC home directory.  A DCC home directory, <I>@prefix@</I> should be cre-
+     ated.  It must be owned and writable by the UID to which cdcc is set.
+
+     After the DCC client programs have been obtained, contact the operator(s)
+     of the chosen DCC server(s) to obtain each server's hostname, port num-
+     ber, and a <I>client-ID</I> and corresponding password.  No client-IDs or pass-
+     words are needed touse DCC servers that allow anonymous clients.  Use the
+     <I>load</I> or <I>add</I> commands of cdcc to create a <I>map</I> file in the DCC home direc-
+     tory.  It is usually necessary to create a client whitelist file of the
+     format described above.  To accommodate users sharing a computer but not
+     ideas about what is solicited bulk mail, the client whitelist file can be
+     any valid path name and need not be in the DCC home directory.
+
+     If dccm is chosen, arrange to start it with suitable arguments before
+     sendmail is started.  See the <I>homedir/dcc</I><B>_</B><I>conf</I> file and the <I>misc/rcDCC</I>
+     script in the DCC source.  The procmail DCCM interface, <B><A HREF="dccproc.html">dccproc(8)</A></B>, can
+     be run manually or by a <B>procmailrc(5)</B> rule.
+
+   <A NAME="Server-Installation"><B>Server Installation</B></A>
+     The DCC server, <B><A HREF="dccd.html">dccd(8)</A></B>, also requires that the DCC home directory exist.
+     It does not use the client shared or memory mapped file of server
+     addresses, but it requires other files.  One is the <I>@prefix@/ids</I> file of
+     client-IDs,  server-IDs, and corresponding passwords.  Another is a <I>flod</I>
+     file of peers that send and receive floods of reports of checksums with
+     large counts.  Both files are described in <B><A HREF="dccd.html">dccd(8)</A></B>.
+
+     The server daemon should be started when the system is rebooted, probably
+     before sendmail.  See the <I>misc/rcDCC</I> and <I>misc/start-dccd</I> files in the DCC
+     source.
+
+     The database should be cleaned regularly with <B><A HREF="dbclean.html">dbclean(8)</A></B> such as by run-
+     ning the crontab job that is in the misc directory.
+
+
+</PRE>
+<H2><A NAME="SEE-ALSO">SEE ALSO</A></H2><PRE>
+     <B><A HREF="cdcc.html">cdcc(8)</A></B>, <B><A HREF="dbclean.html">dbclean(8)</A></B>, <B><A HREF="dcc.html">dcc(8)</A></B>, <B><A HREF="dccd.html">dccd(8)</A></B>, <B><A HREF="dccifd.html">dccifd(8)</A></B>, <B><A HREF="dccm.html">dccm(8)</A></B>, <B><A HREF="dccproc.html">dccproc(8)</A></B>,
+     <B><A HREF="dblist.html">dblist(8)</A></B>, <B><A HREF="dccsight.html">dccsight(8)</A></B>, <B>sendmail(8)</B>.
+
+
+</PRE>
+<H2><A NAME="HISTORY">HISTORY</A></H2><PRE>
+     Distributed Checksum Clearinghouses are based on an idea of Paul Vixie
+     with code designed and written at Rhyolite Software starting in 2000.
+     This document describes version 1.3.103.
+
+                               February 26, 2009
+</PRE>
+<HR>
+<ADDRESS>
+Man(1) output converted with
+<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
+modified for the DCC $Date 2001/04/29 03:22:18 $
+<BR>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG SRC="http://logos.dcc-servers.net/border.png"
+            class=logo ALT="DCC logo">
+    </A>
+<A HREF="http://validator.w3.org/check?uri=referer">
+    <IMG class=logo ALT="Valid HTML 4.01 Strict"
+        SRC="http://www.w3.org/Icons/valid-html401">
+    </A>
+</ADDRESS>
+</BODY>
+</HTML>
diff -r 000000000000 -r c7f6b056b673 dcc.ide
Binary file dcc.ide has changed
diff -r 000000000000 -r c7f6b056b673 dccd.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd.0	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,439 @@
+dccd(8)               Distributed Checksum Clearinghouse               dccd(8)
+
+NNAAMMEE
+     ddccccdd -- Distributed Checksum Clearinghouse Daemon
+
+SSYYNNOOPPSSIISS
+     ddccccdd [--6644ddVVbbffFFQQ] --ii _s_e_r_v_e_r_-_I_D [--nn _b_r_a_n_d] [--hh _h_o_m_e_d_i_r] --II [_h_o_s_t_-_I_D][_,_u_s_e_r]
+          [--aa [_s_e_r_v_e_r_-_a_d_d_r][_,_s_e_r_v_e_r_-_p_o_r_t]] [--qq _q_s_i_z_e]
+          [--GG [_o_n_,][_w_e_a_k_-_b_o_d_y_,][_w_e_a_k_-_I_P_,][_e_m_b_a_r_g_o][_,_w_i_n_d_o_w][_,_w_h_i_t_e]]
+          [--WW [_r_a_t_e][_,_c_h_g][_,_d_b_s_i_z_e]] [--KK [_n_o_-]_t_y_p_e] [--TT _t_r_a_c_e_m_o_d_e]
+          [--uu _a_n_o_n_-_d_e_l_a_y[_*_i_n_f_l_a_t_e]] [--CC _d_b_c_l_e_a_n] [--LL _l_t_y_p_e_,_f_a_c_i_l_i_t_y_._l_e_v_e_l]
+          [--RR [_R_L___S_U_B],[_R_L___A_N_O_N],[_R_L___A_L_L___A_N_O_N],[_R_L___B_U_G_S]]
+
+DDEESSCCRRIIPPTTIIOONN
+     DDccccdd receives reports of checksums related to mail received by DCC
+     clients and queries about the total number of reports of particular
+     checksums.  A DCC server never receives mail, address, headers, or other
+     information from clients, but only cryptographically secure checksums of
+     such information.  A DCC server cannot determine the text or other infor-
+     mation that corresponds to the checksums it receives.  It only acts as a
+     clearinghouse of total counts of checksums computed by clients.
+
+     Each DCC server or close cluster of DCC servers is identified by a
+     numeric _s_e_r_v_e_r_-_I_D.  Each DCC client is identified by a _c_l_i_e_n_t_-_I_D, either
+     explicitly listed in the _i_d_s file or the special anonymous client-ID.
+     Many computers are expected to share a single _c_l_i_e_n_t_-_I_D.  A _s_e_r_v_e_r_-_I_D is
+     less than 32768 while a _c_l_i_e_n_t_-_I_D is between 32768 and 16777215.  DCC
+     server-IDs need be known only to DCC servers and the people running them.
+     The passwords associated with DCC server-IDs should be protected, because
+     DCC servers listen to commands authenticated with server-IDs and their
+     associated passwords.  Each client that does not use the anonymous ID
+     must know the client-ID and password used by each of its servers.  A sin-
+     gle client computer can use different passwords with different server
+     computers.  See the _i_d_s file.
+
+     A whitelist of known good (or bad) sources of email prevents legitimate
+     mailing lists from being seen as unsolicited bulk email by DCC clients.
+     The whitelist used by a DCC server is built into the database when old
+     entries are removed by dbclean(8).  Each DCC client has its own, local
+     whitelist, and in general, whitelists work better in DCC clients than
+     servers.
+
+     The effectiveness of a Distributed Checksum Clearinghouse increases as
+     the number of subscribers increases.  Flooding reports of checksums among
+     DCC servers increases the effective number of subscribers to each server.
+     Each ddccccdd daemon tries to maintain TCP/IP connections to the other
+     servers listed in the _f_l_o_d file, and send them reports containing check-
+     sums with total counts exceeding thresholds.  Changes in the _f_l_o_d file
+     are noticed automatically within minutes.
+
+     Controls on report flooding are specified in the _f_l_o_d file.  Each line
+     specifies a hostname and port number to which reports should be flooded,
+     a server-ID to identify and authenticate the output stream, a server-ID
+     to identify and authenticate an input stream from the same server, and
+     flags with each ID.  The ability to delete reports of checksums is handy,
+     but could be abused.  If _d_e_l is not present among the _i_n_-_o_p_t_s options for
+     the incoming ID, incoming delete requests are logged and then ignored.
+     Floods from DCC "brands" that count only mail to spam traps and whose
+     servers use the --QQ option to count extremely bulk mail should be marked
+     with _t_r_a_p_s.  They can be seen as counting millions of targets, so the
+     _t_r_a_p_s flag on their _f_l_o_d file entry changes their incoming flooded
+     reports counts to _m_a_n_y_.
+
+     DDccccdd automatically checks its _f_l_o_d and _i_d_s files periodically.  Cdcc(8)
+     has the commands nneeww iiddss and fflloooodd cchheecckk to tell ddccccdd to check those two
+     files immediately.  Both files are also checked for changes after the
+     SIGHUP signal.
+
+   OOPPTTIIOONNSS
+     The following options are available:
+
+     --66   enable IPv6.  The default is equivalent to --44.  See also the IPv4
+          and IPv6 options in the _f_l_o_d file description below and the _I_P_v_6 _o_n
+          cdcc(8) command.
+
+     --44   disable IPv6.  See also --66.
+
+     --dd   enables debugging output.  Additional --dd options increase the number
+          of messages.
+
+     --VV   displays the version of the DCC server daemon.
+
+     --bb   causes the server to not detach itself from the controlling tty or
+          put itself into the background.
+
+     --FF   uses write() instead of mmap() in some cases to modify the DCC data-
+          base.  It is the default on Solaris.
+
+     --ff   turns off --FF.
+
+     --QQ   causes the server to treat reports of checksums as queries except
+          from DCC clients marked trusted in the _i_d_s file with _r_p_t_-_o_k.  See --uu
+          to turn off access by anonymous or unauthenticated clients
+
+     --ii _s_e_r_v_e_r_-_I_D
+          specifies the ID of this DCC server.  Each server identifies itself
+          as responsible for checksums that it forwards to other servers.
+
+     --nn _b_r_a_n_d
+          is an arbitrary string of letters and numbers that identifies the
+          organization running the DCC server.  The brand is required, and
+          appears in the SMTP _X_-_D_C_C headers generated by the DCC.
+
+     --hh _h_o_m_e_d_i_r
+          overrides the default DCC home directory, _/_v_a_r_/_d_c_c.
+
+     --II [_h_o_s_t_-_I_D][_,_u_s_e_r]
+          changes the server's globally unique identity for flooding from the
+          default value consisting of the first 16 characters of the host
+          name.  or changes the UID and GID of the process _H_o_s_t_-_I_D is a string
+          of up to 16 characters that replaces the first 16 characters of the
+          system's hostname in assertions of the server-ID that are flooded to
+          peers.  _U_s_e_r must be valid user name.
+
+     --aa [_s_e_r_v_e_r_-_a_d_d_r][_,_s_e_r_v_e_r_-_p_o_r_t]
+          adds an hostname or IP address to the list of local IP addresses
+          that the server answers.  Multiple --aa options can be used to specify
+          a subset of the available network interfaces or to use more than one
+          port number.  The default without any --aa options is to listen on all
+          local IP addresses.  It can be useful to list some of the IP
+          addresses of multi-homed hosts to deal with firewalls.  By default
+          _s_e_r_v_e_r_-_p_o_r_t is 6277 for DCC servers and 6276 for Greylist servers.
+          It is the UDP port at which DCC requests are received and the TCP
+          port for incoming floods of reports.
+
+          If _s_e_r_v_e_r_-_a_d_d_r is absent and if the getifaddrs(8) function is sup-
+          ported, separate UDP sockets are bound to each configured network
+          interface so that each DCC clients receives replies from the IP
+          addresses to which corresponding request are sent.  If ddccccdd is
+          started before all network interfaces are turned on or there are
+          interfaces that are turned on and off or change their addresses such
+          as PPP interfaces, then the special string _@ should be used to tell
+          ddccccdd to bind to an IN_ADDRANY UDP socket.
+
+          Outgoing TCP connections to flood checksum reports to other DCC
+          servers used the IP address of a single --aa option, but only if there
+          is single option that is not localhost.  See also the _f_l_o_d file.
+
+     --qq _q_s_i_z_e
+          specifies the maximum size of the queue of requests from anonymous
+          or unauthenticated clients.  The default value is the maximum DCC
+          RTT in seconds times 200 or 1000.
+
+     --GG [_o_n_,][_w_e_a_k_-_b_o_d_y_,][_w_e_a_k_-_I_P_,][_e_m_b_a_r_g_o][_,_w_i_n_d_o_w][_,_w_h_i_t_e]
+          changes ddccccdd to a Greylist server for dccm(8) or dccifd(8).
+          Greylisting consists of temporarily rejecting or embargoing mail
+          from unfamiliar combinations of SMTP client IP address, SMTP enve-
+          lope sender, and SMTP envelope recipient.  If the SMTP client per-
+          sists for _e_m_b_a_r_g_o _s_e_c_o_n_d_s and so is probably not an open proxy,
+          worm-infected personal computer, or other transient source of spam,
+          the triple of _(_I_P _a_d_d_r_e_s_s_,_s_e_n_d_e_r_,_r_e_c_i_p_i_e_n_t_) is added to a database
+          similar to the usual DCC database.  If the SMTP client does not try
+          again after _e_m_b_a_r_g_o seconds and before _w_i_n_d_o_w seconds after the
+          first attempt, the triple is forgotten.  If the SMTP client persists
+          past the embargo, the triple is added to the database and becomes
+          familiar and the message is accepted.  Familiar triples are remem-
+          bered for _w_h_i_t_e seconds after the last accepted mail message.  The
+          triple is forgotten if it is ever associated with unsolicited bulk
+          email.
+
+          All three durations can be a number of minutes, hours, days, or
+          weeks followed by _M_I_N_U_T_E_S, _M, _H_O_U_R_S, _H, _D_A_Y_S, _D, _W_E_E_K_S or _W.  The
+          default is --GG _2_7_0_s_e_c_o_n_d_s_,_7_d_a_y_s_,_6_3_d_a_y_s.  The first duration or the
+          _e_m_b_a_r_g_o should be longer than open proxies can linger retransmit-
+          ting.  The second _w_i_n_d_o_w time should be as long as legitimate mail
+          servers persist in retransmitting to recognize embargoed messages
+          whose retransmissions were not received because of network or other
+          problems.  The _w_h_i_t_e time should be long enough to recognize and not
+          embargo messages from regular senders.
+
+          Usually the DCC greylist system requires that an almost identical
+          copy of the message be retransmitted during the _e_m_b_a_r_g_o.  If
+          _w_e_a_k_-_b_o_d_y is present, any message with the same triple of sender IP
+          address, sender mail address, and target mail address ends the
+          embargo, even if the body of the message differs.
+
+          If _w_e_a_k_-_I_P is present, all mail from an SMTP client at an IP address
+          is accept after any message from the same IP address has been
+          accepted.
+
+          Unlike DCC checksums, the contents of greylist databases are private
+          and do not benefit from broad sharing.  However, large installations
+          can use more two or more greylist servers flooding triples among
+          themselves.  Flooding among greylist servers is controlled by the
+          _g_r_e_y___f_l_o_d file.
+
+          All greylist cooperating or flooding greylist servers _m_u_s_t use the
+          same --GG values.
+
+          Clients of greylist servers cannot be anonymous and must have
+          client-IDs and passwords assigned in the _i_d_s file.  This implies
+          that cdcc commands directed to greylist servers must specify the
+          server-ID.
+
+          White- and blacklists are honored by the DCC clients.  whitelisted
+          messages are embargoed or checked with a greylist server.  The
+          greylist triples of blacklisted messages, messages whose DCC counts
+          make them spam, and other messages known to be spam are sent to a
+          greylist server to be removed from the greylist database and cause
+          an embargo on the next messages with those triples.
+
+          Messages whose checksums match greylist server whitelists are not
+          embargoed and the checksums of their triples are not added to the
+          greylist database.
+
+          The target counts of embargoed messages are reported to the DCC net-
+          work to improve the detection of bulk mail.
+
+     --WW [_r_a_t_e][_,_c_h_g][_,_d_b_s_i_z_e]
+          controls quick database cleaning.  If the database is larger than
+          _d_b_s_i_z_e, it seems that the database has not recently and is not about
+          to be cleaned, ddccccdd is receiving fewer than _r_a_t_e requests per sec-
+          ond, and if telling DCC clients that the database is about to be
+          cleaned reduces that rate by _c_h_g%, then ddccccdd starts dbclean(8) for a
+          quick database cleaning.  The cleaning is abandoned if it takes too
+          long.  The default values are equivalent to --WW _1_._0_,_4_0_._0_,_R_S_S where
+          _R_S_S is the maximum dccd resident set displayed the system log by --dd
+          when ssttaarrttss.
+
+     --KK [_n_o_-]_t_y_p_e
+          marks checksums of _t_y_p_e (not) be kept or counted in the database
+          unless they appear in the whitelist.  Explicit settings add to or
+          remove from the initial contents of the list, which is equivalent to
+          --KK _B_o_d_y --KK _F_u_z_1 --KK _F_u_z_2.
+
+     --TT _t_r_a_c_e_m_o_d_e
+          causes the server to trace or record some operations.  _t_r_a_c_e_m_o_d_e
+          must be one of the following:
+            _A_D_M_N    administrative requests from the control program, cdcc(8)
+            _A_N_O_N    errors by anonymous clients
+            _C_L_N_T    errors by authenticated clients
+            _R_L_I_M    rate-limited messages
+            _Q_U_E_R_Y   all queries and reports
+            _R_I_D_C    some messages concerning the report-ID cache that is used
+                    to detect duplicate reports from clients
+            _F_L_O_O_D   messages about inter-server flooding connections
+            _F_L_O_O_D_2  messages about flooded reports
+            _I_D_S     unknown server-IDs in flooded reports
+            _B_L      requests from clients in the _b_l_a_c_k_l_i_s_t file.
+            _D_B      odd database events including long chains of duplicate
+                    checksums
+            _W_L_I_S_T   reports of whitelisted checksums from authenticated, not
+                    anonymous DCC clients
+          The default is _A_N_O_N _C_L_N_T.
+
+     --uu _a_n_o_n_-_d_e_l_a_y[_*_i_n_f_l_a_t_e]
+          changes the number of milliseconds anonymous or unauthenticated
+          clients must wait for answers to their queries and reports.  The
+          purpose of this delay is to discourage large anonymous clients.  The
+          _a_n_o_n_-_d_e_l_a_y is multiplied by 1 plus the number of recent anonymous
+          requests from an IP address divided by the _i_n_f_l_a_t_e value.
+
+          The string _F_O_R_E_V_E_R turns off all anonymous or unauthenticated access
+          not only for checksum queries and reports but also cdcc(8) ssttaattss
+          requests.  A missing value for _i_n_f_l_a_t_e turns off inflation.
+
+          The default value is _5_0_,_n_o_n_e, except when --GG is used in which case
+          _F_O_R_E_V_E_R is assumed and required.
+
+     --CC _d_b_c_l_e_a_n
+          changes the default name or path of the program used to rebuild the
+          hash table when it becomes too full.  The default value is
+          _/_v_a_r_/_d_c_c_/_l_i_b_e_x_e_c_/_d_b_c_l_e_a_n in the _/_v_a_r_/_d_c_c_/_l_i_b_e_x_e_c directory.  The
+          value can include arguments as in _-_C _'_$_D_C_C___L_I_B_E_X_E_C_/_d_b_c_l_e_a_n _-_F_'.
+
+           Dbclean _s_h_o_u_l_d _n_o_t be run by ddccccdd except in emergencies such as
+          database corruption or hash table overflow.  Dbclean(8) should be
+          run daily with the /var/dcc/libexec/cron-dccd cron script
+
+     --LL _l_t_y_p_e_,_f_a_c_i_l_i_t_y_._l_e_v_e_l
+          specifies how messages should be logged.  _L_t_y_p_e must be _e_r_r_o_r, _i_n_f_o,
+          or _o_f_f to indicate which of the two types of messages are being con-
+          trolled or to turn off all syslog(3) messages from ddccccdd.  _L_e_v_e_l must
+          be a syslog(3) level among _E_M_E_R_G, _A_L_E_R_T, _C_R_I_T, _E_R_R, _W_A_R_N_I_N_G, _N_O_T_I_C_E,
+          _I_N_F_O, and _D_E_B_U_G.  _F_a_c_i_l_i_t_y must be among _A_U_T_H, _A_U_T_H_P_R_I_V, _C_R_O_N,
+          _D_A_E_M_O_N, _F_T_P, _K_E_R_N, _L_P_R, _M_A_I_L, _N_E_W_S, _U_S_E_R, _U_U_C_P, and _L_O_C_A_L_0 through
+          _L_O_C_A_L_7.  The default is equivalent to
+                --LL _i_n_f_o_,_M_A_I_L_._N_O_T_I_C_E --LL _e_r_r_o_r_,_M_A_I_L_._E_R_R
+
+     --RR [_R_L___S_U_B],[_R_L___A_N_O_N],[_R_L___A_L_L___A_N_O_N],[_R_L___B_U_G_S]
+          sets one or more of the four rate-limits.  _R_L___S_U_B limits the number
+          of DCC transactions per second from subscribers or DCC clients with
+          known client-IDs and passwords.  This limit applies to each IP
+          address independently.
+
+          _R_L___A_N_O_N limits the number of DCC transactions per second from anony-
+          mous DCC clients.  This limit applies to each IP address indepen-
+          dently.  It is better to use --uu than to change this value to exclude
+          anonymous clients.
+
+          _R_L___A_L_L___A_N_O_N limits the number of DCC transactions per second from
+          all anonymous DCC clients.  This limit applies to all anonymous
+          clients as a group, regardless of their IP addresses.
+
+          _R_L___B_U_G_S limits the number of complaints or error messages per second
+          for all anonymous DCC clients as a group as well as for each DCC
+          client by IP address.
+
+          The default is equivalent to --RR _4_0_0_,_5_0_,_6_0_0_,_0_._1
+
+FFIILLEESS
+     /var/dcc  is the DCC home directory containing data and control files.
+     dcc_db    is the database of mail checksums.
+     dcc_db.hash is the mail checksum database hash table.
+     grey_db   is the database of greylist checksums.
+     grey_db.hash is the greylist database hash table.
+     flod      contains lines controlling DCC flooding of the form:
+               _h_o_s_t[_,_r_p_o_r_t][_;_s_r_c[_,_l_p_o_r_t]] _r_e_m_-_I_D [_p_a_s_s_w_d_-_I_D [_o_-_o_p_t [_i_-_o_p_t]]]
+               where absent optional values are signaled with "-" and
+                _h_o_s_t is the IP address or name of a DCC server and _r_p_o_r_t is
+                    the name or number of the TCP port used by the remote
+                    server.
+                _s_r_c and _l_p_o_r_t are the IP address or host name and TCP port
+                    from which the outgoing flooding connection should come.
+                    Incoming flooding connections must arrive at an address
+                    and port specified with --aa.
+                _r_e_m_-_i_d is the server-ID of the remote DCC server.
+                _p_a_s_s_w_d_-_I_D is a server-ID that is not assigned to a server, but
+                    whose first password is used to sign checksum reports sent
+                    to the remote system.  Either of its passwords are
+                    required with incoming reports.  If it is absent or "-",
+                    outgoing floods are signed with the first password of the
+                    local server in the _i_d_s file and incoming floods must be
+                    signed with either password of the remote server-ID.
+                _i_-_o_p_t and _o_-_o_p_t are comma separated lists of
+                     _o_f_f turns off flooding to the remote or local system.
+                     _t_r_a_p_s indicates that the remote sending or local receiv-
+                         ing system has only spam traps.
+                     _n_o_-_d_e_l says checksum delete requests are refused by the
+                         remote or local server and so turns off sending or
+                         accepting delete requests, respectively.  By default,
+                         delete requests are sent to remote servers and
+                         accepted in incoming floods if and only if the peers
+                         are exchanging DCC reputations.
+                     _d_e_l says delete requests are accepted by the remote or
+                         local server.
+                     _n_o_-_l_o_g_-_d_e_l turns off logging of incoming requests to
+                         delete checksums.
+                     _p_a_s_s_i_v_e is used to tell a server outside a firewall to
+                         expect a peer inside to create both of the pair of
+                         input and output TCP connections used for flooding.
+                         The peer inside the firewall should use _S_O_C_K_S or _N_A_T
+                         on its _f_l_o_d file entry for this system.
+                     _S_O_C_K_S is used to tell a server inside a firewall that it
+                         should create both of the TCP connections used for
+                         flooding and that SOCKS protocol should be used.  The
+                         peer outside the firewall should use _p_a_s_s_i_v_e on its
+                         _f_l_o_d file entry for this system.
+                     _N_A_T differs from _S_O_C_K_S only by not using the SOCKS proto-
+                         col.
+                     _I_D_1_-_>_I_D_2 converts server-ID _I_D_1 in flooded reports to
+                         server-ID _I_D_2.  Either _I_D_1 or _I_D_2 may be the string
+                         `self' to specify the server's own ID.  _I_D_1 can be
+                         the string `all' to specify all server-IDs or a pair
+                         of server-IDs separated by a dash to specify an
+                         inclusive range.  _I_D_2 can be the string `ok' to send
+                         or receive reports without translation or the string
+                         `reject' to not send outgoing or refuse incoming
+                         reports.  Only the first matching conversion is
+                         applied.  For example, when `self->ok,all->reject' is
+                         applied to a locally generated report, the first con-
+                         version is applied and the second is ignored.
+                     _l_e_a_f_=_p_a_t_h_-_l_e_n does not send reports with paths longer
+                         than _p_a_t_h_-_l_e_n server-IDs.
+                     _I_P_v_4 overrides a --66 setting for this flooding peer.
+                     _I_P_v_6 overrides the default or an explicit --44 setting.
+                     _v_e_r_s specifies the version of the DCC flooding protocol
+                         used by the remote DCC server with a string such as
+                         `version2'.
+                     _t_r_a_c_e sends information about a single peer like the
+                         cdcc(8) command ttrraaccee FFLLOOOODD oonn does for all peers.
+                     _t_r_a_c_e_2 sends information about individual flooded reports
+                         like the cdcc(8) command ttrraaccee FFLLOOOODD22 oonn does for all
+                         peers.
+     grey_flod is the equivalent of _f_l_o_d used by ddccccdd when it is a greylist
+               server.
+     flod.map  is an automatically generated file in which ddccccdd records its
+               progress sending or flooding reports to DCC peers.
+     grey_flod.map is the equivalent of _f_l_o_d_._m_a_p _u_s_e_d _b_y ddccccdd when it is a
+               greylist server.
+     ids       contains the IDs and passwords known by the DCC server.  An _i_d_s
+               file that can be read by others cannot be used.  It contains
+               blank lines, comments starting with "#" and lines of the form:
+                     _i_d[_,_r_p_t_-_o_k][_,_d_e_l_a_y_=_m_s[_*_i_n_f_l_a_t_e]] _p_a_s_s_w_d_1 [_p_a_s_s_w_d_2]
+               where
+                _i_d  is a DCC _c_l_i_e_n_t_-_I_D or _s_e_r_v_e_r_-_I_D.
+                _R_p_t_-_o_k if present overrides --QQ by saying that this client is
+                    trusted to report only checksums for unsolicited bulk
+                    mail.
+                _d_e_l_a_y_=_m_s[_*_i_n_f_l_a_t_e] delays answers to systems using the client
+                    _i_d.  The _d_e_l_a_y in milliseconds is multiplied by 1 plus the
+                    number of recent requests from an IP address using _i_d
+                    divided by the _i_n_f_l_a_t_e value.  See --uu.
+                _p_a_s_s_w_d_1 is the password currently used by clients with identi-
+                    fier _i_d.  It is a 1 to 32 character string that does not
+                    contain blank, tab, newline or carriage return characters.
+                _p_a_s_s_w_d_2 is the optional next password that those clients will
+                    use.  A DCC server accepts either password if both are
+                    present in the file.
+               Both passwords can be absent if the entry not used except to
+               tell ddccccdd that server-IDs in the flooded reports are valid.
+               The string _u_n_k_n_o_w_n is equivalent to the null string.
+     whitelist contains the DCC server whitelist.  It is not used directly but
+               is loaded into the database when dbclean(8) is run.
+     grey_whitelist contains the greylist server whitelist.  It is not used
+               directly but is loaded into the database when dbclean(8) is run
+               with --GG.
+     blacklist if present, contains a list of IP addresses and blocks of IP
+               addresses DCC clients that are ignored.  Each line in the file
+               should be blank, a comment starting with '#', or an IP address
+               or block of IP addresses in the form
+                     [_t_r_a_c_e_,] [_o_k_,] [_b_a_d] xxx.xxx.xxx.xxx[/yy]
+               Changes to the file are automatically noticed and acted upon
+               within a few minutes.  Addresses or blocks of addresses can be
+               preceded with _o_k to "punch holes" in blacklisted blocks or with
+               _t_r_a_c_e to log activity.  This mechanism is intended for no more
+               than a few dozen blocks of addresses.
+     dccd_clients contains client IP addresses and activity counts.
+     grey_clients contains greylist client IP addresses and activity counts.
+
+EEXXAAMMPPLLEESS
+     ddccccdd is usually started with other system daemons with something like the
+     script _/_v_a_r_/_d_c_c_/_l_i_b_e_x_e_c_/_r_c_D_C_C.  That scripts uses values in
+     /var/dcc/dcc_conf to start the server.  With the argument _s_t_o_p,
+     _/_v_a_r_/_d_c_c_/_l_i_b_e_x_e_c_/_r_c_D_C_C can be used to stop the daemon.
+
+     The database grows too large unless old reports are removed.  dbclean(8)
+     should be run daily with the /var/dcc/libexec/cron-dccd cron script
+
+SSEEEE AALLSSOO
+     cdcc(8), dcc(8), dbclean(8), dblist(8), dccifd(8), dccm(8), dccproc(8).
+     dccsight(8),
+
+HHIISSTTOORRYY
+     ddccccdd is based on an idea from Paul Vixie.  It was designed and written at
+     Rhyolite Software, starting in 2000.  This document describes version
+     1.3.103.
+
+                               February 26, 2009
diff -r 000000000000 -r c7f6b056b673 dccd.8.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd.8.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,961 @@
+.\" Copyright (c) 2008 by Rhyolite Software, LLC
+.\"
+.\" This agreement is not applicable to any entity which sells anti-spam
+.\" solutions to others or provides an anti-spam solution as part of a
+.\" security solution sold to other entities, or to a private network
+.\" which employs the DCC or uses data provided by operation of the DCC
+.\" but does not provide corresponding data to other users.
+.\"
+.\" Permission to use, copy, modify, and distribute this software without
+.\" changes for any purpose with or without fee is hereby granted, provided
+.\" that the above copyright notice and this permission notice appear in all
+.\" copies and any distributed versions or copies are either unchanged
+.\" or not called anything similar to "DCC" or "Distributed Checksum
+.\" Clearinghouse".
+.\"
+.\" Parties not eligible to receive a license under this agreement can
+.\" obtain a commercial license to use DCC by contacting Rhyolite Software
+.\" at sales@rhyolite.com.
+.\"
+.\" A commercial license would be for Distributed Checksum and Reputation
+.\" Clearinghouse software.  That software includes additional features.  This
+.\" free license for Distributed ChecksumClearinghouse Software does not in any
+.\" way grant permision to use Distributed Checksum and Reputation Clearinghouse
+.\" software
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+.\" 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.
+.\"
+.\" Rhyolite Software DCC 1.3.103-1.143 $Revision$
+.\"
+.Dd February 26, 2009
+.ds volume-ds-DCC Distributed Checksum Clearinghouse
+.Dt dccd 8 DCC
+.Os " "
+.Sh NAME
+.Nm dccd
+.Nd Distributed Checksum Clearinghouse Daemon
+.Sh SYNOPSIS
+.Bk -words
+.Nm dccd
+.Op Fl 64dVbfFQ
+.Fl i Ar server-ID
+.Op Fl n Ar brand
+.Op Fl h Ar homedir
+.Fl I Xo
+.Sm off
+.Op Ar host-ID
+.Op Ar ,user
+.Sm on
+.Xc
+.br
+.Oo
+.Fl a Xo
+.Sm off
+.Op Ar server-addr
+.Op Ar ,server-port
+.Xc
+.Sm on
+.Oc
+.Op Fl q Ar qsize
+.br
+.Oo
+.Fl G Xo
+.Sm off
+.Op Ar on,
+.Op Ar weak-body,
+.Op Ar weak-IP,
+.Op Ar embargo
+.Op Ar ,window
+.Op Ar ,white
+.Xc
+.Sm on
+.Oc
+.br
+.Oo
+.Fl W Xo
+.Sm off
+.Op Ar rate
+.Op Ar ,chg
+.Op Ar ,dbsize
+.Sm on
+.Xc
+.Oc
+.Oo
+.Fl K Xo
+.Sm off
+.Op Ar no-
+.Ar type
+.Sm on
+.Xc
+.Oc
+.Op Fl T Ar tracemode
+.Op Fl u Ar anon-delay Ns Op Ar *inflate
+.Op Fl C Ar dbclean
+.Op Fl L Ar ltype,facility.level
+.br
+.Oo
+.Fl R Xo
+.Sm off
+.Op Ar RL_SUB ,
+.Op Ar RL_ANON ,
+.Op Ar RL_ALL_ANON ,
+.Op Ar RL_BUGS
+.Xc
+.Sm on
+.Oc
+.Ek
+.Sh DESCRIPTION
+.Nm Dccd
+receives reports of checksums related to mail received by DCC clients
+and queries about the total number of reports of particular checksums.
+A DCC server never receives
+mail, address, headers, or other information from clients, but only
+cryptographically secure checksums of such information.
+A DCC server cannot determine the text or other information that corresponds
+to the checksums it receives.
+It only acts as a clearinghouse of total counts of checksums
+computed by clients.
+.Pp
+Each DCC server or close cluster of DCC servers is identified by a numeric
+.Ar server-ID .
+Each DCC client is identified by a
+.Ar client-ID ,
+either explicitly listed in the
+.Pa ids
+file or
+the special anonymous client-ID.
+Many computers are expected to share a single
+.Ar client-ID .
+A
+.Ar server-ID
+is less than 32768 while a
+.Ar client-ID
+is between 32768 and 16777215.
+DCC server-IDs need be known only to DCC servers and the people running
+them.
+The passwords associated with DCC server-IDs should be protected,
+because DCC servers listen to commands authenticated with server-IDs
+and their associated passwords.
+Each client that does not use the anonymous ID must know the client-ID
+and password used by each of its servers.
+A single client computer can use different passwords with different
+server computers.
+See the
+.Pa ids
+file.
+.Pp
+A whitelist of known good (or bad) sources of email prevents
+legitimate mailing lists from being seen as unsolicited bulk email
+by DCC clients.
+The whitelist used by a DCC server is built into
+the database when old entries are removed by
+.Xr dbclean 8 .
+Each DCC client has its own, local whitelist, and in general,
+whitelists work better in DCC clients than servers.
+.Pp
+The effectiveness of a Distributed Checksum Clearinghouse
+increases as the number of subscribers increases.
+Flooding reports of checksums among DCC servers increases
+the effective number of subscribers to each server.
+Each
+.Nm
+daemon tries to maintain TCP/IP connections to the other servers
+listed in the
+.Pa flod
+file, and send them reports containing checksums with total
+counts exceeding thresholds.
+Changes in the
+.Pa flod
+file are noticed automatically within minutes.
+.Pp
+Controls on report flooding are specified in the
+.Pa flod
+file.
+Each line specifies a hostname and port number to which reports
+should be flooded,
+a server-ID to identify and authenticate the output stream,
+a server-ID to identify and authenticate an input stream from the
+same server,
+and flags with each ID.
+The ability to delete reports of checksums is handy, but could
+be abused.
+If
+.Ar del
+is not present among the
+.Ar in-opts
+options for the incoming ID,
+incoming delete requests are logged and then ignored.
+Floods from DCC "brands" that count only mail to
+spam traps and whose servers use the
+.Fl Q
+option to count extremely bulk mail
+should be marked with
+.Ar traps .
+They can be seen as counting millions of targets, so the
+.Ar traps
+flag on their
+.Pa flod
+file entry changes their incoming flooded reports counts to
+.Em many.
+.Pp
+.Nm Dccd
+automatically checks its
+.Pa flod
+and
+.Pa ids
+files periodically.
+.Xr Cdcc 8
+has the commands
+.Ic new ids
+and
+.Ic flood check
+to tell
+.Nm
+to check those two files immediately.
+Both files are also checked for changes after the SIGHUP signal.
+.Ss OPTIONS
+The following options are available:
+.Bl -tag -width 3n
+.It Fl 6
+enable IPv6.
+The default is equivalent to
+.Fl 4 .
+See also the IPv4 and IPv6 options in the
+.Pa flod
+file description below and the
+.Em IPv6 on
+.Xr cdcc 8
+command.
+.It Fl 4
+disable IPv6.
+See also
+.Fl 6 .
+.It Fl d
+enables debugging output.
+Additional
+.Fl d
+options increase the number of messages.
+.It Fl V
+displays the version of the DCC server daemon.
+.It Fl b
+causes the server to not detach itself from the controlling tty
+or put itself into the background.
+.It Fl F
+uses write() instead of mmap() in some cases to modify the DCC database.
+It is the default on Solaris.
+.It Fl f
+turns off
+.Fl F .
+.It Fl Q
+causes the server to treat reports of checksums as queries
+except from DCC clients marked trusted in the
+.Pa ids
+file with
+.Ar rpt-ok .
+See
+.Fl u
+to turn off access by anonymous or unauthenticated clients
+.It Fl i Ar server-ID
+specifies the ID of this DCC server.
+Each server identifies itself as responsible for checksums
+that it forwards to other servers.
+.It Fl n Ar brand
+is an arbitrary string of letters and numbers that
+identifies the organization running the DCC server.
+The brand is required, and appears in the SMTP
+.Em X-DCC
+headers generated by the DCC.
+.It Fl h Ar homedir
+overrides the default DCC home directory,
+.Pa @prefix@ .
+.It Fl I Xo
+.Sm off
+.Op Ar host-ID
+.Op Ar ,user
+.Sm on
+.Xc
+changes the server's globally unique identity for flooding 
+from the default value
+consisting of the first 16 characters of the host name.
+or changes the UID and GID of the process
+.Ar Host-ID
+is a string of up to 16 characters that replaces the first
+16 characters of the system's hostname in assertions 
+of the server-ID that are flooded to peers.
+.Ar User
+must be valid user name.
+.It Fl a Xo
+.Sm off
+.Op Ar server-addr
+.Op Ar ,server-port
+.Sm on
+.Xc
+adds an hostname or IP address to the list of local IP addresses
+that the server answers.
+Multiple
+.Fl a
+options can be used to specify a subset of the available network
+interfaces or to use more than one port number.
+The default without any
+.Fl a
+options is to listen on all local IP addresses.
+It can be useful to list some of the IP addresses of
+multi-homed hosts to deal with firewalls.
+By default
+.Ar server-port
+is 6277 for DCC servers and 6276 for Greylist servers.
+It is the UDP port at which DCC
+requests are received and the TCP port for incoming floods of reports.
+.Pp
+If
+.Ar server-addr
+is absent
+and if the
+.Xr getifaddrs 8
+function is supported,
+separate UDP sockets are bound to each configured network interface so
+that each DCC clients receives replies from the
+IP addresses to which corresponding request are sent.
+If
+.Nm
+is started before all network interfaces are turned on or
+there are interfaces that are turned on and off or change their addresses
+such as PPP interfaces,
+then the special string
+.Ar @
+should be used to tell
+.Nm
+to bind to an IN_ADDRANY UDP socket.
+.Pp
+Outgoing TCP connections to flood checksum reports to other DCC servers
+used the IP address of a single
+.Fl a
+option,
+but only if there is single option that is not localhost.
+See also the
+.Pa flod
+file.
+.It Fl q Ar qsize
+specifies the maximum size of the queue of requests from anonymous or
+unauthenticated clients.
+The default value is the maximum DCC RTT in seconds times 200 or 1000.
+.It Fl G Xo
+.Sm off
+.Op Ar on,
+.Op Ar weak-body,
+.Op Ar weak-IP,
+.Op Ar embargo
+.Op Ar ,window
+.Op Ar ,white
+.Xc
+.Sm on
+changes
+.Nm
+to a Greylist server for
+.Xr dccm 8
+or
+.Xr dccifd 8 .
+Greylisting consists of temporarily rejecting or embargoing mail from
+unfamiliar combinations of SMTP client IP address, SMTP envelope sender,
+and SMTP envelope recipient.
+If the SMTP client persists for
+.Ar embargo seconds
+and so is probably not an open proxy, worm-infected personal computer,
+or other transient source of spam, the triple of
+.Em (IP\ address,sender,recipient)
+is added to a database similar to the usual DCC database.
+If the SMTP client does not try again after
+.Ar embargo
+seconds and before
+.Ar window
+seconds after the first attempt,
+the triple is forgotten.
+If the SMTP client persists past the embargo,
+the triple is added to the database and becomes familiar
+and the message is accepted.
+Familiar triples are remembered for
+.Ar white
+seconds after the last accepted mail message.
+The triple is forgotten if it is ever associated with unsolicited bulk email.
+.Pp
+All three durations can be a number of minutes, hours, days, or
+weeks followed by
+.Ar MINUTES ,
+.Ar M ,
+.Ar HOURS ,
+.Ar H ,
+.Ar DAYS ,
+.Ar D ,
+.Ar WEEKS
+or
+.Ar W .
+The default is
+.Fl G Ar 270seconds,7days,63days .
+The first duration or the
+.Ar embargo
+should be longer than open proxies can linger retransmitting.
+The second
+.Ar window
+time should be as long as legitimate mail servers persist in retransmitting
+to recognize embargoed messages whose retransmissions were not
+received because of network or other problems.
+The
+.Ar white
+time should be long enough to recognize and not embargo messages from
+regular senders.
+.Pp
+Usually the DCC greylist system requires that an almost
+identical copy of the message be retransmitted during the
+.Ar embargo .
+If
+.Ar weak-body
+is present,
+any message with the same triple of sender IP address, sender mail
+address, and target mail address ends the embargo,
+even if the body of the message differs.
+.Pp
+If
+.Ar weak-IP
+is present,
+all mail from an SMTP client at an IP address is accept
+after any message from the same IP address has been accepted.
+.Pp
+Unlike DCC checksums, the contents of
+greylist databases are private and do not benefit from broad sharing.
+However, large installations can use more two or more greylist servers
+flooding triples among themselves.
+Flooding among greylist servers is controlled by the
+.Pa grey_flod
+file.
+.Pp
+All greylist cooperating or flooding greylist servers
+.Em must
+use the same
+.Fl G
+values.
+.Pp
+Clients of greylist servers cannot be anonymous and must have
+client-IDs and passwords assigned in the
+.Pa ids
+file.
+This implies that
+.Xr cdcc
+commands directed to greylist servers must specify the server-ID.
+.Pp
+White- and blacklists are honored by the DCC clients.
+whitelisted messages are embargoed or checked with a greylist server.
+The greylist triples of blacklisted messages, messages whose DCC counts make
+them spam, and other messages known to be spam are sent to a greylist
+server to be removed from the greylist database and cause an embargo
+on the next messages with those triples.
+.Pp
+Messages whose checksums match greylist server whitelists
+are not embargoed and the checksums of their triples are not
+added to the greylist database.
+.Pp
+The target counts of embargoed messages are reported to the DCC network
+to improve the detection of bulk mail.
+.It Fl W Xo
+.Sm off
+.Op Ar rate
+.Op Ar ,chg
+.Op Ar ,dbsize
+.Sm on
+.Xc
+controls quick database cleaning.
+If the database is larger than
+.Ar dbsize ,
+it seems that the database has not recently and is not about to be cleaned,
+.Nm
+is receiving fewer than
+.Ar rate
+requests per second,
+and if telling DCC clients that the database is about to be cleaned
+reduces that rate by
+.Ar chg Ns %,
+then
+.Nm
+starts
+.Xr dbclean 8
+for a quick database cleaning.
+The cleaning is abandoned if it takes too long.
+The default values are equivalent to
+.Bk -words
+.Fl W Ar 1.0,40.0,RSS
+where
+.Ar RSS
+is the maximum dccd resident set
+displayed the system log by
+.Fl d
+when
+.Nm starts .
+.Ek
+.It Fl K Xo
+.Sm off
+.Op Ar no-
+.Ar type
+.Sm on
+.Xc
+marks checksums of
+.Ar type
+(not) be kept
+or counted in the database unless they appear in the whitelist.
+Explicit settings add to or remove from the initial contents of the list,
+which is equivalent to
+.Fl K Ar Body
+.Fl K Ar Fuz1
+.Fl K Ar Fuz2 .
+.It Fl T Ar tracemode
+causes the server to trace or record some operations.
+.Ar tracemode
+must be one of the following:
+.Bl -tag -width FLOOD2 -offset 2n -compact
+.It Ar ADMN
+administrative requests from the control program,
+.Xr cdcc 8
+.It Ar ANON
+errors by anonymous clients
+.It Ar CLNT
+errors by authenticated clients
+.It Ar RLIM
+rate-limited messages
+.It Ar QUERY
+all queries and reports
+.It Ar RIDC
+some messages concerning the report-ID cache that is used
+to detect duplicate reports from clients
+.It Ar FLOOD
+messages about inter-server flooding connections
+.It Ar FLOOD2
+messages about flooded reports
+.It Ar IDS
+unknown server-IDs in flooded reports
+.It Ar BL
+requests from clients in the
+.Pa blacklist
+file.
+.It Ar DB
+odd database events including long chains of duplicate checksums
+.It Ar WLIST
+reports of whitelisted checksums from authenticated, not anonymous DCC clients
+.El
+The default is
+.Ar ANON CLNT .
+.It Fl u Ar anon-delay Ns Op Ar *inflate
+changes the number of milliseconds anonymous or unauthenticated clients
+must wait for answers to their queries and reports.
+The purpose of this delay is to discourage large anonymous clients.
+The
+.Ar anon-delay
+is multiplied by 1 plus the number of recent anonymous requests from
+an IP address divided by the
+.Ar inflate
+value.
+.Pp
+The string
+.Ar FOREVER
+turns off all anonymous or unauthenticated access not only
+for checksum queries and reports but also
+.Xr cdcc 8
+.Ic stats
+requests.
+A missing value for
+.Ar inflate
+turns off inflation.
+.Pp
+The default value is
+.Ar 50,none ,
+except when
+.Fl G
+is used in which case
+.Ar FOREVER
+is assumed and required.
+.It Fl C Ar dbclean
+changes the default name or path of the program used to rebuild
+the hash table when it becomes too full.
+The default value is
+.Pa @libexecdir@/dbclean
+in the
+.Pa @libexecdir@
+directory.
+The value can include arguments as in
+.Ar -C '$DCC_LIBEXEC/dbclean -F' .
+.Pp
+ Dbclean
+.Em should not
+be run by
+.Nm
+except in emergencies such as database corruption or hash table overflow.
+.Xr Dbclean 8
+should be run daily with the @libexecdir@/cron-dccd cron script
+.It Fl L Ar ltype,facility.level
+specifies how messages should be logged.
+.Ar Ltype
+must be
+.Ar error ,
+.Ar info ,
+or
+.Ar off
+to indicate which of the two types of messages are being controlled or
+to turn off all
+.Xr syslog 3
+messages from
+.Nm .
+.Ar Level
+must be a
+.Xr syslog 3
+level among
+.Ar EMERG ,
+.Ar ALERT ,
+.Ar CRIT , ERR ,
+.Ar WARNING ,
+.Ar NOTICE ,
+.Ar INFO ,
+and
+.Ar DEBUG .
+.Ar Facility
+must be among
+.Ar AUTH ,
+.Ar AUTHPRIV ,
+.Ar CRON ,
+.Ar DAEMON ,
+.Ar FTP ,
+.Ar KERN ,
+.Ar LPR ,
+.Ar MAIL ,
+.Ar NEWS ,
+.Ar USER ,
+.Ar UUCP ,
+and
+.Ar LOCAL0
+through
+.Ar LOCAL7 .
+The default is equivalent to
+.Dl Fl L Ar info,MAIL.NOTICE  Fl L Ar error,MAIL.ERR
+.It Fl R Xo
+.Sm off
+.Op Ar RL_SUB ,
+.Op Ar RL_ANON ,
+.Op Ar RL_ALL_ANON ,
+.Op Ar RL_BUGS
+.Xc
+.Sm on
+sets one or more of the four rate-limits.
+.Ar RL_SUB
+limits the number of DCC transactions per second from subscribers
+or DCC clients with known client-IDs and passwords.
+This limit applies to each IP address independently.
+.Pp
+.Ar RL_ANON
+limits the number of DCC transactions per second from anonymous DCC clients.
+This limit applies to each IP address independently.
+It is better to use
+.Fl u
+than to change this value to exclude anonymous clients.
+.Pp
+.Ar RL_ALL_ANON
+limits the number of DCC transactions per second from all anonymous DCC clients.
+This limit applies to all anonymous clients as a group, regardless of their
+IP addresses.
+.Pp
+.Ar RL_BUGS
+limits the number of complaints or error messages per second for all
+anonymous DCC clients as a group as well as for each DCC client by IP
+address.
+.Pp
+The default is equivalent to
+.Fl R Ar 400,50,600,0.1
+.El
+.Sh FILES
+.Bl -hang -width @prefix@ -compact
+.It Pa @prefix@
+is the DCC home directory containing data and control files.
+.It Pa dcc_db
+is the database of mail checksums.
+.It Pa dcc_db.hash
+is the mail checksum database hash table.
+.It Pa grey_db
+is the database of greylist checksums.
+.It Pa grey_db.hash
+is the greylist database hash table.
+.It Pa flod
+contains lines controlling DCC flooding of the form:
+.br
+.Bd -ragged -compact
+.Ar host Ns Xo
+.Sm off
+.Op Ar ,rport
+.Op Ar ;src Op Ar ,lport
+.Sm on
+.Xc
+.Ar rem-ID
+.Op Ar passwd-ID Op Ar o-opt Op Ar i-opt
+.Ed
+where absent optional values are signaled with "-" and
+.Bl -hang -offset 1n -width 2n -compact
+.It Ar host
+is the IP address or name of a DCC server and
+.Ar rport
+is the name or number of the TCP port used by the remote server.
+.It Ar src
+and
+.Ar lport
+are the IP address or host name and TCP port
+from which the outgoing flooding connection should come.
+Incoming flooding connections must arrive at an address and port
+specified with
+.Fl a .
+.It Ar rem-id
+is the server-ID of the remote DCC server.
+.It Ar passwd-ID
+is a server-ID that is not assigned to a server,
+but whose first password is used to sign
+checksum reports sent to the remote system.
+Either of its passwords are required with incoming reports.
+If it is absent or "-", outgoing floods are signed with the first
+password of the local server in the
+.Pa ids
+file and incoming floods must be signed with either password of
+the remote server-ID.
+.It Ar i-opt Li and Ar o-opt
+are comma separated lists of
+.Bl -hang -offset 1n -width 2n -compact
+.It Ar off
+turns off flooding to the remote or local system.
+.It Ar traps
+indicates that
+the remote sending or local receiving system has only spam traps.
+.It Ar no-del
+says checksum delete requests are refused by the remote or local server
+and so turns off sending or accepting delete requests, respectively.
+By default, delete requests are sent to remote servers and accepted
+in incoming floods if and only if the peers are exchanging DCC reputations.
+.It Ar del
+says delete requests are accepted by the remote or local server.
+.It Ar no-log-del
+turns off logging of incoming requests to delete checksums.
+.It Ar passive
+is used to tell a server outside a firewall to expect a peer
+inside to create both of the pair
+of input and output TCP connections used for flooding.
+The peer inside the firewall should use
+.Ar SOCKS
+or
+.Ar NAT
+on its
+.Pa flod
+file entry for this system.
+.It Ar SOCKS
+is used to tell a server inside a firewall that it should create both
+of the TCP connections used for flooding and that SOCKS protocol should
+be used.
+The peer outside the firewall should use
+.Ar passive
+on its
+.Pa flod
+file entry for this system.
+.It Ar NAT
+differs from
+.Ar SOCKS
+only by not using the SOCKS protocol.
+.It Ar ID1->ID2
+converts server-ID
+.Ar ID1
+in flooded reports to server-ID
+.Ar ID2 .
+Either
+.Ar ID1
+or
+.Ar ID2
+may be the string
+.Sq self
+to specify the server's own ID.
+.Ar ID1
+can be the string
+.Sq all
+to specify all server-IDs
+or a pair of server-IDs separated by a dash to specify an inclusive range.
+.Ar ID2
+can be the string
+.Sq ok
+to send or receive reports without translation
+or the string
+.Sq reject
+to not send outgoing or refuse incoming reports.
+Only the first matching conversion is applied.
+For example, when
+.Sq self->ok,all->reject
+is applied to a locally generated report,
+the first conversion is applied and the second is ignored.
+.It Ar leaf=path-len
+does not send reports with paths longer than
+.Ar path-len
+server-IDs.
+.It Ar IPv4
+overrides a
+.Fl 6
+setting for this flooding peer.
+.It Ar IPv6
+overrides the
+default or an explicit
+.Fl 4
+setting.
+.It Ar vers
+specifies the version of the DCC flooding protocol used by the remote
+DCC server with a string such as
+.Sq version2 .
+.It Ar trace
+sends information about a single peer like the
+.Xr cdcc 8
+command
+.Ic trace FLOOD on
+does for all peers.
+.It Ar trace2
+sends information about individual flooded reports like the
+.Xr cdcc 8
+command
+.Ic trace FLOOD2 on
+does for all peers.
+.El
+.El
+.It Pa grey_flod
+is the equivalent of
+.Pa flod
+used by
+.Nm
+when it is a greylist server.
+.It Pa flod.map
+is an automatically generated file in which
+.Nm
+records its progress sending or flooding reports to DCC peers.
+.It Pa grey_flod.map
+is the equivalent of
+.Pa flod.map used by
+.Nm
+when it is a greylist server.
+.It Pa ids
+contains the IDs and passwords known by the DCC server.
+An
+.Pa ids
+file that can be read by others cannot be used.
+It contains blank lines, comments starting
+with "#" and lines of the form:
+.Bd -ragged -compact -offset indent
+.Sm off
+.Ar id
+.Op Ar ,rpt-ok
+.Op Ar ,delay=ms  Ns Op Ar *inflate
+.Sm on
+.Ar passwd1 Op Ar passwd2
+.Ed
+where
+.Bl -hang -offset 1n -width 2n -compact
+.It Ar id
+is a DCC
+.Ar client-ID
+or
+.Ar server-ID .
+.It Ar Rpt-ok
+if present overrides
+.Fl Q
+by saying that this client is trusted
+to report only checksums for unsolicited bulk mail.
+.It Ar delay=ms  Ns Op Ar *inflate
+delays answers to systems using the client
+.Ar id .
+The
+.Ar delay
+in milliseconds is multiplied by 1 plus the number of recent requests from
+an IP address using
+.Ar id
+divided by the
+.Ar inflate
+value.
+See
+.Fl u .
+.It Ar passwd1
+is the password currently used by clients with identifier
+.Ar id .
+It is a 1 to 32 character string that does not contain
+blank, tab, newline or carriage return characters.
+.It Ar passwd2
+is the optional next password that those clients will use.
+A DCC server accepts either password if both are present in the file.
+.El
+Both passwords can be absent if the entry not used except to tell
+.Nm
+that server-IDs in the flooded reports are valid.
+The string
+.Em unknown
+is equivalent to the null string.
+.It Pa whitelist
+contains the DCC server whitelist.
+It is not used directly but is loaded into the database when
+.Xr dbclean 8
+is run.
+.It Pa grey_whitelist
+contains the greylist server whitelist.
+It is not used directly but is loaded into the database when
+.Xr dbclean 8
+is run with
+.Fl G .
+.It Pa blacklist
+if present, contains a list of IP addresses and blocks of IP addresses
+DCC clients that are ignored.
+Each line in the file should be blank, a comment starting with '#',
+or an IP address or block of IP addresses in the form
+.Bd -ragged -compact -offset indent
+.Op Ar trace,
+.Op Ar ok,
+.Op Ar bad
+.No xxx.xxx.xxx.xxx Ns Op /yy
+.Ed
+Changes to the file are automatically noticed and acted upon within
+a few minutes.
+Addresses or blocks of addresses can be preceded with
+.Em ok
+to "punch holes"
+in blacklisted blocks or with
+.Em trace
+to log activity.
+This mechanism is intended for no more than a few dozen blocks of addresses.
+.It Pa dccd_clients
+contains client IP addresses and activity counts.
+.It Pa grey_clients
+contains greylist client IP addresses and activity counts.
+.El
+.Sh EXAMPLES
+.Nm
+is usually started with other system daemons with something like the
+script
+.Pa @libexecdir@/rcDCC .
+That scripts uses values in @prefix@/dcc_conf to start the server.
+With the argument
+.Em stop ,
+.Pa @libexecdir@/rcDCC
+can be used to stop the daemon.
+.Pp
+The database grows too large unless old reports are removed.
+.Xr dbclean 8
+should be run daily with the @libexecdir@/cron-dccd cron script
+.Sh SEE ALSO
+.Xr cdcc 8 ,
+.Xr dcc 8 ,
+.Xr dbclean 8 ,
+.Xr dblist 8 ,
+.Xr dccifd 8 ,
+.Xr dccm 8 ,
+.Xr dccproc 8 .
+.Xr dccsight 8 ,
+.Sh HISTORY
+.Nm
+is based on an idea from Paul Vixie.
+It was designed and written at Rhyolite Software, starting in 2000.
+This document describes version 1.3.103.
diff -r 000000000000 -r c7f6b056b673 dccd.html.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd.html.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,487 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<HTML>
+<HEAD>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <TITLE>dccd.0.8</TITLE>
+    <META http-equiv="Content-Style-Type" content="text/css">
+    <STYLE type="text/css">
+	BODY {background-color:white; color:black}
+	ADDRESS {font-size:smaller}
+        IMG.logo {width:6em; vertical-align:middle}
+    </STYLE>
+</HEAD>
+<BODY>
+<PRE>
+<!-- Manpage converted by man2html 3.0.1 -->
+<B><A HREF="dccd.html">dccd(8)</A></B>               Distributed Checksum Clearinghouse               <B><A HREF="dccd.html">dccd(8)</A></B>
+
+
+</PRE>
+<H2><A NAME="NAME">NAME</A></H2><PRE>
+     <B>dccd</B> -- Distributed Checksum Clearinghouse Daemon
+
+
+</PRE>
+<H2><A NAME="SYNOPSIS">SYNOPSIS</A></H2><PRE>
+     <B>dccd</B> [<B>-64dVbfFQ</B>] <B>-i</B> <I>server-ID</I> [<B>-n</B> <I>brand</I>] [<B>-h</B> <I>homedir</I>] <B>-I</B> [<I>host-ID</I>][<I>,user</I>]
+          [<B>-a</B> [<I>server-addr</I>][<I>,server-port</I>]] [<B>-q</B> <I>qsize</I>]
+          [<B>-G</B> [<I>on,</I>][<I>weak-body,</I>][<I>weak-IP,</I>][<I>embargo</I>][<I>,window</I>][<I>,white</I>]]
+          [<B>-W</B> [<I>rate</I>][<I>,chg</I>][<I>,dbsize</I>]] [<B>-K</B> [<I>no-</I>]<I>type</I>] [<B>-T</B> <I>tracemode</I>]
+          [<B>-u</B> <I>anon-delay</I>[<I>*inflate</I>]] [<B>-C</B> <I>dbclean</I>] [<B>-L</B> <I>ltype,facility.level</I>]
+          [<B>-R</B> [<I>RL</I><B>_</B><I>SUB</I>],[<I>RL</I><B>_</B><I>ANON</I>],[<I>RL</I><B>_</B><I>ALL</I><B>_</B><I>ANON</I>],[<I>RL</I><B>_</B><I>BUGS</I>]]
+
+
+</PRE>
+<H2><A NAME="DESCRIPTION">DESCRIPTION</A></H2><PRE>
+     <B>Dccd</B> receives reports of checksums related to mail received by DCC
+     clients and queries about the total number of reports of particular
+     checksums.  A DCC server never receives mail, address, headers, or other
+     information from clients, but only cryptographically secure checksums of
+     such information.  A DCC server cannot determine the text or other infor-
+     mation that corresponds to the checksums it receives.  It only acts as a
+     clearinghouse of total counts of checksums computed by clients.
+
+     Each DCC server or close cluster of DCC servers is identified by a
+     numeric <I>server-ID</I>.  Each DCC client is identified by a <I>client-ID</I>, either
+     explicitly listed in the <I>ids</I> file or the special anonymous client-ID.
+     Many computers are expected to share a single <I>client-ID</I>.  A <I>server-ID</I> is
+     less than 32768 while a <I>client-ID</I> is between 32768 and 16777215.  DCC
+     server-IDs need be known only to DCC servers and the people running them.
+     The passwords associated with DCC server-IDs should be protected, because
+     DCC servers listen to commands authenticated with server-IDs and their
+     associated passwords.  Each client that does not use the anonymous ID
+     must know the client-ID and password used by each of its servers.  A sin-
+     gle client computer can use different passwords with different server
+     computers.  See the <I>ids</I> file.
+
+     A whitelist of known good (or bad) sources of email prevents legitimate
+     mailing lists from being seen as unsolicited bulk email by DCC clients.
+     The whitelist used by a DCC server is built into the database when old
+     entries are removed by <B><A HREF="dbclean.html">dbclean(8)</A></B>.  Each DCC client has its own, local
+     whitelist, and in general, whitelists work better in DCC clients than
+     servers.
+
+     The effectiveness of a Distributed Checksum Clearinghouse increases as
+     the number of subscribers increases.  Flooding reports of checksums among
+     DCC servers increases the effective number of subscribers to each server.
+     Each <B>dccd</B> daemon tries to maintain TCP/IP connections to the other
+     servers listed in the <I>flod</I> file, and send them reports containing check-
+     sums with total counts exceeding thresholds.  Changes in the <I>flod</I> file
+     are noticed automatically within minutes.
+
+     Controls on report flooding are specified in the <I>flod</I> file.  Each line
+     specifies a hostname and port number to which reports should be flooded,
+     a server-ID to identify and authenticate the output stream, a server-ID
+     to identify and authenticate an input stream from the same server, and
+     flags with each ID.  The ability to delete reports of checksums is handy,
+     but could be abused.  If <I>del</I> is not present among the <I>in-opts</I> options for
+     the incoming ID, incoming delete requests are logged and then ignored.
+     Floods from DCC "brands" that count only mail to spam traps and whose
+     servers use the <B>-Q</B> option to count extremely bulk mail should be marked
+     with <I>traps</I>.  They can be seen as counting millions of targets, so the
+     <I>traps</I> flag on their <I>flod</I> file entry changes their incoming flooded
+     reports counts to <I>many.</I>
+
+     <B>Dccd</B> automatically checks its <I>flod</I> and <I>ids</I> files periodically.  <B><A HREF="cdcc.html">Cdcc(8)</A></B>
+     has the commands <B>new ids</B> and <B>flood check</B> to tell <B>dccd</B> to check those two
+     files immediately.  Both files are also checked for changes after the
+     SIGHUP signal.
+
+   <A NAME="OPTIONS"><B>OPTIONS</B></A>
+     The following options are available:
+
+     <A NAME="OPTION-6"><B>-6</B></A>   enable IPv6.  The default is equivalent to <B>-4</B>.  See also the IPv4
+          and IPv6 options in the <I>flod</I> file description below and the <I>IPv6</I> <I>on</I>
+          <B><A HREF="cdcc.html">cdcc(8)</A></B> command.
+
+     <A NAME="OPTION-4"><B>-4</B></A>   disable IPv6.  See also <B>-6</B>.
+
+     <A NAME="OPTION-d"><B>-d</B></A>   enables debugging output.  Additional <B>-d</B> options increase the number
+          of messages.
+
+     <A NAME="OPTION-V"><B>-V</B></A>   displays the version of the DCC server daemon.
+
+     <A NAME="OPTION-b"><B>-b</B></A>   causes the server to not detach itself from the controlling tty or
+          put itself into the background.
+
+     <A NAME="OPTION-F"><B>-F</B></A>   uses write() instead of mmap() in some cases to modify the DCC data-
+          base.  It is the default on Solaris.
+
+     <A NAME="OPTION-f"><B>-f</B></A>   turns off <B>-F</B>.
+
+     <A NAME="OPTION-Q"><B>-Q</B></A>   causes the server to treat reports of checksums as queries except
+          from DCC clients marked trusted in the <I>ids</I> file with <I>rpt-ok</I>.  See <B>-u</B>
+          to turn off access by anonymous or unauthenticated clients
+
+     <A NAME="OPTION-i"><B>-i</B></A> <I>server-ID</I>
+          specifies the ID of this DCC server.  Each server identifies itself
+          as responsible for checksums that it forwards to other servers.
+
+     <A NAME="OPTION-n"><B>-n</B></A> <I>brand</I>
+          is an arbitrary string of letters and numbers that identifies the
+          organization running the DCC server.  The brand is required, and
+          appears in the SMTP <I>X-DCC</I> headers generated by the DCC.
+
+     <A NAME="OPTION-h"><B>-h</B></A> <I>homedir</I>
+          overrides the default DCC home directory, <I>@prefix@</I>.
+
+     <A NAME="OPTION-I"><B>-I</B></A> [<I>host-ID</I>][<I>,user</I>]
+          changes the server's globally unique identity for flooding from the
+          default value consisting of the first 16 characters of the host
+          name.  or changes the UID and GID of the process <I>Host-ID</I> is a string
+          of up to 16 characters that replaces the first 16 characters of the
+          system's hostname in assertions of the server-ID that are flooded to
+          peers.  <I>User</I> must be valid user name.
+
+     <A NAME="OPTION-a"><B>-a</B></A> [<I>server-addr</I>][<I>,server-port</I>]
+          adds an hostname or IP address to the list of local IP addresses
+          that the server answers.  Multiple <B>-a</B> options can be used to specify
+          a subset of the available network interfaces or to use more than one
+          port number.  The default without any <B>-a</B> options is to listen on all
+          local IP addresses.  It can be useful to list some of the IP
+          addresses of multi-homed hosts to deal with firewalls.  By default
+          <I>server-port</I> is 6277 for DCC servers and 6276 for Greylist servers.
+          It is the UDP port at which DCC requests are received and the TCP
+          port for incoming floods of reports.
+
+          If <I>server-addr</I> is absent and if the <B>getifaddrs(8)</B> function is sup-
+          ported, separate UDP sockets are bound to each configured network
+          interface so that each DCC clients receives replies from the IP
+          addresses to which corresponding request are sent.  If <B>dccd</B> is
+          started before all network interfaces are turned on or there are
+          interfaces that are turned on and off or change their addresses such
+          as PPP interfaces, then the special string <I>@</I> should be used to tell
+          <B>dccd</B> to bind to an IN_ADDRANY UDP socket.
+
+          Outgoing TCP connections to flood checksum reports to other DCC
+          servers used the IP address of a single <B>-a</B> option, but only if there
+          is single option that is not localhost.  See also the <I>flod</I> file.
+
+     <A NAME="OPTION-q"><B>-q</B></A> <I>qsize</I>
+          specifies the maximum size of the queue of requests from anonymous
+          or unauthenticated clients.  The default value is the maximum DCC
+          RTT in seconds times 200 or 1000.
+
+     <A NAME="OPTION-G"><B>-G</B></A> [<I>on,</I>][<I>weak-body,</I>][<I>weak-IP,</I>][<I>embargo</I>][<I>,window</I>][<I>,white</I>]
+          changes <B>dccd</B> to a Greylist server for <B><A HREF="dccm.html">dccm(8)</A></B> or <B><A HREF="dccifd.html">dccifd(8)</A></B>.
+          Greylisting consists of temporarily rejecting or embargoing mail
+          from unfamiliar combinations of SMTP client IP address, SMTP enve-
+          lope sender, and SMTP envelope recipient.  If the SMTP client per-
+          sists for <I>embargo</I> <I>seconds</I> and so is probably not an open proxy,
+          worm-infected personal computer, or other transient source of spam,
+          the triple of <I>(IP</I> <I>address,sender,recipient)</I> is added to a database
+          similar to the usual DCC database.  If the SMTP client does not try
+          again after <I>embargo</I> seconds and before <I>window</I> seconds after the
+          first attempt, the triple is forgotten.  If the SMTP client persists
+          past the embargo, the triple is added to the database and becomes
+          familiar and the message is accepted.  Familiar triples are remem-
+          bered for <I>white</I> seconds after the last accepted mail message.  The
+          triple is forgotten if it is ever associated with unsolicited bulk
+          email.
+
+          All three durations can be a number of minutes, hours, days, or
+          weeks followed by <I>MINUTES</I>, <I>M</I>, <I>HOURS</I>, <I>H</I>, <I>DAYS</I>, <I>D</I>, <I>WEEKS</I> or <I>W</I>.  The
+          default is <B>-G</B> <I>270seconds,7days,63days</I>.  The first duration or the
+          <I>embargo</I> should be longer than open proxies can linger retransmit-
+          ting.  The second <I>window</I> time should be as long as legitimate mail
+          servers persist in retransmitting to recognize embargoed messages
+          whose retransmissions were not received because of network or other
+          problems.  The <I>white</I> time should be long enough to recognize and not
+          embargo messages from regular senders.
+
+          Usually the DCC greylist system requires that an almost identical
+          copy of the message be retransmitted during the <I>embargo</I>.  If
+          <I>weak-body</I> is present, any message with the same triple of sender IP
+          address, sender mail address, and target mail address ends the
+          embargo, even if the body of the message differs.
+
+          If <I>weak-IP</I> is present, all mail from an SMTP client at an IP address
+          is accept after any message from the same IP address has been
+          accepted.
+
+          Unlike DCC checksums, the contents of greylist databases are private
+          and do not benefit from broad sharing.  However, large installations
+          can use more two or more greylist servers flooding triples among
+          themselves.  Flooding among greylist servers is controlled by the
+          <I>grey</I><B>_</B><I>flod</I> file.
+
+          All greylist cooperating or flooding greylist servers <I>must</I> use the
+          same <B>-G</B> values.
+
+          Clients of greylist servers cannot be anonymous and must have
+          client-IDs and passwords assigned in the <I>ids</I> file.  This implies
+          that cdcc commands directed to greylist servers must specify the
+          server-ID.
+
+          White- and blacklists are honored by the DCC clients.  whitelisted
+          messages are embargoed or checked with a greylist server.  The
+          greylist triples of blacklisted messages, messages whose DCC counts
+          make them spam, and other messages known to be spam are sent to a
+          greylist server to be removed from the greylist database and cause
+          an embargo on the next messages with those triples.
+
+          Messages whose checksums match greylist server whitelists are not
+          embargoed and the checksums of their triples are not added to the
+          greylist database.
+
+          The target counts of embargoed messages are reported to the DCC net-
+          work to improve the detection of bulk mail.
+
+     <A NAME="OPTION-W"><B>-W</B></A> [<I>rate</I>][<I>,chg</I>][<I>,dbsize</I>]
+          controls quick database cleaning.  If the database is larger than
+          <I>dbsize</I>, it seems that the database has not recently and is not about
+          to be cleaned, <B>dccd</B> is receiving fewer than <I>rate</I> requests per sec-
+          ond, and if telling DCC clients that the database is about to be
+          cleaned reduces that rate by <I>chg</I>%, then <B>dccd</B> starts <B><A HREF="dbclean.html">dbclean(8)</A></B> for a
+          quick database cleaning.  The cleaning is abandoned if it takes too
+          long.  The default values are equivalent to <B>-W</B> <I>1.0,40.0,RSS</I> where
+          <I>RSS</I> is the maximum dccd resident set displayed the system log by <B>-d</B>
+          when <B>starts</B>.
+
+     <A NAME="OPTION-K"><B>-K</B></A> [<I>no-</I>]<I>type</I>
+          marks checksums of <I>type</I> (not) be kept or counted in the database
+          unless they appear in the whitelist.  Explicit settings add to or
+          remove from the initial contents of the list, which is equivalent to
+          <B>-K</B> <I>Body</I> <B>-K</B> <I>Fuz1</I> <B>-K</B> <I>Fuz2</I>.
+
+     <A NAME="OPTION-T"><B>-T</B></A> <I>tracemode</I>
+          causes the server to trace or record some operations.  <I>tracemode</I>
+          must be one of the following:
+            <I>ADMN</I>    administrative requests from the control program, <B><A HREF="cdcc.html">cdcc(8)</A></B>
+            <I>ANON</I>    errors by anonymous clients
+            <I>CLNT</I>    errors by authenticated clients
+            <I>RLIM</I>    rate-limited messages
+            <I>QUERY</I>   all queries and reports
+            <I>RIDC</I>    some messages concerning the report-ID cache that is used
+                    to detect duplicate reports from clients
+            <I>FLOOD</I>   messages about inter-server flooding connections
+            <I>FLOOD2</I>  messages about flooded reports
+            <I>IDS</I>     unknown server-IDs in flooded reports
+            <I>BL</I>      requests from clients in the <I>blacklist</I> file.
+            <I>DB</I>      odd database events including long chains of duplicate
+                    checksums
+            <I>WLIST</I>   reports of whitelisted checksums from authenticated, not
+                    anonymous DCC clients
+          The default is <I>ANON</I> <I>CLNT</I>.
+
+     <A NAME="OPTION-u"><B>-u</B></A> <I>anon-delay</I>[<I>*inflate</I>]
+          changes the number of milliseconds anonymous or unauthenticated
+          clients must wait for answers to their queries and reports.  The
+          purpose of this delay is to discourage large anonymous clients.  The
+          <I>anon-delay</I> is multiplied by 1 plus the number of recent anonymous
+          requests from an IP address divided by the <I>inflate</I> value.
+
+          The string <I>FOREVER</I> turns off all anonymous or unauthenticated access
+          not only for checksum queries and reports but also <B><A HREF="cdcc.html">cdcc(8)</A> stats</B>
+          requests.  A missing value for <I>inflate</I> turns off inflation.
+
+          The default value is <I>50,none</I>, except when <B>-G</B> is used in which case
+          <I>FOREVER</I> is assumed and required.
+
+     <A NAME="OPTION-C"><B>-C</B></A> <I>dbclean</I>
+          changes the default name or path of the program used to rebuild the
+          hash table when it becomes too full.  The default value is
+          <I>@libexecdir@/dbclean</I> in the <I>@libexecdir@</I> directory.  The
+          value can include arguments as in <I>-C</I> <I>'$DCC</I><B>_</B><I>LIBEXEC/dbclean</I> <I>-F'</I>.
+
+           Dbclean <I>should</I> <I>not</I> be run by <B>dccd</B> except in emergencies such as
+          database corruption or hash table overflow.  <B><A HREF="dbclean.html">Dbclean(8)</A></B> should be
+          run daily with the @libexecdir@/cron-dccd cron script
+
+     <A NAME="OPTION-L"><B>-L</B></A> <I>ltype,facility.level</I>
+          specifies how messages should be logged.  <I>Ltype</I> must be <I>error</I>, <I>info</I>,
+          or <I>off</I> to indicate which of the two types of messages are being con-
+          trolled or to turn off all <B>syslog(3)</B> messages from <B>dccd</B>.  <I>Level</I> must
+          be a <B>syslog(3)</B> level among <I>EMERG</I>, <I>ALERT</I>, <I>CRIT</I>, <I>ERR</I>, <I>WARNING</I>, <I>NOTICE</I>,
+          <I>INFO</I>, and <I>DEBUG</I>.  <I>Facility</I> must be among <I>AUTH</I>, <I>AUTHPRIV</I>, <I>CRON</I>,
+          <I>DAEMON</I>, <I>FTP</I>, <I>KERN</I>, <I>LPR</I>, <I>MAIL</I>, <I>NEWS</I>, <I>USER</I>, <I>UUCP</I>, and <I>LOCAL0</I> through
+          <I>LOCAL7</I>.  The default is equivalent to
+                <B>-L</B> <I>info,MAIL.NOTICE</I> <B>-L</B> <I>error,MAIL.ERR</I>
+
+     <A NAME="OPTION-R"><B>-R</B></A> [<I>RL</I><B>_</B><I>SUB</I>],[<I>RL</I><B>_</B><I>ANON</I>],[<I>RL</I><B>_</B><I>ALL</I><B>_</B><I>ANON</I>],[<I>RL</I><B>_</B><I>BUGS</I>]
+          sets one or more of the four rate-limits.  <I>RL</I><B>_</B><I>SUB</I> limits the number
+          of DCC transactions per second from subscribers or DCC clients with
+          known client-IDs and passwords.  This limit applies to each IP
+          address independently.
+
+          <I>RL</I><B>_</B><I>ANON</I> limits the number of DCC transactions per second from anony-
+          mous DCC clients.  This limit applies to each IP address indepen-
+          dently.  It is better to use <B>-u</B> than to change this value to exclude
+          anonymous clients.
+
+          <I>RL</I><B>_</B><I>ALL</I><B>_</B><I>ANON</I> limits the number of DCC transactions per second from
+          all anonymous DCC clients.  This limit applies to all anonymous
+          clients as a group, regardless of their IP addresses.
+
+          <I>RL</I><B>_</B><I>BUGS</I> limits the number of complaints or error messages per second
+          for all anonymous DCC clients as a group as well as for each DCC
+          client by IP address.
+
+          The default is equivalent to <B>-R</B> <I>400,50,600,0.1</I>
+
+
+</PRE>
+<H2><A NAME="FILES">FILES</A></H2><PRE>
+     <A NAME="FILE-@prefix@">@prefix@</A>  is the DCC home directory containing data and control files.
+     <A NAME="FILE-dcc_db">dcc_db</A>    is the database of mail checksums.
+     <A NAME="FILE-dcc_db.hash">dcc_db.hash</A> is the mail checksum database hash table.
+     <A NAME="FILE-grey_db">grey_db</A>   is the database of greylist checksums.
+     <A NAME="FILE-grey_db.hash">grey_db.hash</A> is the greylist database hash table.
+     <A NAME="FILE-flod">flod</A>      contains lines controlling DCC flooding of the form:
+               <I>host</I>[<I>,rport</I>][<I>;src</I>[<I>,lport</I>]] <I>rem-ID</I> [<I>passwd-ID</I> [<I>o-opt</I> [<I>i-opt</I>]]]
+               where absent optional values are signaled with "-" and
+                <I>host</I> is the IP address or name of a DCC server and <I>rport</I> is
+                    the name or number of the TCP port used by the remote
+                    server.
+                <I>src</I> and <I>lport</I> are the IP address or host name and TCP port
+                    from which the outgoing flooding connection should come.
+                    Incoming flooding connections must arrive at an address
+                    and port specified with <B>-a</B>.
+                <I>rem-id</I> is the server-ID of the remote DCC server.
+                <I>passwd-ID</I> is a server-ID that is not assigned to a server, but
+                    whose first password is used to sign checksum reports sent
+                    to the remote system.  Either of its passwords are
+                    required with incoming reports.  If it is absent or "-",
+                    outgoing floods are signed with the first password of the
+                    local server in the <I>ids</I> file and incoming floods must be
+                    signed with either password of the remote server-ID.
+                <I>i-opt</I> and <I>o-opt</I> are comma separated lists of
+                     <I>off</I> turns off flooding to the remote or local system.
+                     <I>traps</I> indicates that the remote sending or local receiv-
+                         ing system has only spam traps.
+                     <I>no-del</I> says checksum delete requests are refused by the
+                         remote or local server and so turns off sending or
+                         accepting delete requests, respectively.  By default,
+                         delete requests are sent to remote servers and
+                         accepted in incoming floods if and only if the peers
+                         are exchanging DCC reputations.
+                     <I>del</I> says delete requests are accepted by the remote or
+                         local server.
+                     <I>no-log-del</I> turns off logging of incoming requests to
+                         delete checksums.
+                     <I>passive</I> is used to tell a server outside a firewall to
+                         expect a peer inside to create both of the pair of
+                         input and output TCP connections used for flooding.
+                         The peer inside the firewall should use <I>SOCKS</I> or <I>NAT</I>
+                         on its <I>flod</I> file entry for this system.
+                     <I>SOCKS</I> is used to tell a server inside a firewall that it
+                         should create both of the TCP connections used for
+                         flooding and that SOCKS protocol should be used.  The
+                         peer outside the firewall should use <I>passive</I> on its
+                         <I>flod</I> file entry for this system.
+                     <I>NAT</I> differs from <I>SOCKS</I> only by not using the SOCKS proto-
+                         col.
+                     <I>ID1-&gt;ID2</I> converts server-ID <I>ID1</I> in flooded reports to
+                         server-ID <I>ID2</I>.  Either <I>ID1</I> or <I>ID2</I> may be the string
+                         `self' to specify the server's own ID.  <I>ID1</I> can be
+                         the string `all' to specify all server-IDs or a pair
+                         of server-IDs separated by a dash to specify an
+                         inclusive range.  <I>ID2</I> can be the string `ok' to send
+                         or receive reports without translation or the string
+                         `reject' to not send outgoing or refuse incoming
+                         reports.  Only the first matching conversion is
+                         applied.  For example, when `self-&gt;ok,all-&gt;reject' is
+                         applied to a locally generated report, the first con-
+                         version is applied and the second is ignored.
+                     <I>leaf=path-len</I> does not send reports with paths longer
+                         than <I>path-len</I> server-IDs.
+                     <I>IPv4</I> overrides a <B>-6</B> setting for this flooding peer.
+                     <I>IPv6</I> overrides the default or an explicit <B>-4</B> setting.
+                     <I>vers</I> specifies the version of the DCC flooding protocol
+                         used by the remote DCC server with a string such as
+                         `version2'.
+                     <I>trace</I> sends information about a single peer like the
+                         <B><A HREF="cdcc.html">cdcc(8)</A></B> command <B>trace FLOOD on</B> does for all peers.
+                     <I>trace2</I> sends information about individual flooded reports
+                         like the <B><A HREF="cdcc.html">cdcc(8)</A></B> command <B>trace FLOOD2 on</B> does for all
+                         peers.
+     <A NAME="FILE-grey_flod">grey_flod</A> is the equivalent of <I>flod</I> used by <B>dccd</B> when it is a greylist
+               server.
+     <A NAME="FILE-flod.map">flod.map</A>  is an automatically generated file in which <B>dccd</B> records its
+               progress sending or flooding reports to DCC peers.
+     <A NAME="FILE-grey_flod.map">grey_flod.map</A> is the equivalent of <I>flod.map</I> <I>used</I> <I>by</I> <B>dccd</B> when it is a
+               greylist server.
+     <A NAME="FILE-ids">ids</A>       contains the IDs and passwords known by the DCC server.  An <I>ids</I>
+               file that can be read by others cannot be used.  It contains
+               blank lines, comments starting with "#" and lines of the form:
+                     <I>id</I>[<I>,rpt-ok</I>][<I>,delay=ms</I>[<I>*inflate</I>]] <I>passwd1</I> [<I>passwd2</I>]
+               where
+                <I>id</I>  is a DCC <I>client-ID</I> or <I>server-ID</I>.
+                <I>Rpt-ok</I> if present overrides <B>-Q</B> by saying that this client is
+                    trusted to report only checksums for unsolicited bulk
+                    mail.
+                <I>delay=ms</I>[<I>*inflate</I>] delays answers to systems using the client
+                    <I>id</I>.  The <I>delay</I> in milliseconds is multiplied by 1 plus the
+                    number of recent requests from an IP address using <I>id</I>
+                    divided by the <I>inflate</I> value.  See <B>-u</B>.
+                <I>passwd1</I> is the password currently used by clients with identi-
+                    fier <I>id</I>.  It is a 1 to 32 character string that does not
+                    contain blank, tab, newline or carriage return characters.
+
+                <I>passwd2</I> is the optional next password that those clients will
+                    use.  A DCC server accepts either password if both are
+                    present in the file.
+               Both passwords can be absent if the entry not used except to
+               tell <B>dccd</B> that server-IDs in the flooded reports are valid.
+               The string <I>unknown</I> is equivalent to the null string.
+     <A NAME="FILE-whitelist">whitelist</A> contains the DCC server whitelist.  It is not used directly but
+               is loaded into the database when <B><A HREF="dbclean.html">dbclean(8)</A></B> is run.
+     <A NAME="FILE-grey_whitelist">grey_whitelist</A> contains the greylist server whitelist.  It is not used
+               directly but is loaded into the database when <B><A HREF="dbclean.html">dbclean(8)</A></B> is run
+               with <B>-G</B>.
+     <A NAME="FILE-blacklist">blacklist</A> if present, contains a list of IP addresses and blocks of IP
+               addresses DCC clients that are ignored.  Each line in the file
+               should be blank, a comment starting with '#', or an IP address
+               or block of IP addresses in the form
+                     [<I>trace,</I>] [<I>ok,</I>] [<I>bad</I>] xxx.xxx.xxx.xxx[/yy]
+               Changes to the file are automatically noticed and acted upon
+               within a few minutes.  Addresses or blocks of addresses can be
+               preceded with <I>ok</I> to "punch holes" in blacklisted blocks or with
+               <I>trace</I> to log activity.  This mechanism is intended for no more
+               than a few dozen blocks of addresses.
+     <A NAME="FILE-dccd_clients">dccd_clients</A> contains client IP addresses and activity counts.
+     <A NAME="FILE-grey_clients">grey_clients</A> contains greylist client IP addresses and activity counts.
+
+
+</PRE>
+<H2><A NAME="EXAMPLES">EXAMPLES</A></H2><PRE>
+     <B>dccd</B> is usually started with other system daemons with something like the
+     script <I>@libexecdir@/rcDCC</I>.  That scripts uses values in
+     @prefix@/dcc_conf to start the server.  With the argument <I>stop</I>,
+     <I>@libexecdir@/rcDCC</I> can be used to stop the daemon.
+
+     The database grows too large unless old reports are removed.  <B><A HREF="dbclean.html">dbclean(8)</A></B>
+     should be run daily with the @libexecdir@/cron-dccd cron script
+
+
+</PRE>
+<H2><A NAME="SEE-ALSO">SEE ALSO</A></H2><PRE>
+     <B><A HREF="cdcc.html">cdcc(8)</A></B>, <B><A HREF="dcc.html">dcc(8)</A></B>, <B><A HREF="dbclean.html">dbclean(8)</A></B>, <B><A HREF="dblist.html">dblist(8)</A></B>, <B><A HREF="dccifd.html">dccifd(8)</A></B>, <B><A HREF="dccm.html">dccm(8)</A></B>, <B><A HREF="dccproc.html">dccproc(8)</A></B>.
+     <B><A HREF="dccsight.html">dccsight(8)</A></B>,
+
+
+</PRE>
+<H2><A NAME="HISTORY">HISTORY</A></H2><PRE>
+     <B>dccd</B> is based on an idea from Paul Vixie.  It was designed and written at
+     Rhyolite Software, starting in 2000.  This document describes version
+     1.3.103.
+
+                               February 26, 2009
+</PRE>
+<HR>
+<ADDRESS>
+Man(1) output converted with
+<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
+modified for the DCC $Date 2001/04/29 03:22:18 $
+<BR>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG SRC="http://logos.dcc-servers.net/border.png"
+            class=logo ALT="DCC logo">
+    </A>
+<A HREF="http://validator.w3.org/check?uri=referer">
+    <IMG class=logo ALT="Valid HTML 4.01 Strict"
+        SRC="http://www.w3.org/Icons/valid-html401">
+    </A>
+</ADDRESS>
+</BODY>
+</HTML>
diff -r 000000000000 -r c7f6b056b673 dccd/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,8 @@
+Makefile.in
+dccd.c
+dccd_defs.h
+iflod.c
+oflod.c
+rl.c
+work.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 dccd/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,56 @@
+# make the Distributed Checksum Clearinghouse server
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.20 $Revision$
+# @configure_input@
+
+DEPTH	=..
+PROG	=dccd
+SRCS	=$(PROG).c iflod.c oflod.c rl.c work.c
+
+SUBDIR	=dump-clients
+
+CFLAGS	+=$(SRVRINC)
+LDADD	+=$(SRVRLIBS)
+DPADD	+=$(SRVRLIBS)
+
+@MAKE_DOT@ifdef DCCD_MAX_FLOODS
+CFLAGS	+=-DDCCD_MAX_FLOODS=$(DCCD_MAX_FLOODS)
+@MAKE_DOT@endif
+
+DCC_BINDIR=@installroot@@libexecdir@
+@MAKE_PROG@
+@MAKE_INC2@
diff -r 000000000000 -r c7f6b056b673 dccd/dccd.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd/dccd.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,2263 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * server
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.297 $Revision$
+ */
+
+#include "dccd_defs.h"
+#include <signal.h>			/* for Linux and SunOS */
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include "dcc_ifaddrs.h"
+
+DCC_EMSG dcc_emsg;
+
+static const char *homedir;
+static char *aargs[10];
+static char **aarg = &aargs[0];
+static DCC_PATH dbclean_def = DCC_LIBEXECDIR"/dbclean";
+static char *dbclean = dbclean_def;
+static pid_t dbclean_pid = -1;
+static char *addr_str;			/* an IP address for dbclean */
+static char dbclean_str[] = "dbclean";
+#define MAX_DBCLEAN_FLAGS 50
+static char dbclean_flags[MAX_DBCLEAN_FLAGS+1] = "-Pq";
+static int dbclean_flags_len = LITZ("-Pq");
+static const char *dbclean_argv[20] = {dbclean_str, dbclean_flags};
+static int dbclean_argc = 2;
+static char dbclean_args_str[200];
+static int dbclean_args_str_len;
+
+/* do not try to run dbclean too often */
+time_t dbclean_limit_secs = DBCLEAN_LIMIT_SECS;
+static time_t dbclean_failed;
+
+static double wfix_quiet_rate = 1.0;	/* max load that allows window fixing */
+static double wfix_busy_rate;		/* measured active rate */
+static double wfix_rate_change = 0.4;	/* rate reduction to this allows fix */
+static DB_PTR wfix_size;
+static u_char wfix_size_set = 0;
+DBCLEAN_WFIX_STATE dbclean_wfix_state = WFIX_DELAY;
+static double wfix_ops;
+static time_t wfix_check_start;
+static time_t dbclean_wfix_secs;	/* window fixing timer */
+#define WFIX_POST_CLEAN_SECS (6*60*60)	/* do not fix it soon after cleaning */
+#define WFIX_PRE_CLEAN_SECS (2*60*60)	/*	or just before cleaning */
+#define WFIX_RECHECK_SECS   (15*60)	/* between checks for window overflow */
+#define WFIX_QUIET_SECS	    (5*60)	/* waiting for clients to flee */
+#define	WFIX_MEASURE_SECS   (5*60)	/* counting clients */
+#define WFIX_MAX_SECS	(WFIX_PRE_CLEAN_SECS+WFIX_POST_CLEAN_SECS)
+
+const char *need_del_dbclean;
+time_t del_dbclean_next;
+time_t dbclean_limit;
+static time_t clean_fake_secs;		/* timer for missing cron job */
+static time_t clean_last_secs;
+
+u_long dccd_tracemask = DCC_TRACE_ON_DEF_BITS;
+
+u_char background = 1;
+int stopint;				/* !=0 if stopping or received signal */
+
+static time_t flods_ck_secs = FLODS_CK_SECS;
+
+const char *brand = "";
+
+static char *my_srvr_id_str;
+DCC_SRVR_ID my_srvr_id;
+
+u_char use_ipv6 = 0;
+u_int16_t def_port;			/* default port #, network byte order */
+typedef struct port_list {
+    struct port_list *fwd;
+    u_int16_t	port;			/* network byte order */
+} PORT_LIST;
+static PORT_LIST *ports;
+SRVR_SOC *srvr_socs;
+static SRVR_SOC **srvr_socs_end = &srvr_socs;
+int srvr_rcvbuf = 1024*1024;
+
+#ifdef USE_DBCLEAN_F
+static u_char db_mode = DB_OPEN_MMAP_WRITE;
+#else
+static u_char db_mode = 0;
+#endif
+
+int grey_embargo = DEF_GREY_EMBARGO;	/* seconds to delay new traffic */
+int grey_window = DEF_GREY_WINDOW;	/* wait as long as this */
+int grey_white = DEF_GREY_WHITE;	/* remember this long */
+
+char our_hostname[MAXHOSTNAMELEN];
+DCC_SUM host_id_sum;			/* advertised with our server-ID */
+time_t host_id_next, host_id_last;	/* when to advertise */
+
+u_char anon_off;			/* turn off anonymous access */
+time_t anon_delay_us = DCC_ANON_DELAY_US_DEF;   /* delay anonymous clients */
+u_int anon_delay_inflate = DCC_ANON_INFLATE_OFF;
+
+u_char stop_mode;			/* 0=normal 1=reboot 2=with/DB clean */
+
+static int total_ops;
+static QUEUE *queue_free;
+static QUEUE *queue_head;
+
+/* assume or hope we can handle 500 requests/second */
+int queue_max = 500*DCC_MAX_RTT_SECS;	/* ultimate bound on queue size */
+static int queue_max_cur;		/* current upper bound */
+static int queue_cur;			/* current queue size */
+
+struct timeval wake_time;		/* when we awoke from select() */
+struct timeval req_recv_time;		/* when request arrived */
+
+DCC_TS future;				/* timestamp sanity */
+
+
+static DB_NOKEEP_CKS set_new_nokeep_cks, reset_new_nokeep_cks;
+
+DCC_TGTS flod_tholds[DCC_DIM_CKS];	/* flood at or after these thresholds */
+
+static const char *parse_rl_rate(RL_RATE *, float, const char *, const char *);
+static void add_dbclean_flag(char);
+static void add_dbclean_arg(const char *);
+static void check_dbclean(int);
+static void run_dbclean(const char *, const char *);
+static void sigterm(int);
+static void sighup(int);
+static void stop_children(void);
+static u_char get_if_changes(u_char);
+static SRVR_SOC *add_srvr_soc(u_char, int, const void *, u_int16_t);
+static int open_srvr_socs(int);
+static void wfix_later(time_t);
+static void recv_job(void) NRATTRIB;
+static u_char new_job(SRVR_SOC *);
+static void NRATTRIB dccd_quit(int, const char *, ...) PATTRIB(2,3);
+
+static void
+usage(const char* barg)
+{
+	static const char str[] = {
+	    "usage: [-64dVbfFQ] -i server-ID [-n brand] [-h homedir]\n"
+	    "   [-I [host-ID][,user]] [-a [server-addr][,server-port]]"
+	    " [-q qsize]\n"
+	    "   [-G [on,][weak-body,][weak-IP,]embargo],[window],[white]]\n"
+	    "   [-W [rate][,chg][,dbsize]] [-K [no-]type] [-T tracemode]\n"
+	    "   [-u anon-delay[,inflate] [-C dbclean]"
+	    " [-L ltype,facility.level]\n"
+	    "   [-R [RL_SUB],[RL_FREE],[RL_ALL_FREE],[RL_BUGS]]"
+	};
+	static u_char complained;
+
+	if (!complained) {
+		if (barg)
+			dcc_error_msg("unrecognized \"%s\"\nusage: %s\n..."
+				      " continuing",
+				      barg, str);
+		else
+			dcc_error_msg("%s\n... continuing", str);
+		complained = 1;
+	}
+}
+
+
+int NRATTRIB
+main(int argc, char **argv)
+{
+	char *p;
+	const char *rest;
+	u_char print_version = 0;
+	DCC_CK_TYPES type;
+	DCC_SOCKU *sup;
+	u_int16_t port;
+	int new_embargo, new_window, new_white;
+	int error, i;
+	const char *cp;
+	double d1, d2;
+	u_long l;
+
+	dcc_syslog_init(1, argv[0], 0);
+
+	if (DCC_DIM_CKS != DCC_COMP_DIM_CKS)
+		dcc_logbad(EX_SOFTWARE,
+			   "DCC_DIM_CKS != DCC_COMP_DIM_CKS;"
+			   " check uses of both");
+
+	/* get first bytes of our hostname to name our server-ID */
+	memset(our_hostname, 0, sizeof(our_hostname));
+	if (0 > gethostname(our_hostname, sizeof(our_hostname)-1))
+		dcc_logbad(EX_NOHOST, "gethostname(): %s", ERROR_STR());
+	our_hostname[sizeof(our_hostname)-1] = '\0';
+	if (our_hostname[0] == '\0')
+		dcc_logbad(EX_NOHOST, "null hostname from gethostname()");
+	strncpy((char *)host_id_sum, our_hostname, sizeof(host_id_sum));
+
+	parse_rl_rate(&rl_sub_rate, 0.5, "RL_SUB", "400");
+	parse_rl_rate(&rl_anon_rate, RL_AVG_SECS, "RL_ANON", "50");
+	parse_rl_rate(&rl_all_anon_rate, 0.5, "RL_ALL_ANON", "600");
+	parse_rl_rate(&rl_bugs_rate, RL_AVG_SECS, "RL_BUGS", "0.1");
+
+	/* this must match DCCD_GETOPTS in cron-dccd.in */
+	while ((i = getopt(argc, argv,
+			   "64dVbfFQi:n:h:a:I:q:G:t:W:K:T:u:C:L:R:")) != -1) {
+		switch (i) {
+		case '6':
+#ifndef NO_IPV6
+			use_ipv6 = 2;
+#endif
+			break;
+		case '4':
+			use_ipv6 = 0;
+			break;
+
+		case 'd':
+			add_dbclean_flag('d');
+			++db_debug;
+			break;
+
+		case 'V':
+			fprintf(stderr, DCC_VERSION"\n");
+			print_version = 1;
+			break;
+
+		case 'b':
+			background = 0;
+			break;
+
+		case 'f':
+			db_mode &= ~DB_OPEN_MMAP_WRITE;
+			add_dbclean_flag('f');
+			break;
+
+		case 'F':
+			db_mode |= DB_OPEN_MMAP_WRITE;
+			add_dbclean_flag('F');
+			break;
+
+		case 'Q':
+			query_only = 1;
+			break;
+
+		case 'i':
+			my_srvr_id_str = optarg;
+			if (!dcc_get_srvr_id(dcc_emsg, &my_srvr_id,
+					     my_srvr_id_str, 0, 0, 0))
+				dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+			add_dbclean_arg("-i");
+			add_dbclean_arg(my_srvr_id_str);
+			break;
+
+		case 'n':
+			/* RFC 2822 says "values between 33 and 126" */
+			cp = optarg;
+			while (*cp >= 33 && *cp <= 126 && *cp != ':')
+				++cp;
+			if (cp == optarg
+			    || (cp - optarg) > ISZ(DCC_BRAND)
+			    || *cp != '\0') {
+				dcc_error_msg("invalid brand name \"-n %s\"",
+					      optarg);
+			} else {
+				brand = optarg;
+			}
+			break;
+
+		case 'h':
+			homedir = optarg;
+			/* tell dbclean "-h ." because we will already
+			 * be there and that allows our -h to be relative */
+			add_dbclean_arg("-h.");
+			break;
+
+		case 'I':
+			p = strchr(optarg, ',');
+			if (p) {
+				*p++ = '\0';
+				dcc_daemon_su(p);
+				if (*optarg == '\0')
+					break;
+			}
+			if (*optarg != '\0')
+				strncpy((char *)host_id_sum, optarg,
+					sizeof(host_id_sum));
+			break;
+
+		case 'a':
+			/* postpone checking host names until we know -6 */
+			if (aarg > LAST(aargs)) {
+				dcc_error_msg("too many -a args");
+				break;
+			}
+			optarg += strspn(optarg, DCC_WHITESPACE);
+			*aarg++ = optarg;
+			break;
+
+		case 'q':
+			l = strtoul(optarg, &p, 10);
+			if (*p != '\0' || l < 2 || l > 1000) {
+				dcc_error_msg("invalid queue length \"%s\"",
+					      optarg);
+			} else {
+				queue_max = l;
+			}
+			break;
+
+		case 'G':
+			grey_on = 1;
+			dcc_syslog_init(1, argv[0], " grey");
+			add_dbclean_arg("-Gon");
+			/* handle leading "on" "weak-body", and "weak-IP" */
+			rest = optarg;
+			while (*rest) {
+				if (dcc_ck_word_comma(&rest, "weak-body")
+				    || dcc_ck_word_comma(&rest, "weak_body")
+				    || dcc_ck_word_comma(&rest, "weak")) {
+					grey_weak_body = 1;
+					continue;
+				}
+				if (dcc_ck_word_comma(&rest, "weak-IP")
+				    || dcc_ck_word_comma(&rest, "weak_IP")) {
+					grey_weak_ip = 1;
+					continue;
+				}
+				if (!dcc_ck_word_comma(&rest, "on"))
+					break;
+			}
+			new_embargo = dcc_get_secs(rest, &rest,
+						   0, MAX_GREY_EMBARGO,
+						   grey_embargo);
+			if (new_embargo < 0) {
+				dcc_error_msg("invalid greylist embargo"
+					      " \"-G %s\"", optarg);
+				break;
+			}
+			new_window = dcc_get_secs(rest, &rest,
+						  new_embargo, MAX_GREY_WINDOW,
+						  max(new_embargo,grey_window));
+			if (new_window < 0) {
+				dcc_error_msg("invalid greylist wait time"
+					      " \"-G %s\"", optarg);
+				break;
+			}
+			new_white = dcc_get_secs(rest, &rest,
+						 new_window, MAX_GREY_WHITE,
+						 max(new_window, grey_white));
+			if (new_white < 0 || *rest != '\0') {
+				dcc_error_msg("invalid greylist whitelist time"
+					      " \"-G %s\"", optarg);
+				break;
+			}
+			grey_embargo = new_embargo;
+			grey_window = new_window;
+			grey_white = new_white;
+			break;
+
+		case 't':		/* obsolete */
+			break;
+
+		case 'W':
+			p = optarg;
+			if (*p == '\0') {
+				dcc_error_msg("unrecognized"
+					      " \"-W %s\"", optarg);
+				break;
+			}
+			d1 = wfix_quiet_rate;
+			if (*p != '\0' && *p != ',') {
+				d1 = strtod(p, &p);
+				if ((*p != '\0' && *p != ',')
+				    || d1 < 0.0 || d1 > 1000*1000.0) {
+					dcc_error_msg("bad quiet rate in"
+						      " \"-W %s\"", optarg);
+					break;
+				}
+			}
+			if (*p == ',')
+				++p;
+			d2 = wfix_rate_change;
+			if (*p != '\0' && *p != ',') {
+				d1 = strtod(p, &p);
+				if ((*p != '\0' && *p != ',')
+				    || d1 < 0.0 || d1 > 1000*1000.0) {
+					dcc_error_msg("bad rate change in"
+						      " \"-W %s\"", optarg);
+					break;
+				}
+			}
+			if (*p == ',')
+				++p;
+			l = wfix_size/(1024*1024);
+			if (*p != '\0') {
+				l = strtoul(p, &p, 10);
+				if ((*p != '\0' || l < DB_MIN_MIN_MBYTE
+				     || l > MAX_MAX_DB_MBYTE)
+				    && l != wfix_size) {
+					dcc_error_msg("bad database size in"
+						      " \"-W %s\"", optarg);
+					break;
+				}
+			}
+			wfix_quiet_rate = d1;
+			wfix_rate_change = d2;
+			if (wfix_size/(1024*1024) != l) {
+				wfix_size = ((DB_PTR)l)*(1024*1024);
+				wfix_size_set = 1;
+			}
+			break;
+
+		case 'K':
+			if (!strcasecmp(optarg, "all")) {
+				reset_new_nokeep_cks = -1;
+				break;
+			}
+			if (!CLITCMP(optarg, "no-")) {
+				optarg += LITZ("no-");
+				i = 0;
+			} else {
+				i = 1;
+			}
+			type = dcc_str2type_db(optarg, -1);
+			if (type == DCC_CK_INVALID) {
+				dcc_error_msg("bad checksum type in"
+					      " \"-K %s\"", optarg);
+				break;
+			}
+			if (i)
+				DB_SET_NOKEEP(reset_new_nokeep_cks, type);
+			else
+				DB_SET_NOKEEP(set_new_nokeep_cks, type);
+			break;
+
+		case 'T':
+			if (!strcasecmp(optarg, "ADMN")) {
+				dccd_tracemask |= DCC_TRACE_ADMN_BIT;
+			} else if (!strcasecmp(optarg, "ANON")) {
+				dccd_tracemask |= DCC_TRACE_ANON_BIT;
+			} else if (!strcasecmp(optarg, "CLNT")) {
+				dccd_tracemask |= DCC_TRACE_CLNT_BIT;
+			} else if (!strcasecmp(optarg, "RLIM")) {
+				dccd_tracemask |= DCC_TRACE_RLIM_BIT;
+			} else if (!strcasecmp(optarg, "QUERY")) {
+				dccd_tracemask |= DCC_TRACE_QUERY_BIT;
+			} else if (!strcasecmp(optarg, "RIDC")) {
+				dccd_tracemask |= DCC_TRACE_RIDC_BIT;
+			} else if (!strcasecmp(optarg, "FLOOD")) {
+				dccd_tracemask |= DCC_TRACE_FLOD_BIT;
+			} else if (!strcasecmp(optarg, "FLOOD2")) {
+				dccd_tracemask |= DCC_TRACE_FLOD2_BIT;
+			} else if (!strcasecmp(optarg, "IDS")) {
+				dccd_tracemask |= DCC_TRACE_IDS_BIT;
+			} else if (!strcasecmp(optarg, "BL")) {
+				dccd_tracemask |= DCC_TRACE_BL_BIT;
+			} else if (!strcasecmp(optarg, "DB")) {
+				dccd_tracemask |= DCC_TRACE_DB_BIT;
+			} else if (!strcasecmp(optarg, "WLIST")) {
+				dccd_tracemask |= DCC_TRACE_WLIST_BIT;
+			} else {
+				dcc_error_msg("invalid trace mode \"%s\"",
+					      optarg);
+			}
+			break;
+
+		case 'u':
+			i = parse_dccd_delay(dcc_emsg, &anon_delay_us,
+					     &anon_delay_inflate, optarg,
+					     0, 0);
+			if (!i) {
+				dcc_error_msg("%s", dcc_emsg);
+			} else if (i == 2) {
+				anon_off = 1;
+			} else {
+				anon_off = 0;
+			}
+			break;
+
+		case 'C':
+			if (*optarg == '\0') {
+				dcc_error_msg("no path to dbclean \"-C %s\"",
+					      optarg);
+				break;
+			}
+			/* capture the path to the dbclean program */
+			dbclean = optarg;
+			/* capture any args following the program */
+			for (p = strpbrk(optarg, DCC_WHITESPACE);
+			     p != 0;
+			     p = strpbrk(p, DCC_WHITESPACE)) {
+				*p++ = '\0';
+				p += strspn(p, DCC_WHITESPACE);
+				if (*p == '\0')
+					break;
+				add_dbclean_arg(p);
+			}
+			break;
+
+		case 'L':
+			if (dcc_parse_log_opt(optarg)) {
+				add_dbclean_arg("-L");
+				add_dbclean_arg(optarg);
+			}
+			break;
+
+		case 'R':
+			rest = parse_rl_rate(&rl_sub_rate, -1.0,
+					     "RL_SUB", optarg);
+			rest = parse_rl_rate(&rl_anon_rate, -1.0,
+					     "RL_ANON", rest);
+			rest = parse_rl_rate(&rl_all_anon_rate, -1.0,
+					     "RL_ALL_ANON", rest);
+			rest = parse_rl_rate(&rl_bugs_rate, -1.0,
+					     "RL_BUGS", rest);
+			break;
+
+		default:
+			usage(optopt2str(optopt));
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	if (argc != 0)
+		usage(argv[0]);
+
+	if (my_srvr_id == DCC_ID_INVALID) {
+		if (print_version)
+			exit(EX_OK);
+		dcc_logbad(EX_USAGE, "server-ID not set with -i");
+	}
+
+	if (grey_on) {
+		anon_off = 1;
+		dccd_tracemask |= DCC_TRACE_IDS_BIT;
+	}
+
+	/* parse addresses after we know whether -6 was among the args */
+	for (aarg = &aargs[0]; aarg <= LAST(aargs) && *aarg; ++aarg) {
+		char hostname[DCC_MAXDOMAINLEN];
+
+		addr_str = *aarg;
+		rest = dcc_parse_nm_port(dcc_emsg, *aarg, 0,
+					 hostname, sizeof(hostname),
+					 &port, 0, 0, 0, 0);
+		if (!rest) {
+			dcc_error_msg("%s", dcc_emsg);
+			continue;
+		}
+		rest += strspn(rest, DCC_WHITESPACE);
+		if (*rest != '\0')
+			dcc_error_msg("unrecognized port number in"
+				      "\"-a %s\"", *aarg);
+		if (hostname[0] == '\0') {
+			PORT_LIST *pport = dcc_malloc(sizeof(*pport));
+			pport->port = port;
+			pport->fwd = ports;
+			ports = pport;
+			continue;
+		}
+		if (!strcmp(hostname, "@")) {
+			++addr_str;	/* "" but not a const */
+			add_srvr_soc(SRVR_SOC_ADDR, AF_UNSPEC, 0, port);
+			continue;
+		}
+		dcc_host_lock();
+		if (!dcc_get_host(hostname, use_ipv6 ? 2 : 0, &error)) {
+			dcc_host_unlock();
+			dcc_error_msg("%s: %s",
+				      *aarg, DCC_HSTRERROR(error));
+			continue;
+		}
+		for (sup = dcc_hostaddrs; sup < dcc_hostaddrs_end; ++sup) {
+			if (sup->sa.sa_family == AF_INET)
+				add_srvr_soc(SRVR_SOC_ADDR, AF_INET,
+					     &sup->ipv4.sin_addr, port);
+			else
+				add_srvr_soc(SRVR_SOC_ADDR, AF_INET6,
+					     &sup->ipv6.sin6_addr, port);
+		}
+		dcc_host_unlock();
+	}
+	if (addr_str) {
+		/* tell dbclean about one "-a addr" */
+		add_dbclean_arg("-a");
+		add_dbclean_arg(addr_str);
+	}
+
+	dcc_clnt_unthread_init();
+	if (!dcc_cdhome(dcc_emsg, homedir, 0))
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+
+	i = check_load_ids(1);
+	if (i < 0)
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+	else if (!i)
+		dcc_error_msg("%s", dcc_emsg);
+
+	if (!def_port)
+		def_port = DCC_GREY2PORT(grey_on);
+	if (!srvr_socs && !ports) {
+		ports = dcc_malloc(sizeof(*ports));
+		ports->fwd = 0;
+		ports->port = def_port;
+	}
+	get_if_changes(db_debug != 0);
+
+	/* make initial attempt to open the server UDP sockets
+	 * This also sets use_ipv6 to 0 or 1 if it is still 2 */
+	if (open_srvr_socs(45) <= 0)
+		dcc_logbad(EX_OSERR, "failed to open any server sockets");
+
+	add_dbclean_flag(use_ipv6 == 0 ? '4' : '6');
+
+	if (background) {
+		if (0 > daemon(1, 0))
+			dcc_logbad(EX_OSERR, "daemon(): %s", ERROR_STR());
+	}
+
+	if (!background)
+		signal(SIGHUP, sigterm);    /* SIGHUP fatal during debugging */
+	else
+		signal(SIGHUP, sighup);	/* speed configuration check */
+	signal(SIGTERM, sigterm);
+	signal(SIGINT, sigterm);
+	signal(SIGPIPE, SIG_IGN);
+#ifdef SIGXFSZ
+	signal(SIGXFSZ, SIG_IGN);
+#endif
+
+	atexit(stop_children);
+
+	gettimeofday(&db_time, 0);
+	wake_time = db_time;
+
+	flod_mmap_path_set();
+
+	/* open the database, and try once to fix it */
+	if (!dccd_db_open(DB_OPEN_LOCK_NOWAIT)) {
+		dcc_error_msg("%s", dcc_emsg);
+		run_dbclean("SRbad", "database initially broken");
+		check_dbclean(0);	/* stall until dbclean finishes */
+		if (!dccd_db_open(DB_OPEN_LOCK_NOWAIT)) {
+			dcc_error_msg("%s", dcc_emsg);
+			dcc_logbad(EX_NOINPUT,
+				   "could not start database %s", db_nm);
+		}
+	}
+	/* do not start if dbclean is running */
+	if (!lock_dbclean(dcc_emsg, db_nm))
+		dcc_logbad(dcc_ex_code, "%s: dbclean already running?",
+			   dcc_emsg);
+	unlock_dbclean();
+
+	flod_trace_gen = db_time.tv_sec;
+	host_id_next = db_time.tv_sec + DCC_SRVR_ID_SECS_ST;
+	check_blacklist_file();
+	flods_init();
+	clients_load();
+	stats_clear();
+	if (flod_mmaps != 0
+	    && flod_mmaps->dccd_stats.reset.tv_sec != 0) {
+		memcpy(&dccd_stats, &flod_mmaps->dccd_stats,
+		       sizeof(dccd_stats));
+	}
+	dcc_trace_msg(DCC_VERSION" listening to port %d  %s  %s",
+		      ntohs(srvr_socs->arg_port),
+		      dcc_homedir, db_window_size_str);
+
+	recv_job();
+}
+
+
+
+static SRVR_SOC *			/* 0 or new entry */
+add_srvr_soc(u_char flags,
+	     int family,		/* AF_UNSPEC or 0 if addrp==0 */
+	     const void *addrp,		/* 0, *in_addr, or *in6_addr */
+	     u_int16_t port)
+{
+	DCC_SOCKU su;
+	SRVR_SOC *sp;
+
+	dcc_mk_su(&su, family, addrp, port);
+	for (sp = srvr_socs; sp; sp = sp->fwd) {
+		if (sp->arg_family == family
+		    && sp->arg_port == port
+		    && !memcmp(&sp->arg_addr, addrp,
+			       ((family == AF_INET)
+				? sizeof(sp->arg_addr.in4)
+				: sizeof(sp->arg_addr.in6))))
+			return sp;
+	}
+
+	sp = dcc_malloc(sizeof(*sp));
+	memset(sp, 0, sizeof(*sp));
+	sp->flags = flags;
+	sp->udp = -1;
+	sp->listen = -1;
+	sp->su = su;
+	sp->arg_family = family;
+	if (addrp)
+		memcpy(&sp->arg_addr, addrp,
+		       ((family == AF_INET)
+			? sizeof(sp->arg_addr.in4)
+			: sizeof(sp->arg_addr.in6)));
+	sp->arg_port = port;
+
+	*srvr_socs_end = sp;
+	srvr_socs_end = &sp->fwd;
+
+	return sp;
+}
+
+
+
+static int				/* # of sockets opened */
+open_srvr_socs(int retry_secs)
+{
+	static u_char srvr_rcvbuf_set = 0;
+	int *retry_secsp;
+	u_char family;
+	SRVR_SOC *sp;
+	int i;
+	int num_socs = 0;
+
+	if (stopint)
+		return -1;
+
+	retry_secsp = retry_secs ? &retry_secs : 0;
+
+	for (sp = srvr_socs; sp; sp = sp->fwd) {
+		if (sp->udp >= 0) {
+			++num_socs;
+			continue;
+		}
+
+		/* resolve default port number if we finally know it */
+		if (!sp->arg_port)
+			sp->arg_port = def_port;
+
+		family = sp->arg_family;
+
+		/* create the UDP socket
+		 * If we are using INADDR_ANY
+		 * and do not yet know if IPv6 works, just try it */
+		if (family == AF_UNSPEC
+		    && use_ipv6 == 2) {
+			dcc_mk_su(&sp->su, AF_INET6,
+				  &sp->arg_addr, sp->arg_port);
+			i = dcc_udp_bind(dcc_emsg, &sp->udp,
+					 &sp->su, retry_secsp);
+			if (i == 0) {
+				dcc_error_msg("%s", dcc_emsg);
+				/* still don't know about IPv6 */
+				continue;
+			}
+			if (i > 0) {
+				/* we finished an INADDR_ANY socket
+				 * and learned that IPv6 works */
+				use_ipv6 = 1;
+				continue;
+			}
+			/* we know or guess that IPv6 does not work */
+			use_ipv6 = 0;
+		}
+
+		if (family == AF_UNSPEC) {
+			/* using INADDR_ANY but now know whether IPv6 works */
+			family = use_ipv6 ? AF_INET6 : AF_INET;
+		} else if (family == AF_INET6 && use_ipv6 == 2) {
+			/* don't know if IPv6 works but have an IPv6 address */
+			use_ipv6 = 1;
+		}
+
+		dcc_mk_su(&sp->su, family, &sp->arg_addr, sp->arg_port);
+		if (0 >= dcc_udp_bind(dcc_emsg, &sp->udp,
+				      &sp->su, retry_secsp)) {
+			dcc_error_msg("%s", dcc_emsg);
+			continue;
+		}
+
+		/* set socket receive buffer size as large as possible */
+		for (;;) {
+			if (!setsockopt(sp->udp, SOL_SOCKET, SO_RCVBUF,
+					&srvr_rcvbuf, sizeof(srvr_rcvbuf)))
+				break;
+			if (srvr_rcvbuf_set || srvr_rcvbuf <= 4096) {
+				dcc_error_msg("setsockopt(%s,SO_RCVBUF=%d): %s",
+					      dcc_su2str_err(&sp->su),
+					      srvr_rcvbuf,
+					      ERROR_STR());
+				break;
+			}
+			srvr_rcvbuf -= 4096;
+		}
+		srvr_rcvbuf_set = 1;
+
+		++num_socs;
+	}
+
+	/* Finally decide the IPv6 issue if we found no sign of IPv6 */
+	if (use_ipv6 == 2)
+		use_ipv6 = 0;
+
+	return num_socs;
+}
+
+
+
+/* get ready to bind to all local IP addreses */
+static u_char				/* 1=added an interface */
+add_ifs(u_char not_quiet)
+{
+	u_char added;
+	SRVR_SOC *sp;
+	PORT_LIST *pport;
+#ifdef HAVE_GETIFADDRS
+	struct ifaddrs *ifap0, *ifap;
+	int num_ifs;
+#endif
+
+	if (!ports)
+		return 0;
+
+	added = 0;
+
+#ifdef HAVE_GETIFADDRS
+	if (0 > getifaddrs(&ifap0)) {
+		dcc_error_msg("getifaddrs(): %s", ERROR_STR());
+		ifap0 = 0;
+	}
+
+	num_ifs = 0;
+	for (pport = ports; pport; pport = pport->fwd) {
+		const SRVR_SOC *listener = 0;
+
+		for (ifap = ifap0; ifap; ifap = ifap->ifa_next) {
+			if (!(ifap->ifa_flags & IFF_UP))
+				continue;
+			if (!ifap->ifa_addr)
+				continue;
+			switch (ifap->ifa_addr->sa_family) {
+			case AF_INET:
+				++num_ifs;
+				sp = add_srvr_soc(SRVR_SOC_IF | SRVR_SOC_NEW,
+						  ifap->ifa_addr->sa_family,
+						  &((struct sockaddr_in *)ifap
+						    ->ifa_addr)->sin_addr,
+						  pport->port);
+				break;
+			case AF_INET6:
+				if (use_ipv6 == 0)
+					continue;
+				if (use_ipv6 == 2)
+					use_ipv6 = 1;
+				++num_ifs;
+				sp = add_srvr_soc(SRVR_SOC_IF | SRVR_SOC_NEW,
+						  ifap->ifa_addr->sa_family,
+						  &((struct sockaddr_in6*)ifap
+						    ->ifa_addr)->sin6_addr,
+						  pport->port);
+				break;
+			default:
+				continue;
+			}
+			if (sp->flags & SRVR_SOC_NEW) {
+				added = 1;
+				if (not_quiet)
+					dcc_trace_msg("start listening on %s",
+						      dcc_su2str_err(&sp->su));
+			}
+			sp->flags &= ~(SRVR_SOC_MARK | SRVR_SOC_NEW);
+
+			/* interfaces can have duplicate addresses */
+			if (listener == sp)
+				continue;
+			if (!listener) {
+				listener = sp;
+				if (!(sp->flags & SRVR_SOC_LISTEN)) {
+					sp->flags |= SRVR_SOC_LISTEN;
+					added = 1;
+				}
+			} else {
+				if (sp->flags & SRVR_SOC_LISTEN) {
+					sp->flags &= ~SRVR_SOC_LISTEN;
+					added = 1;
+				}
+			}
+		}
+	}
+#ifdef HAVE_FREEIFADDRS
+	/* since this is done only a few times when HAVE_FREEIFADDRS is not
+	 * defined, don't worry if we cannot release the list of interfaces */
+	freeifaddrs(ifap0);
+#endif
+
+	if (num_ifs > 0)
+		return added;
+#endif /* HAVE_GETIFADDRS */
+
+	/* if we got no joy from getifaddrs(), use INADDR_ANY */
+	for (pport = ports; pport; pport = pport->fwd) {
+		sp = add_srvr_soc(SRVR_SOC_IF | SRVR_SOC_LISTEN | SRVR_SOC_NEW,
+				  AF_UNSPEC, 0, pport->port);
+		if (sp->flags & SRVR_SOC_NEW) {
+			added = 1;
+			if (not_quiet)
+				dcc_trace_msg("fallback listen on %s",
+					      dcc_su2str_err(&sp->su));
+		}
+		sp->flags &= ~(SRVR_SOC_MARK | SRVR_SOC_NEW);
+	}
+
+	return added;
+}
+
+
+
+/* deal with changes to network interfaces */
+static u_char				/* 1=something changed */
+get_if_changes(u_char not_quiet)
+{
+	SRVR_SOC *sp, **spp;
+	u_char changed;
+
+	for (sp = srvr_socs; sp; sp = sp->fwd) {
+		if (sp->flags & SRVR_SOC_IF)
+			sp->flags |= SRVR_SOC_MARK;
+	}
+
+	changed = add_ifs(not_quiet);
+
+	spp = &srvr_socs;
+	while ((sp = *spp) != 0) {
+		/* an interface recognized by add_srvr_soc() will have
+		 * its SRVR_SOC_MARK cleared */
+		if (!(sp->flags & SRVR_SOC_MARK)) {
+			spp = &sp->fwd;
+			continue;
+		}
+
+		/* forget interfaces that have disappeared */
+		dcc_trace_msg("stop listening on %s", dcc_su2str_err(&sp->su));
+		changed = 1;
+		if (srvr_socs_end == &sp->fwd)
+			srvr_socs_end = spp;
+		*spp = sp->fwd;
+		if (sp->udp >= 0)
+			close(sp->udp);
+		if (sp->listen >= 0)
+			close(sp->listen);
+		dcc_free(sp);
+	}
+
+	return changed;
+}
+
+
+
+static const char *
+parse_rl_rate(RL_RATE *rate, float penalty_secs,
+	      const char *label, const char *arg)
+{
+	char *p;
+	int per_sec, hi;
+
+	if (penalty_secs >= 0.0)
+		rate->penalty_secs = penalty_secs;
+
+	if (*arg == '\0')
+		return arg;
+
+	if (*arg == ',')
+		return ++arg;
+
+	per_sec = strtod(arg, &p) * RL_SCALE;
+	hi = per_sec*RL_AVG_SECS;
+	if ((*p != '\0' && *p != ',')
+	    || hi < RL_SCALE || per_sec > RL_MAX_CREDITS) {
+		dcc_error_msg("invalid %s value in \"%s\"",
+			      label, arg);
+		return "";
+	}
+
+	/* maximum events/second * RL_SCALE */
+	rate->per_sec = per_sec;
+
+	/* maximum allowed accumulated credits */
+	rate->hi = hi;
+
+	/* minimum credit account balance */
+	rate->lo = -per_sec * rate->penalty_secs;
+
+	return (*p == ',') ? p+1 : p;
+}
+
+
+
+static void
+add_dbclean_flag(char flag)
+{
+	if (dbclean_flags_len >= MAX_DBCLEAN_FLAGS)
+		dcc_logbad(EX_SOFTWARE, "too many flags for dbclean");
+	dbclean_flags[dbclean_flags_len++] = flag;
+}
+
+
+
+static void
+add_dbclean_arg(const char *arg)
+{
+	int i;
+
+	if (dbclean_argc >= DIM(dbclean_argv)-2)
+		dcc_logbad(EX_SOFTWARE, "too many args for dbclean");
+	dbclean_argv[dbclean_argc++] = arg;
+	i = snprintf(dbclean_args_str+dbclean_args_str_len,
+		     sizeof(dbclean_args_str)-dbclean_args_str_len,
+		     " %s", arg);
+	dbclean_args_str_len += i;
+	if (dbclean_args_str_len >= ISZ(dbclean_args_str)-2)
+		dcc_logbad(EX_SOFTWARE, "too many args for dbclean");
+}
+
+
+
+/* check effort to repair database */
+static void
+check_dbclean(int options)
+{
+	int status;
+	pid_t pid;
+	u_char ok;
+
+	if (dbclean_pid < 0)
+		return;
+
+	pid = waitpid(dbclean_pid, &status, options);
+	if (pid != dbclean_pid)
+		return;
+
+	dbclean_pid = -1;
+
+	/* do not try failing dbclean too often */
+#if defined(WIFEXITED) && defined(WEXITSTATUS) && defined(WTERMSIG) && defined(WIFSIGNALED)
+	ok = 1;
+	if (WIFSIGNALED(status)) {
+		dcc_error_msg("dbclean exited with signal %d",
+			      WTERMSIG(status));
+		ok = 0;
+	} else if (WIFEXITED(status)) {
+		status = WEXITSTATUS(status);
+		if (status != EX_OK) {
+			if (status > 100 && status < 130)
+				dcc_error_msg("dbclean stopped after signal %d",
+					      status-100);
+			else
+				dcc_error_msg("dbclean exited with status %d",
+					      status);
+			ok = 0;
+		}
+	}
+#else
+	ok = (status == EX_OK);
+#endif
+	if (ok) {
+		dbclean_failed = 0;
+		dbclean_limit_secs = DBCLEAN_LIMIT_SECS;
+	} else {
+		dbclean_failed = db_time.tv_sec;
+		dbclean_limit_secs *= 2;
+		if (dbclean_limit_secs > DEL_DBCLEAN_SECS)
+			dbclean_limit_secs = DEL_DBCLEAN_SECS;
+	}
+
+	/* don't restart dbclean until after it has stopped running
+	 * and cooled for a while */
+	dbclean_limit = db_time.tv_sec + dbclean_limit_secs;
+}
+
+
+
+/* try to repair the database */
+static void
+run_dbclean(const char *mode,		/* combination of '', R, S, and W */
+	    const char *reason)
+{
+	int i;
+
+	check_dbclean(0);		/* wait until previous ends */
+
+	wfix_later(WFIX_RECHECK_SECS);
+
+	i = snprintf(&dbclean_flags[dbclean_flags_len],
+		     ISZ(dbclean_flags)-dbclean_flags_len, "%s",
+		     mode);
+	if (i+dbclean_flags_len >= ISZ(dbclean_flags))
+		dcc_logbad(EX_SOFTWARE, "too many flags for dbclean");
+
+	dbclean_pid = fork();
+	if (dbclean_pid < 0) {
+		dcc_error_msg("dbclean fork(): %s", ERROR_STR());
+	} else if (dbclean_pid == 0) {
+		dcc_trace_msg("%s; starting `%s %s%s`",
+			      reason, dbclean, dbclean_flags, dbclean_args_str);
+		execv(dbclean, (char **)dbclean_argv);
+		dcc_error_msg("execv(%s %s%s): %s",
+			      dbclean, dbclean_flags, dbclean_args_str,
+			      ERROR_STR());
+		exit(-1);
+	}
+
+	need_del_dbclean = 0;
+	dbclean_limit = db_time.tv_sec + dbclean_limit_secs;
+}
+
+
+
+static void
+close_srvr_socs(void)
+{
+	SRVR_SOC *sp;
+
+	for (sp = srvr_socs; sp; sp = sp->fwd) {
+		if (sp->udp >= 0) {
+			close(sp->udp);
+			sp->udp = -1;
+		}
+		iflod_listen_close(sp);
+	}
+}
+
+
+
+/* close files and otherwise clean up after being forked as a helper */
+void
+after_fork(void)
+{
+	IFLOD_INFO *ifp;
+	OFLOD_INFO *ofp;
+
+	resolve_hosts_pid = -1;
+	dbclean_pid = -1;
+
+	close_srvr_socs();
+	for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
+		if (ifp->soc >= 0)
+			close(ifp->soc);
+	}
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		if (ofp->soc >= 0)
+			close(ofp->soc);
+	}
+
+	signal(SIGHUP, SIG_DFL);
+	signal(SIGTERM, SIG_DFL);
+	signal(SIGINT, SIG_DFL);
+	signal(SIGPIPE, SIG_DFL);
+#ifdef SIGXFSZ
+	signal(SIGXFSZ, SIG_DFL);
+#endif
+}
+
+
+
+/* do not worry about cleaning to fix a window overflow for a while */
+static void
+wfix_later(time_t delay)
+{
+	dbclean_wfix_state = WFIX_DELAY;
+	dbclean_wfix_secs = db_time.tv_sec + delay;
+}
+
+
+
+static double
+wfix_measure(u_char force)
+{
+	double secs;
+
+	secs = db_time.tv_sec - wfix_check_start;
+	if (force || secs <= 0.0 || secs > WFIX_MEASURE_SECS*2
+	    || total_ops < wfix_ops) {
+		wfix_check_start = db_time.tv_sec;
+		dbclean_wfix_secs = db_time.tv_sec + WFIX_MEASURE_SECS;
+		wfix_ops = total_ops;
+		return -1.0;
+	}
+	return (total_ops - wfix_ops) / secs;
+}
+
+
+
+static u_char				/* 1=need dbclean now */
+wfix(char *reason, u_int reason_len)
+{
+	double rate;
+	struct timeval sn;
+	time_t next_clean;
+
+	/* stop everything if dbclean is running */
+	if (db_minimum_map || wfix_quiet_rate <= 0.0) {
+		wfix_later(WFIX_RECHECK_SECS);
+		return 0;
+	}
+
+	switch (dbclean_wfix_state) {
+	case WFIX_DELAY:		/* just checking */
+		if (!DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS))
+			return 0;
+
+		/* no quick cleaning soon after the database was created,
+		 * cleaned or repaired */
+		dcc_ts2timeval(&sn, &db_parms.sn);
+		if (sn.tv_sec > db_time.tv_sec)
+			sn.tv_sec = 0;
+		if (sn.tv_sec < dbclean_failed
+		    && dbclean_failed <= db_time.tv_sec)
+			sn.tv_sec = dbclean_failed;
+		if (sn.tv_sec <= db_time.tv_sec) {
+			dbclean_wfix_secs = sn.tv_sec + WFIX_POST_CLEAN_SECS;
+			if (!DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS))
+				return 0;
+		}
+
+		/* check later if dbclean might run soon */
+		next_clean = clean_fake_secs;
+		if (db_time.tv_sec >= next_clean - WFIX_PRE_CLEAN_SECS) {
+			dbclean_wfix_secs = next_clean + WFIX_POST_CLEAN_SECS;
+			if (!DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS))
+				return 0;
+		}
+		next_clean = clean_last_secs + 24*60*60;
+		if (db_time.tv_sec >= next_clean - WFIX_PRE_CLEAN_SECS) {
+			dbclean_wfix_secs = next_clean + WFIX_POST_CLEAN_SECS;
+			if (!DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS))
+				return 0;
+		}
+
+		/* check later if the database is not too large now */
+		if (db_fsize + db_hash_fsize
+		    < (wfix_size_set ? wfix_size : db_max_rss)) {
+			wfix_later(WFIX_RECHECK_SECS);
+			break;
+		}
+		/* the database is too big, so try to chase the clients
+		 * to the other DCC server (if any) */
+		dbclean_wfix_state = WFIX_BUSY;
+		wfix_measure(1);
+		break;
+
+	case WFIX_BUSY:
+		rate = wfix_measure(0);
+		if (rate >= 0.0) {
+			wfix_busy_rate = rate;
+			dbclean_wfix_state = WFIX_QUIET;
+			dbclean_wfix_secs = db_time.tv_sec + WFIX_QUIET_SECS;
+		}
+		break;
+
+	case WFIX_QUIET:		/* waiting for clients to flee */
+		dbclean_wfix_state = WFIX_CHECK;
+		wfix_measure(1);
+		break;
+
+	case WFIX_CHECK:		/* counting clients */
+		rate = wfix_measure(0);
+		if (rate < 0.0)
+			break;
+		if (rate <= wfix_quiet_rate
+		    || rate <= wfix_busy_rate * wfix_rate_change) {
+			snprintf(reason, reason_len,
+				 "database size "L_DPAT">"L_DPAT
+				 "; load changed from %0.1f to %0.1f",
+				 db_fsize + db_hash_fsize,
+				 wfix_size_set ? wfix_size : db_max_rss,
+				 wfix_busy_rate, rate);
+			return 1;
+		}
+		/* Other DCC servers did not take over the load. Maybe later */
+		if (db_debug)
+			dcc_trace_msg("database size "L_DPAT" > "L_DPAT
+				      ", but load changed from %0.1f to %0.1f",
+				      db_fsize + db_hash_fsize,
+				      wfix_size_set ? wfix_size : db_max_rss,
+				      wfix_busy_rate, rate);
+		wfix_later(WFIX_RECHECK_SECS);
+		break;
+	}
+
+	return 0;
+}
+
+
+
+/* check for changes or other interesting events when the flood timer expires */
+static time_t				/* microseconds to wait */
+check_changes(void)
+{
+	static time_t misc_timer, files_timer;
+	time_t secs;
+	DB_HADDR hash_free;
+	const char *mode;
+	const char *reason;
+	char reason_buf[100];
+
+	reason = 0;
+	mode = 0;
+
+	if (db_failed_line) {
+		snprintf(reason_buf, sizeof(reason_buf),
+			 "database broken at line %d in %s "DCC_VERSION,
+			 db_failed_line, db_failed_file);
+		reason = reason_buf;
+		mode = "Rbad";
+
+	} else if ((hash_free
+		    = db_hash_len - db_hash_used) < MIN_CLEAN_HASH_ENTRIES
+		   || hash_free < db_hash_len/20) {
+		/* try to expand the hash table when there are only
+		 * a few free slots or the load factor rises above .95 */
+		if (hash_free < MIN_HASH_ENTRIES)
+			snprintf(reason_buf, sizeof(reason_buf),
+				 "%d free hash entries",
+				 hash_free);
+		else
+			snprintf(reason_buf, sizeof(reason_buf),
+				 "%d free hash entries among %d total",
+				 hash_free, db_hash_len);
+		reason = reason_buf;
+		mode = "Rhash";
+
+	} else {
+		/* check nothing else if it is not yet time */
+		secs = next_flods_ck - db_time.tv_sec;
+		if (secs > 0 && secs <= flods_ck_secs)
+			return secs * DCC_US;
+	}
+
+	next_flods_ck = db_time.tv_sec + flods_ck_secs;
+
+	/* do not make some checks too often even when flood checking is
+	 * being rushed */
+	if (DB_IS_TIME(misc_timer, MISC_CK_SECS)) {
+		misc_timer = db_time.tv_sec + MISC_CK_SECS;
+
+		/* shrink our memory footprint
+		 * if we are so idle that we don't have anything to flush */
+		if (db_need_flush_secs == 0) {
+			if (db_unload(0, 1) == 1) {
+				rel_db_states();
+				db_unload(0, 1);
+			}
+		}
+
+		if (DB_IS_TIME(files_timer, 30)) {
+			files_timer = db_time.tv_sec + 30;
+
+			if (0 >= check_load_ids(0))
+				dcc_error_msg("check/reload: %s", dcc_emsg);
+
+			check_blacklist_file();
+
+#ifdef HAVE_GETIFADDRS
+			/* check for network interface changes, but only
+			 * if we can release the result of getifaddrs() */
+			if (get_if_changes(1)) {
+				int socs = open_srvr_socs(0);
+				if (!socs)
+					bad_stop("no server sockets");
+				else if (socs > 0)
+					flods_restart("new network interfaces",
+						      1);
+			}
+#endif
+		}
+
+		if (DB_IS_TIME(need_clients_save, CLIENTS_SAVE_SECS))
+			clients_save();
+
+		/* sound a claim to our server-ID if the database is locked */
+		if (DB_IS_TIME(host_id_next, DCC_SRVR_ID_SECS)
+		    && DB_IS_LOCKED()
+		    &&!db_failed_line ) {
+			refresh_srvr_rcd(host_id_sum, my_srvr_id,
+					 "adding server-ID claim");
+
+			if (!db_failed_line ) {
+				host_id_last = db_time.tv_sec;
+				host_id_next = host_id_last + DCC_SRVR_ID_SECS;
+			}
+		}
+	}
+
+	/* note when hash expansion finishes and collect a zombie */
+	check_dbclean(WNOHANG);
+
+	if (reason != 0) {
+		;
+
+	} else if (DB_IS_TIME(clean_fake_secs, 3*24*60*60)
+		   && (!db_minimum_map
+		       || DB_IS_TIME(clean_last_secs+2*24*60*60, 4*24*60*60))) {
+		reason = "work around broken cron job";
+		mode = "Rcron";
+
+	} else if (need_del_dbclean != 0
+		   && DB_IS_TIME(del_dbclean_next, DEL_DBCLEAN_SECS)) {
+		/* the deletion of a report needs to be cleaned up */
+		reason = need_del_dbclean;
+		mode = "Rdel";
+
+	} else if (DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS)
+		   && wfix(reason_buf, sizeof(reason_buf))) {
+		reason = reason_buf;
+		mode = "Rquick";
+	}
+
+	if (reason) {
+		if (!DB_IS_TIME(dbclean_limit, dbclean_limit_secs)) {
+			if (next_flods_ck > dbclean_limit)
+				next_flods_ck = dbclean_limit;
+		} else if (dbclean_pid < 0) {
+			run_dbclean(mode, reason);
+		} else {
+			RUSH_NEXT_FLODS_CK();
+		}
+	} else {
+		flods_ck(0);
+	}
+
+	/* we probably delayed */
+	gettimeofday(&db_time, 0);
+
+	secs = next_flods_ck - db_time.tv_sec;
+	return secs >= 0 ? secs*DCC_US : 0;
+}
+
+
+
+static void NRATTRIB
+recv_job(void)
+{
+	fd_set rfds, *prfds, wfds, *pwfds;
+#	define PFD_SET(_fd0,_fds) {int _fd = _fd0;	    \
+		p##_fds = &_fds; FD_SET(_fd,p##_fds);	    \
+		if (max_fd < _fd) max_fd = _fd;}
+	int max_fd, nfds;
+	IFLOD_INFO *ifp;
+	OFLOD_INFO *ofp;
+	struct timeval delay;
+	time_t delay_us, slept_us, was_too_busy, us;
+	struct timeval iflods_read, busy_time, extra_time;
+	u_char db_has_failed;
+#	define QUANTUM (DCC_US/10)	/* try to use only ~50% of the system */
+	SRVR_SOC *sp;
+	QUEUE *q;
+	int bad_select;
+	u_char worked;
+	int fd, i;
+
+	bad_select = 3;
+	was_too_busy = 0;
+	gettimeofday(&db_time, 0);
+	iflods_read = db_time;
+	busy_time = db_time;
+	extra_time = db_time;
+	delay_us = flods_ck_secs*DCC_US;
+	db_has_failed = 0;
+	for (;;) {
+		dcc_emsg[0] = '\0';
+
+		if (stopint) {
+			flods_ck_secs = SHUTDOWN_DELAY;
+			if (flods_off < 100000) {
+				flods_off = 100000;
+				flods_stop("server stopping", 0);
+				check_dbclean(WNOHANG);
+				if (dbclean_pid > 0) {
+					kill(dbclean_pid, SIGINT);
+					usleep(100*1000);
+				}
+				/* get started flushing the database while
+				 * we wait for flooding to stop */
+				rel_db_states();
+				db_minimum_map = 1;
+				db_unload(0, 2);
+			}
+			/* get serious when the floods have stopped */
+			if (oflods.total == 0) {
+				if (stopint < 0)
+					dccd_quit(0, "gracefully stopping%s",
+						  stop_mode == 1
+						  ? " for reboot"
+						  : stop_mode == 2
+						  ? " cleanly"
+						  : "");
+				dccd_quit(stopint | 128,
+					  "exiting with signal %d", stopint);
+			}
+		}
+
+		if (db_has_failed != db_failed_line) {
+			db_has_failed = db_failed_line;
+			if (db_failed_line) {
+				++flods_off;
+				flods_stop("database corrupt", 1);
+				rel_db_states();
+				db_unload(0, 2);
+				db_unlock();
+			}
+		}
+
+		FD_ZERO(&rfds);
+		prfds = 0;
+		FD_ZERO(&wfds);
+		pwfds = 0;
+		max_fd = -1;
+
+		/* look for client requests */
+		for (sp = srvr_socs; sp; sp = sp->fwd) {
+			if (sp->udp >= 0)
+				PFD_SET(sp->udp, rfds);
+		}
+
+		if (was_too_busy > 0)
+			was_too_busy = QUANTUM - tv_diff2us(&db_time,
+							&busy_time);
+		if (was_too_busy > 0
+		    && (tv_diff2us(&db_time, &extra_time)
+			< min(30, min(KEEPALIVE_IN, KEEPALIVE_OUT)/2))
+		    && FLODS_OK()) {
+			/* if we have been too busy,
+			 * then do nothing extra for a while */
+			if (delay_us > was_too_busy)
+				delay_us = was_too_busy;
+		} else {
+			extra_time = db_time;
+
+			/* Accept new incoming flood connections
+			 * if flooding is on
+			 * and we don't already have too many floods. */
+			if (iflods.open < DCCD_MAX_FLOODS) {
+				for (sp = srvr_socs; sp; sp = sp->fwd) {
+					if (sp->listen >= 0)
+					    PFD_SET(sp->listen, rfds);
+				}
+			}
+
+			/* pump floods out */
+			for (ofp = oflods.infos, i = 0;
+			     i < oflods.open;
+			     ++ofp) {
+				if (ofp->soc < 0)
+					continue;
+				++i;
+				if (ofp->flags & OFLOD_FG_CONNECTED) {
+					PFD_SET(ofp->soc, rfds);
+					if (!(ofp->flags & OFLOD_FG_EAGAIN)
+					    && ofp->obuf_len != 0)
+					    PFD_SET(ofp->soc, wfds);
+				} else {
+					PFD_SET(ofp->soc, wfds);
+				}
+			}
+
+			/* pump floods in */
+			for (ifp = iflods.infos, i = 0;
+			     i < iflods.open;
+			     ++ifp) {
+				if (ifp->soc < 0)
+					continue;
+				++i;
+				if (ifp->flags & IFLOD_FG_CONNECTED) {
+					PFD_SET(ifp->soc, rfds);
+				} else {
+					PFD_SET(ifp->soc, wfds);
+				}
+			}
+		}
+
+		/* push data to the disk */
+		if (db_need_flush_secs != 0) {
+			if (DB_IS_TIME(db_need_flush_secs,
+				       max(DB_URGENT_NEED_FLUSH_SECS,
+					   DB_NEED_FLUSH_SECS))) {
+				db_flush_needed();
+				gettimeofday(&db_time, 0);
+			}
+			us = db_need_flush_secs - db_time.tv_sec;
+			if (us >= 0) {
+				us *= DCC_US;
+				if (delay_us > us)
+					delay_us = us;
+			}
+		}
+
+		/* let dbclean run if we have run out of work
+		 * or if we have been holding the lock for 0.1 seconds*/
+		if (db_minimum_map
+		    && DB_IS_LOCKED()
+		    && (delay_us != 0
+			|| tv_diff2us(&db_time, &db_locked) >= DCC_US/10)) {
+			db_unlock();
+		}
+
+		/* delay until it is time to answer the oldest anonymous
+		 * request or something else that needs doing */
+		delay.tv_sec = delay_us/DCC_US;
+		delay.tv_usec = delay_us%DCC_US;
+		nfds = select(max_fd+1, prfds, pwfds, 0, &delay);
+		if (nfds < 0) {
+			if (errno != EINTR) {
+				if (--bad_select < 0)
+					bad_stop("give up after select(): %s",
+						 ERROR_STR());
+				else
+					dcc_error_msg("select(): %s",
+						      ERROR_STR());
+			}
+			/* ignore EINTR but recompute timers */
+			FD_ZERO(&rfds);
+			FD_ZERO(&wfds);
+		} else {
+			bad_select = 3;
+		}
+
+		gettimeofday(&wake_time, 0);
+		slept_us = tv_diff2us(&wake_time, &db_time);
+		if (slept_us >= 500) {
+			/* If select() paused for at least 0.5 millisecond,
+			 * then the waiting request has just now arrived. */
+			req_recv_time = wake_time;
+		} else {
+			/* If select() did not pause, then assume the waiting
+			 * requests arrived when we were half finished working
+			 * on flooding and other work besides ordinary requests
+			 * before calling select(). */
+			tv_add_us(&req_recv_time,
+				  tv_diff2us(&wake_time, &req_recv_time) / 2);
+		}
+		db_time = wake_time;
+
+		worked = 0;
+		for (sp = srvr_socs; sp; sp = sp->fwd) {
+			/* queue a new anonymous request
+			 * or answer a new authenticated request */
+			fd = sp->udp;
+			if (fd >= 0 && FD_ISSET(fd, &rfds)) {
+				--nfds;
+				worked = 1;
+				while (new_job(sp))
+					continue;
+			}
+
+			/* start a new incoming flood */
+			fd = sp->listen;
+			if (fd >= 0 && FD_ISSET(fd, &rfds)) {
+				--nfds;
+				worked = 1;
+				iflod_start(sp);
+			}
+		}
+		if (worked)
+			gettimeofday(&db_time, 0);
+		/* reset request receipt clock for next time */
+		req_recv_time = db_time;
+
+		/* Accept new flood data or start new SOCKS floods.
+		 * Listen to all peers to prevent starvation */
+		worked = 0;
+		for (ifp = iflods.infos, i = 0;
+		     nfds > 0 && i < iflods.open;
+		     ++ifp) {
+			if (ifp->soc < 0)
+				continue;
+			++i;
+			if (FD_ISSET(ifp->soc, &rfds)
+			    || FD_ISSET(ifp->soc, &wfds)) {
+				--nfds;
+				iflod_read(ifp);
+				worked = 1;
+			}
+		}
+		if (worked) {
+			gettimeofday(&db_time, 0);
+			iflods_read = db_time;
+		} else if (was_too_busy <= 0) {
+			/* if incoming floods have been quiet for
+			 * awhile, then assume flooding has caught up
+			 * after having been turned off */
+			if (tv_diff2us(&wake_time, &iflods_read) > 2*DCC_US)
+				iflods_ok_timer = db_time.tv_sec;
+		}
+
+		/* pump output flood data and receive confirmations
+		 * talk to all peers to prevent starvation */
+		worked = 0;
+		for (ofp = oflods.infos, i = 0;
+		     nfds > 0 && i < oflods.open;
+		     ++ofp) {
+			if (ofp->soc < 0)
+				continue;
+			++i;
+			if (FD_ISSET(ofp->soc, &rfds)) {
+				--nfds;
+				oflod_read(ofp);
+				if (ofp->soc < 0)
+					continue;
+			}
+			if (FD_ISSET(ofp->soc, &wfds)) {
+				--nfds;
+				oflod_write(ofp);
+				worked = 1;
+			}
+		}
+		if (worked)
+			gettimeofday(&db_time, 0);
+
+		/* process delayed jobs when their times arrive */
+		worked = 0;
+		for (;;) {
+			q = queue_head;
+			if (!q) {
+				delay_us = flods_ck_secs*DCC_US;
+				break;
+			}
+
+			/* decide whether this job's time has come
+			 * while defending against time jumps */
+			delay_us = tv_diff2us(&q->answer, &db_time);
+			if (delay_us >= 1000
+			    && delay_us <= DCC_MAX_RTT
+			    && !stopint)
+				break;  /* not yet time for next job */
+
+			queue_head = q->later;
+			if (queue_head)
+				queue_head->earlier = 0;
+			--queue_cur;
+			do_work(q);
+			worked = 1;
+			free_q(q);
+		}
+		if (worked)
+			gettimeofday(&db_time, 0);
+
+		/* check configuration changes etc. */
+		us = check_changes();
+		if (delay_us >= us)
+			delay_us = us;
+
+		us = tv_diff2us(&db_time, &wake_time);
+		if (us >= QUANTUM && !stopint) {
+			gettimeofday(&db_time, 0);
+			busy_time = db_time;
+			was_too_busy = QUANTUM;
+		}
+	}
+}
+
+
+
+static void
+add_queue(QUEUE *q)
+{
+	QUEUE *qnext, **qp;
+
+	TMSG1(QUERY, "received %s", op_id_ip(q));
+	if (!ck_clnt_id(q)) {
+		free_q(q);
+		return;
+	}
+
+	++total_ops;
+
+	/* immediately process requests from authenticated clients
+	 * if flooding is working */
+	if (q->delay_us == 0) {
+		do_work(q);
+		free_q(q);
+		return;
+	}
+
+	/* don't let the queue of delayed requests get too large */
+	if (queue_cur >= queue_max) {
+		clnt_msg(q, "drop excess queued %s", op_id_ip(q));
+		free_q(q);
+		return;
+	}
+
+	tv_add_us(&q->answer, q->delay_us);
+
+	/* add the new job to the queue */
+	++queue_cur;
+	qp = &queue_head;
+	for (;;) {
+		qnext = *qp;
+		if (!qnext) {
+			*qp = q;
+			break;
+		}
+		if (qnext->answer.tv_sec > q->answer.tv_sec
+		    || (qnext->answer.tv_sec == q->answer.tv_sec
+			&& qnext->answer.tv_usec > q->answer.tv_usec)) {
+			q->later = qnext;
+			qnext->earlier = q;
+			*qp = q;
+			break;
+		}
+		q->earlier = qnext;
+		qp = &qnext->later;
+	}
+}
+
+
+
+/* get a new job in a datagram */
+static u_char				/* 1=call again */
+new_job(SRVR_SOC *sp)
+{
+	QUEUE *q;
+	static struct iovec iov = {0, sizeof(q->pkt)};
+	static struct msghdr msg;
+	int i, j;
+
+	/* Find a free queue entry for the job.
+	 * Because we don't check for incoming jobs unless we think the
+	 * queue is not full, there must always be a free entry or
+	 * permission to make more entries. */
+	q = queue_free;
+	if (q) {
+		queue_free = q->later;
+	} else {
+		i = 16;
+		q = dcc_malloc(i * sizeof(*q));
+		if (!q)
+			dcc_logbad(EX_UNAVAILABLE,
+				   "malloc(%d queue entries) failed", i);
+		queue_max_cur += i;
+		/* put all but the last new queue entry on the free list */
+		while (--i > 0) {
+			q->later = queue_free;
+			queue_free = q;
+			++q;
+		}
+	}
+
+	memset(q, 0, sizeof(*q));
+	q->sp = sp;
+	iov.iov_base = (char *)&q->pkt;
+	msg.msg_name = (void *)&q->clnt_su;
+	msg.msg_namelen = sizeof(q->clnt_su);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	i = recvmsg(sp->udp, &msg, 0);
+	if (i < 0) {
+		/* ignore some results of ICMP unreachables for UDP
+		 * retransmissions seen on some platforms */
+		if (DCC_BLOCK_ERROR()) {
+			;
+		} else if (UNREACHABLE_ERRORS()) {
+			TMSG2(QUERY, "recvmsg(%s): %s",
+			      dcc_su2str_err(&sp->su), ERROR_STR());
+		} else {
+			dcc_error_msg("recvmsg(%s): %s",
+				      dcc_su2str_err(&sp->su), ERROR_STR());
+		}
+		free_q(q);
+		return 0;
+	}
+	if (q->clnt_su.sa.sa_family != sp->su.sa.sa_family
+	    && !dcc_ipv4sutoipv6(&q->clnt_su, &q->clnt_su)) {
+		dcc_error_msg("recvmsg address family %d instead of %d",
+			      q->clnt_su.sa.sa_family,
+			      sp->su.sa.sa_family);
+		free_q(q);
+		return 1;
+	}
+
+	if (DCC_SU_PORT(&q->clnt_su) == 0) {
+		drop_msg(q, "source port 0");
+		free_q(q);
+		return 1;
+	}
+
+	q->pkt_len = i;
+	if (i < ISZ(DCC_HDR)) {
+		drop_msg(q, "short request of %d bytes", i);
+		free_q(q);
+		return 1;
+	}
+	j = ntohs(q->pkt.hdr.len);
+	if (j != i) {
+		drop_msg(q, "request with header length %d instead of %d",
+			 j, i);
+		free_q(q);
+		return 1;
+	}
+
+	if (q->pkt.hdr.pkt_vers > DCC_PKT_VERSION_MAX_VALID
+	    || q->pkt.hdr.pkt_vers < DCC_PKT_VERSION_MIN_VALID
+	    || ((q->pkt.hdr.pkt_vers > DCC_PKT_VERSION_MAX
+		 || q->pkt.hdr.pkt_vers < DCC_PKT_VERSION_MIN)
+		&& q->pkt.hdr.op != DCC_OP_NOP)) {
+		drop_msg(q, "%s in unrecognized protocol version #%d",
+			 qop2str(q), q->pkt.hdr.pkt_vers);
+		free_q(q);
+		return 1;
+	}
+
+	q->answer = req_recv_time;
+
+	switch ((DCC_OPS)q->pkt.hdr.op) {
+	case DCC_OP_NOP:
+		do_nop(q);
+		free_q(q);
+		return 1;
+
+	case DCC_OP_REPORT:
+		if (db_parms.flags & DB_PARM_FG_GREY)
+			break;		/* not valid for greylist servers */
+		add_queue(q);
+		return 1;
+
+	case DCC_OP_QUERY:
+		add_queue(q);
+		return 1;
+
+	case DCC_OP_ADMN:
+		do_admn(q);
+		free_q(q);
+		return 1;
+
+	case DCC_OP_DELETE:
+		do_delete(q);
+		free_q(q);
+		return 1;
+
+	case DCC_OP_GREY_REPORT:
+	case DCC_OP_GREY_QUERY:
+	case DCC_OP_GREY_WHITE:
+		if (!(db_parms.flags & DB_PARM_FG_GREY))
+			break;		/* valid only for greylist servers */
+		do_grey(q);
+		free_q(q);
+		return 1;
+
+	case DCC_OP_GREY_SPAM:
+		if (!(db_parms.flags & DB_PARM_FG_GREY))
+			break;		/* valid only for greylist servers */
+		do_grey_spam(q);
+		free_q(q);
+		return 1;
+
+	case DCC_OP_INVALID:
+	case DCC_OP_ANSWER:
+	case DCC_OP_OK:
+	case DCC_OP_ERROR:
+		break;
+	}
+
+	drop_msg(q, "invalid %s", op_id_ip(q));
+	free_q(q);
+	return 1;
+}
+
+
+
+void
+free_q(QUEUE *q)
+{
+	if (q->rl)
+		--q->rl->ref_cnt;
+	q->later = queue_free;
+	queue_free = q;
+}
+
+
+
+u_char
+dccd_db_open(u_char lock_mode)
+{
+	DCC_CK_TYPES type;
+	time_t clean_secs;
+	DCC_TGTS tgts;
+	int i;
+
+	if (!db_open(dcc_emsg, -1, 0, 0, lock_mode | db_mode))
+		return 0;
+
+	if (grey_on) {
+		/* for greylisting, ignore the args an silently impose our
+		 * notion of which checksums to keep and flooding thresholds */
+		db_parms.nokeep_cks = def_nokeep_cks();
+		if (grey_weak_ip)
+			DB_RESET_NOKEEP(db_parms.nokeep_cks, DCC_CK_IP);
+
+		for (type = DCC_CK_TYPE_FIRST;
+		     type <= DCC_CK_TYPE_LAST;
+		     ++type) {
+			if (type == DCC_CK_SRVR_ID) {
+				flod_tholds[type] = 1;
+				continue;
+			}
+
+			if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+				flod_tholds[type] = DCC_TGTS_INVALID;
+			else
+				flod_tholds[type] = 1;
+
+			if (DCC_CK_IS_GREY_TRIPLE(grey_on,type)
+			    || type == DCC_CK_IP) {
+				db_parms.ex_secs[type].all = grey_window;
+				db_parms.ex_secs[type].spam = grey_white;
+			} else if (type == DCC_CK_BODY
+				   || DCC_CK_IS_GREY_MSG(grey_on,type)) {
+				db_parms.ex_secs[type].all = grey_window;
+				db_parms.ex_secs[type].spam = grey_window;
+			} else {
+				db_parms.ex_secs[type].all = 1;
+				db_parms.ex_secs[type].spam = 1;
+			}
+		}
+
+		summarize_delay_secs = grey_embargo - FLODS_CK_SECS*2;
+
+	} else {
+		/* impose our notion of which normal checksums to keep */
+		DB_SET_NOKEEP(set_new_nokeep_cks, DCC_CK_FLOD_PATH);
+		DB_SET_NOKEEP(set_new_nokeep_cks, DCC_CK_INVALID);
+		DB_SET_NOKEEP(set_new_nokeep_cks, DCC_CK_REP_TOTAL);
+		DB_SET_NOKEEP(set_new_nokeep_cks, DCC_CK_REP_BULK);
+		db_parms.nokeep_cks = ((def_nokeep_cks()
+					& ~reset_new_nokeep_cks)
+				       | set_new_nokeep_cks);
+
+		for (type = DCC_CK_TYPE_FIRST;
+		     type <= DCC_CK_TYPE_LAST;
+		     ++type) {
+			if (type == DCC_CK_SRVR_ID) {
+				flod_tholds[type] = 1;
+				continue;
+			}
+
+			if (type == DCC_CK_REP_TOTAL)
+				tgts = DCC_TGTS_INVALID;
+			if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+				tgts = DCC_TGTS_INVALID;
+			else if (DCC_CK_IS_REP_CMN(0, type))
+				tgts = DCC_TGTS_INVALID;
+			else
+				tgts = BULK_THRESHOLD;
+			flod_tholds[type] = tgts;
+		}
+
+		/* We should not delay reports or summaries so much that
+		 * dbclean might expire them before we can summarize them. */
+		summarize_delay_secs = DCC_OLD_SPAM_SECS;
+		for (type = DCC_CK_TYPE_FIRST;
+		     type <= DCC_CK_TYPE_LAST;
+		     ++type) {
+			if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+				continue;
+			i = db_parms.ex_secs[type].spam;
+			if (i != 0 && summarize_delay_secs > i)
+				summarize_delay_secs = i;
+		}
+	}
+	if (summarize_delay_secs < 1)
+		summarize_delay_secs = 1;
+
+	/* adjust the thresholds after possible changes to kept checksums */
+	set_db_tholds(db_parms.nokeep_cks);
+
+	/* If we instead of cron asked for the last cleaning, make a note
+	 * to clean the database during the graveyard shift.
+	 * Otherwise the database will bloat while the cron job is broken.
+	 *
+	 * Compute 1 day + 45 minutes after cron should have last cleaned the
+	 *	database, if it has been cleaned by cron within the last 3 days
+	 * or the quarter hour when it was last cleaned by this mechanism
+	 *	provided that was between local midnight and 05:00
+	 * or 3 minutes past a random quarter hour beteen midnight and 05:00 */
+	if (db_parms.cleaned_cron >= db_time.tv_sec - 3*24*60*60
+	    && db_parms.cleaned_cron <= db_time.tv_sec) {
+		clean_last_secs = db_parms.cleaned_cron;
+		/* failsafe cleaning for the greylist database starts 15 minutes
+		 * before the main database */
+		clean_secs = clean_last_secs + 45*60 - grey_on*15*60;
+	} else {
+		struct tm tm;
+
+		clean_last_secs = db_parms.cleaned;
+		if (clean_last_secs >= db_time.tv_sec)
+			clean_last_secs = 0;
+		clean_secs = clean_last_secs;
+
+		/* if the previous time for this mechanism is not good,
+		 * pick a new random time */
+		dcc_localtime(clean_secs, &tm);
+		if (clean_secs == 0 || tm.tm_hour < 1 || tm.tm_hour >= 5) {
+			int rnum = (u_int)(db_time.tv_sec + db_time.tv_usec
+					   + my_srvr_id - grey_on) % 23;
+			tm.tm_hour = ((rnum / 4) % 4) + 1;
+			tm.tm_min = (rnum % 4) * 15;
+			clean_secs = mktime(&tm);
+		}
+	}
+	/* round down to a quarter hour to prevent creep due to inevitiable
+	 * delays in cron or this mechanism starting dbclean */
+	clean_secs -= clean_secs % (15*60);
+	clean_secs %= (24*60*60);	/* failsafe cleaning target minute */
+
+	/* compute the next scheduled failsafe cleaning. */
+	clean_fake_secs = db_time.tv_sec - (db_time.tv_sec % (24*60*60));
+	clean_fake_secs += clean_secs;
+	if (clean_fake_secs <= db_time.tv_sec)
+		clean_fake_secs += 24*60*60;
+
+	/* The next failsafe cleaning should happen a day after the
+	 * most recent cron or failsafe cleaning, modulo the delay before
+	 * dbclean starts */
+	while (clean_fake_secs <= clean_last_secs + 24*60*60 - 60*60)
+		clean_fake_secs += 24*60*60;
+
+	/* Do not failsafe clean during the first 48 hours after the
+	 * database was created to give the cron job a chance.
+	 * We do not want failsafe cleaning to ever be running when the
+	 * cron job tries to start. */
+	while (clean_fake_secs <= db_parms.cleared + 2*24*60*60
+	       && db_parms.cleared <= db_time.tv_sec)
+		clean_fake_secs += 24*60*60;
+
+	total_ops = 0;
+
+	/* push our thresholds and flags to the file */
+	return db_flush_parms(0);
+}
+
+
+
+/* clean shut down */
+static void NRATTRIB
+dccd_quit(int exitcode, const char *p, ...)
+{
+	va_list args;
+
+	if (stop_mode == 1)
+		db_stop();
+	else if (stop_mode == 2)
+		make_clean(2);
+	db_unlock();
+
+	va_start(args, p);
+	if (exitcode)
+		dcc_verror_msg(p, args);
+	else
+		dcc_vtrace_msg(p, args);
+	va_end(args);
+
+	/* db_close() can take a long time, so close some things early. */
+	stop_children();
+	check_dbclean(WNOHANG);
+	clients_save();
+
+#ifdef HAVE_COHERENT_MMAP
+	/* If mmap() is not coherent, do not call close_srvr_socs() but
+	 * keep the UDP sockets open to prevent another server from starting
+	 * until we have flushed our buffers to prevent problems on systems
+	 * that lack inter-process coherent mmap() */
+	if (!(db_mode & DB_OPEN_MMAP_WRITE))
+		close_srvr_socs();
+#endif
+	db_close(1);
+
+	if (exitcode)
+		dcc_error_msg("stopped");
+	else
+		dcc_trace_msg("stopped");
+	exit(exitcode);
+}
+
+
+
+/* watch for fatal signals */
+static void
+sigterm(int sig)
+{
+	stopint = sig;
+	stop_mode = 1;
+	next_flods_ck = 0;
+	(void)signal(sig, SIG_DFL);	/* catch it only once */
+}
+
+
+
+/* SIGHUP hurries checking the configuration files */
+static void
+sighup(int sig UATTRIB)
+{
+	next_flods_ck = 0;
+}
+
+
+
+/* emergency shutdown but close the database cleanly */
+void
+bad_stop(const char *pat, ...)
+{
+	va_list args;
+
+	if (stopint)
+		return;
+
+	va_start(args, pat);
+	dcc_verror_msg(pat, args);
+	va_end(args);
+
+	stopint = -1;
+	next_flods_ck = 0;
+}
+
+
+
+static void
+stop_children(void)
+{
+	if (resolve_hosts_pid > 0)
+		kill(resolve_hosts_pid, SIGKILL);
+
+	if (dbclean_pid > 0)
+		kill(dbclean_pid, SIGKILL);
+}
diff -r 000000000000 -r c7f6b056b673 dccd/dccd_defs.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd/dccd_defs.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,646 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * server daemon definitions
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.221 $Revision$
+ */
+
+#ifndef DCCD_DEFS_H
+#define DCCD_DEFS_H
+
+#include "srvr_defs.h"
+#include "dcc_xhdr.h"
+
+extern DCC_EMSG dcc_emsg;
+
+extern u_char grey;
+extern u_char background;
+extern int stopint;
+
+extern DCC_SRVR_ID my_srvr_id;
+
+extern const char *brand;		/* our brand name */
+
+
+extern u_char use_ipv6;
+extern u_int16_t def_port;
+typedef struct srvr_soc {
+    struct srvr_soc *fwd;
+    DCC_SOCKU	su;
+    union {
+	struct dcc_in6_addr in6;
+	struct in_addr	    in4;
+    } arg_addr;
+    int		udp;
+    int		listen;
+    u_int16_t	arg_port;
+    u_char	arg_family;
+    u_char	flags;
+#    define	 SRVR_SOC_ADDR	    0x01    /* explicit IP address */
+#    define	 SRVR_SOC_IF	    0x02    /* port on all interfaces */
+#    define	 SRVR_SOC_LISTEN    0x04    /* need a listen socket for port */
+#    define	 SRVR_SOC_MARK	    0X08
+#    define	 SRVR_SOC_NEW	    0X10
+} SRVR_SOC;
+extern SRVR_SOC *srvr_socs;
+extern int srvr_rcvbuf;
+
+
+#define MAX_CMD_CLOCK_SKEW (DCC_MAX_RETRANS_DELAY_SECS*2)
+#define MAX_FLOD_CLOCK_SKEW (4*60*60)	/* refuse reports this far in advance */
+
+
+extern char our_hostname[MAXHOSTNAMELEN];
+extern DCC_SUM host_id_sum;		/* advertised with our server-ID */
+extern time_t host_id_next, host_id_last;
+#define DCC_SRVR_ID_SECS    (24*60*60)	/* defend server-IDs this often */
+#define DCC_SRVR_ID_SECS_ST (5*60)	/* defend after daemon starts */
+
+extern int flod_trace_gen;		/* unsuppress tracing */
+
+/* keepalive intervals
+ *	An idle flooding link is kept alive, or known to be healthy, by the
+ *	receiving server repeating its current position.  If the link is
+ *	broken, the receiving server's transmissions of its position will fail
+ *	and the	transmitting server will hear silence on the link. */
+#define	KEEPALIVE_IN	    (10*60)
+#define	KEEPALIVE_OUT	    (KEEPALIVE_IN+FLODS_CK_SECS)
+/* Things should be quicker while we are shutting down flooding
+ *	Some TCP/IP implementations have retransmission delays that can
+ *	totals 7 seconds, so we must not be too quick */
+#define KEEPALIVE_IN_STOP   30
+#define	KEEPALIVE_OUT_STOP  (KEEPALIVE_IN_STOP+FLODS_CK_SECS)
+/* be really quick if stopping the daemon */
+#define SHUTDOWN_DELAY	    2
+
+#define IFP_DEAD(p,secs)  DB_IS_TIME((p)->iflod_alive+(secs), secs)
+#define OFP_DEAD(p,secs)  DB_IS_TIME((p)->oflod_alive+(secs), secs)
+
+
+/* Delay our reports and summaries of our reports by this much.
+ * It should be long enough to allow us to generate useful summaries, but
+ * it cannot be so long that we won't flood the summary when we make it. */
+extern int summarize_delay_secs;
+
+
+extern int queue_max;
+
+extern u_char anon_off;			/* turn off anonymous access */
+extern u_char query_only;		/* 1=treat reports as queries */
+
+extern time_t anon_delay_us;		/* anonymous client delay */
+extern u_int anon_delay_inflate;
+
+extern struct timeval wake_time;	/* when we awoke from select() */
+extern struct timeval req_recv_time;	/* when request arrived */
+
+extern u_char grey_weak_body;		/* 1=ignore bodies for greylisting */
+extern u_char grey_weak_ip;		/* 1=one good triple whitelists addr */
+
+extern int grey_embargo;
+extern int grey_window;
+extern int grey_white;
+
+
+/* rate limiting
+ *	One of these structures is maintained for every recent client,
+ *	where "recent" is at least one day */
+typedef u_int16_t RL_DATA_FG;
+#    define	 RL_FG_MARKED	0x0001	/* seen during `cdcc clients` */
+#    define	 RL_FG_CK_BL	0x0002	/* 0=need to check list of addresses */
+#    define	 RL_FG_TRACE	0x0004
+#    define	 RL_FG_BL_ADDR	0x0008	/* blacklisted client or flood peer */
+#    define	 RL_FG_BL_ID	0x0010	/* blacklisted client ID */
+#    define	 RL_FG_BL_BAD	0x0020	/* ignored for bad behavior */
+#    define	 RL_FG_BLS (RL_FG_BL_ADDR | RL_FG_BL_ID | RL_FG_BL_BAD)
+#    define	 RL_FG_PASSWD	0x0040	/* bad password */
+#    define	 RL_FG_UKN_ID	0x0080	/* bad ID */
+#    define	 RL_FG_ANON	0x0100	/* clnt_id=DCC_ID_ANON or bad */
+typedef struct {
+    u_int64_t	requests;
+    u_int64_t	requests_old;
+    u_int64_t	nops;
+    u_int64_t	nops_old;
+    time_t	last_used;
+    time_t	requests_avg_start;	/* effective start of averaging */
+#    define	 RL_AVG_TERM	(60*60 * 48)
+    time_t	requests_avg_aged;	/* when last updated */
+#    define	 RL_AVG_UPDATE	(10*60)
+    u_int	requests_avg_total;
+    u_int	requests_avg;
+    u_int	nops_avg_total;
+    u_int	nops_avg;
+#    define	 RL_REQUESTS_AVG(d) max((d)->requests_avg, (d)->requests)
+#    define	 RL_NOPS_AVG(d)	    max((d)->nops_avg, (d)->nops)
+    int		request_credits;	/* limit operations */
+    int		bug_credits;		/* limit complaints about this client */
+    DCC_CLNT_ID	clnt_id;
+    struct in6_addr clnt_addr;
+    u_char	pkt_vers;		/* recent protocol version */
+    RL_DATA_FG	flags;
+} RL_DATA;
+typedef struct rl {
+    struct rl	*hfwd, *hbak, **bin;	/* neighbors in hash chain & the bin */
+    struct rl	*older, *newer;		/* global recently used chain */
+    RL_DATA	d;
+    short	ref_cnt;		/* in use by an entry in job queue */
+} RL;
+
+
+/* rate-limit parameters
+ *	Decrease request_credits by RL_SCALE for each event.
+ *	Increase request_credits by .per_sec for every second.
+ *	When request_credits <= 0, there have been too many events.
+ *	Clamp request_credits at .lo to limit the duration of a penalty.
+ *	Clamp request_credits at .hi to limit the duration over which
+ *	    the rate is averaged.
+ */
+typedef struct {
+    float   penalty_secs;		/* drop excess events for this long */
+    int	    per_sec;			/* allowed events/second * RL_SCALE */
+    int	    lo;				/* clamp credit count at this */
+    int	    hi;				/* reset credit count to this */
+} RL_RATE;
+
+extern RL_RATE rl_sub_rate;		/* X/sec/paying customer */
+extern RL_RATE rl_anon_rate;		/* X/sec/freeloader */
+extern RL_RATE rl_all_anon_rate;	/* X/sec for all freeloaders */
+extern RL_RATE rl_bugs_rate;		/* X complaints/sec */
+
+#if defined(RL_MIN_MAX)
+#if RL_MIN_MAX<1000 || RL_MIN_MAX>1000000
+#undef RL_MIN_MAX
+#endif
+#endif
+#define RL_MIN_MAX_DEF	(10*1000)
+#define RL_MIN_MAX_MAX	(1000*1000)	/* fix ./configure if this changes */
+#define RL_AVG_SECS	10		/* average for this many seconds */
+#define	RL_LIFE_SECS    (RL_AVG_SECS*2)	/* lifetime of rate limit block */
+
+#define RL_OVF_CREDITS	0x7fffffff	/* fit {bug,request}_credits */
+#define RL_SCALE	10
+#define RL_MAX_CREDITS	(RL_OVF_CREDITS/RL_AVG_SECS/RL_SCALE/2)
+
+
+extern time_t clients_cleared;
+
+
+/* file containing rate limit blocks */
+#define CLIENTS_NM()	    (grey_on ? "grey_clients" : "dccd_clients")
+#define BAD_CLIENTS_NM()    (grey_on ? "grey_clients-bad" : "dccd_clients-bad")
+
+typedef struct {
+    char    magic[80];
+    char    pad[40];
+    time_t  now;
+    time_t  cleared;
+    int32_t anon_delay_us;
+    u_int32_t anon_delay_inflate;
+    int	    hash_len;
+} CLIENTS_HEADER;
+#define CLIENTS_MAGIC_VERSION	"7"
+#define CLIENTS_MAGIC_STR	" client rate limit blocks version "
+#define CLIENTS_MAGIC_BASE(g)	((g) ? "greylist" CLIENTS_MAGIC_STR	\
+				 : "dccd" CLIENTS_MAGIC_STR)
+#define CLIENTS_MAGIC_V(g,v)	((g) ? "greylist" CLIENTS_MAGIC_STR v	\
+				 : "dccd" CLIENTS_MAGIC_STR v)
+#define CLIENTS_MAGIC(g)	CLIENTS_MAGIC_V(g,CLIENTS_MAGIC_VERSION)
+
+
+
+/* report cache used to detect duplicate reports
+ *	One of these structures is maintained for every current operation */
+typedef struct ridc {
+    struct ridc *fwd, *bak, **hash;
+    struct ridc *older, *newer;
+    time_t	last_used;
+    DCC_HDR	hdr;
+    u_int16_t	clnt_port;
+    int		len;
+    u_char	op;
+    u_char	bad;
+    union {
+	DCC_ANSWER_BODY_CKS b;
+	DCC_ADMN_RESP_ANON_DELAY anon_delay;
+	char		msg[DCC_ERROR_MSG_LEN];
+    } result;
+} RIDC;
+
+/* entry in main job queue */
+typedef struct dccd_queue {
+    struct dccd_queue *later, *earlier;
+    RL		*rl;
+    RIDC	*ridc;
+    SRVR_SOC	*sp;
+    DCC_CLNT_ID	clnt_id;
+    DCC_SOCKU	clnt_su;		/* send answer here */
+    u_int	pkt_len;
+    time_t	delay_us;		/* how long to delay the answer */
+    struct timeval answer;		/* when it should be answered */
+    u_char	flags;
+#    define	 Q_FG_RPT_OK	    0x01    /* override dccd -Q */
+#    define	 Q_FG_UNTRUSTED	    0x02
+#    define	 Q_FG_UKN_ID	    0x04
+#    define	 Q_FG_BAD_PASSWD    0x08
+    DCC_PASSWD	passwd;			/* sign answers with this */
+    union {
+	DCC_HDR	    hdr;
+	DCC_REPORT  r;
+	DCC_DELETE  d;
+	DCC_GREY_SPAM gs;
+	DCC_ADMN_REQ ad;
+    } pkt;
+} QUEUE;
+
+
+typedef struct iflod_info IFLOD_INFO;
+
+typedef struct {
+    DCC_SRVR_ID from_lo;
+    DCC_SRVR_ID from_hi;
+    u_char	result;
+} OFLOD_SRVR_ID_MAP;
+typedef enum {
+    ID_MAP_NO, ID_MAP_REJ, ID_MAP_SELF
+} ID_MAP_RESULT;
+
+typedef struct {
+    int	    cur, lim;			/* signed because lim can be <0 */
+} FLOD_LIMCNT;
+#define FLOD_LIM_CLEAR_SECS	(5*60)
+#define FLOD_LIM_COMPLAINTS	10
+
+typedef u_int OPT_FLAGS;
+typedef struct {
+    OPT_FLAGS	flags;
+#    define	 FLOD_OPT_OFF		0x0001
+#    define	 FLOD_OPT_TRACE		0x0002
+#    define	 FLOD_OPT_TRACE2	0x0004
+#    define	 FLOD_OPT_ROGUE		0x0008
+#     define	  IFLOD_OPT_OFF_ROGUE(o) (((o)->i_opts.flags & FLOD_OPT_OFF)  \
+					  | ((o)->o_opts.flags&FLOD_OPT_ROGUE))
+#     define	  OFLOD_OPT_OFF_ROGUE(o) ((o)->o_opts.flags & (FLOD_OPT_OFF \
+							| FLOD_OPT_ROGUE))
+#    define	 FLOD_OPT_IPv4		0x0010
+#    define	 FLOD_OPT_IPv6		0x0020
+#    define	 FLOD_OPT_PASSIVE	0x0040
+#    define	 FLOD_OPT_SOCKS		0x0080
+#    define	 FLOD_OPT_NAT		0x0100
+#    define	 FLOD_OPT_DEL_OK	0x0200
+#    define	 FLOD_OPT_DEL_SET	0x0400
+#    define	 FLOD_OPT_NO_LOG_DEL	0x0800
+#    define	 FLOD_OPT_TRAPS		0x1000
+#    define	 FLOD_OPT_SIMPLE	0x2000
+    OFLOD_SRVR_ID_MAP srvr_map[10];
+    u_char	num_maps;
+    u_char	path_len;
+} OFLOD_OPTS;
+
+typedef struct {
+    FLOD_MMAP	*mp;
+    int		soc;			/* outgoing socket */
+    int		lno;
+    char	rem_portname[sizeof(flod_mmaps->mmaps[0].rem_portname)];
+    char	rem_hostname[sizeof(flod_mmaps->mmaps[0].rem_hostname)];
+    char	loc_hostname[DCC_MAXDOMAINLEN];
+    DCC_SRVR_ID	rem_id, in_passwd_id, out_passwd_id;
+    u_int16_t	rem_port, loc_port;
+    DCC_SOCKU	rem_su;			/* target of the flood */
+    time_t	limit_reset;		/* when to reset complaint limits */
+    time_t	oflod_alive;		/* when last active */
+    struct {
+	time_t	    saved;		/* last wrote counts to file */
+	u_int	    out_reports;	/* total reports sent */
+	u_int	    total;		/* total reports received */
+	u_int	    accepted;		/* acceptable received reports */
+    } cnts;
+    struct {
+	FLOD_LIMCNT stale;		/* bad timestamp */
+	FLOD_LIMCNT dup;		/* already received */
+	FLOD_LIMCNT wlist;		/* whitelisted */
+	FLOD_LIMCNT not_deleted;	/* delete commands ignored */
+	FLOD_LIMCNT bad_id;		/* unrecognized server-IDs */
+	FLOD_LIMCNT complaint;		/* output complaint from peer */
+	FLOD_LIMCNT iflod_bad;		/* generic bad report */
+    } lc;
+    DB_PTR	xmit_pos;		/* last transmitted position */
+    DB_PTR	recv_pos;		/* heard this from target */
+    DB_PTR	cur_pos;		/* completed to here */
+    DB_PTR	rewind_pos;		/* will have rewound by here */
+    int		ibuf_len;
+    union {
+	DCC_FLOD_RESP r;
+	u_char	    b[sizeof(DCC_FLOD_RESP)*2];
+    } ibuf;
+    u_int	obuf_len;
+    union {
+	DCC_FLOD_STREAM s;
+#	 define	     FLOD_BUF_SIZE 2048
+	u_char	    b[FLOD_BUF_SIZE];
+    } obuf;
+    OFLOD_OPTS	i_opts;
+    OFLOD_OPTS	o_opts;
+    IFLOD_INFO	*ifp;
+    u_int	flags;
+#    define	 OFLOD_FG_CONNECTED	0x0001	/* connect() complete */
+#    define	 OFLOD_FG_NEW		0x0002	/* new connection */
+#    define	 OFLOD_FG_SHUTDOWN	0x0004	/* brakes applied */
+#    define	 OFLOD_FG_SHUTDOWN_REQ	0x0008
+#    define	 OFLOD_FG_HAVE_2PASSWD	0x0010	/* have a 2nd password */
+#    define	 OFLOD_FG_I_USED_2PASSWD 0x020	/* used the 2nd password */
+#    define	 OFLOD_FG_O_USED_2PASSWD 0x040
+#    define	 OFLOD_FG_EAGAIN	0x0080	/* recent bogus EAGAIN */
+	u_char	oversion;
+} OFLOD_INFO;
+
+typedef struct {
+    int		total;			/* known peers */
+    int		open;			/* active outgoing streams */
+    OFLOD_INFO  infos[DCCD_MAX_FLOODS];
+} OFLODS;
+extern OFLODS oflods;
+extern DB_PTR oflods_max_cur_pos;
+
+extern enum FLODS_ST {
+	FLODS_ST_OFF, FLODS_ST_RESTART, FLODS_ST_ON
+} flods_st;
+
+extern DCC_TGTS flod_tholds[DCC_DIM_CKS];
+
+
+struct iflod_info {
+    int		soc;			/* incoming socket */
+    DCC_SOCKU	rem_su;			/* sender of the flood */
+    char	rem_hostname[sizeof(flod_mmaps->mmaps[0].rem_hostname)];
+    DCC_FLOD_POS pos, pos_sent;
+    OFLOD_INFO	*ofp;
+    time_t	iflod_alive;		/* when last active */
+    int		ibuf_len;
+    u_char	flags;
+#    define	 IFLOD_FG_CONNECTED	0x01
+#    define	 IFLOD_FG_CLIENT	0x02	/* outgoing connection */
+#    define	 IFLOD_FG_VERS_CK	0x04
+#    define	 IFLOD_FG_END_REQ	0x08
+#    define	 IFLOD_FG_FAST_LINGER	0x10
+    union {
+	DCC_FLOD_STREAM	s;
+	u_char		b[FLOD_BUF_SIZE];
+    } ibuf;
+};
+
+typedef struct {
+    int		open;
+    IFLOD_INFO	infos[DCCD_MAX_FLOODS];
+} IFLODS;
+extern IFLODS iflods;
+
+extern int flods_off;			/* # of reasons flooding is off */
+#define FLODS_OK()	(!flods_off && !db_minimum_map)
+#define FLODS_OK_ON()	(FLODS_OK() && flods_st == FLODS_ST_ON)
+extern u_int complained_many_iflods;
+
+typedef enum {
+    WFIX_DELAY,				/* waiting for window overflow */
+    WFIX_BUSY,				/* measuring active load */
+    WFIX_QUIET,				/* waiting for clients to flee */
+    WFIX_CHECK,				/* counting clients that stayed */
+} DBCLEAN_WFIX_STATE;
+extern DBCLEAN_WFIX_STATE dbclean_wfix_state;
+
+extern u_char stop_mode;		/* 0=normal 1=reboot 2=with/DB clean */
+extern time_t next_flods_ck;
+#define FLODS_CK_SECS	    5
+#define RUSH_NEXT_FLODS_CK() {if (next_flods_ck > db_time.tv_sec + 1)	\
+	next_flods_ck = db_time.tv_sec + 1;}
+#define MISC_CK_SECS	    FLODS_CK_SECS
+#define CLIENTS_SAVE_SECS   (30*60)
+
+extern time_t flod_mtime;
+
+#define FLOD_RETRY_SECS		(5*60)	/* retry connection no sooner */
+#define FLOD_SUBMAX_RETRY_SECS	(60*60)	/* retry when peer can't poke us */
+#define FLOD_MAX_RETRY_SECS (24*60*60)	/* maximum backoff */
+#define	FLOD_SOCKS_SOCKS_IRETRY	30
+
+#define	FLOD_IN_COMPLAIN    (24*60*60)  /* complain daily about input */
+#define	FLOD_IN_COMPLAIN1   (2*60*60)	/* 1st normal input complaint */
+#define	FLOD_IN_COMPLAIN_NOW	(5*60)	/* complain as soon as possible */
+
+extern time_t iflods_ok_timer;		/* incoming flooding ok since then */
+#define IFLODS_OK_SECS	    (5*60)	/* 5 minutes to catch up */
+
+extern time_t need_clients_save;
+
+extern time_t got_hosts;		/* resolve hostnames */
+#define FLOD_NAMES_RESOLVE_SECS	(5*60)	/*	at most every 5 minutes */
+extern pid_t resolve_hosts_pid;
+
+extern const char *need_del_dbclean;
+extern time_t del_dbclean_next;
+#define DEL_DBCLEAN_SECS	(30*60)	/* limit dbclean if not urgent */
+extern time_t dbclean_limit;
+#define DBCLEAN_LIMIT_SECS	15	/* not too often for any reason */
+extern time_t dbclean_limit_secs;
+
+extern DCCD_STATS dccd_stats;
+
+
+/* Avoid the costs of generating and passing the args to syslog() by
+ * checking bits in the caller.
+ * If the server ran only on modern Unix, we could use gcc's macro varargs. */
+#define TMSG_BIT(t) (DCC_TRACE_##t##_BIT & dccd_tracemask)
+#define TMSG_BLOCK(t,args) do {if TMSG_BIT(t) dcc_trace_msg args;} while (0)
+#define TMSG(t,p)	TMSG_BLOCK(t,(p))
+#define TMSG1(t,p,arg)	TMSG_BLOCK(t,(p,arg))
+#define TMSG2(t,p,arg1,arg2) TMSG_BLOCK(t,(p,arg1,arg2))
+#define TMSG3(t,p,arg1,arg2,arg3) TMSG_BLOCK(t,(p,arg1,arg2,arg3))
+#define TMSG4(t,p,arg1,arg2,arg3,arg4) TMSG_BLOCK(t,(p,arg1,arg2,arg3,arg4))
+#define TMSG5(t,p,arg1,arg2,arg3,arg4,arg5)				\
+		TMSG_BLOCK(t,(p,arg1,arg2,arg3,arg4,arg5))
+
+#define TMSG_FB(ofp) ((DCC_TRACE_FLOD_BIT & dccd_tracemask)		\
+		      || ((ofp && (ofp->o_opts.flags & FLOD_OPT_TRACE))))
+#define TMSG_Fsub(ofp,args) do {if (TMSG_FB(ofp)) dcc_trace_msg args;} while(0)
+#define TMSG_FLOD(ofp,p) TMSG_Fsub(ofp,(p))
+#define TMSG1_FLOD(ofp,p,arg) TMSG_Fsub(ofp,(p,arg))
+#define TMSG2_FLOD(ofp,p,arg1,arg2) TMSG_Fsub(ofp,(p,arg1,arg2))
+#define TMSG3_FLOD(ofp,p,arg1,arg2,arg3) TMSG_Fsub(ofp,(p,arg1,arg2,arg3))
+
+#define TMSG_FB2(ofp) (((DCC_TRACE_FLOD_BIT				\
+			 | DCC_TRACE_FLOD2_BIT) & dccd_tracemask)	\
+		       || ((ofp && (ofp->o_opts.flags			\
+				    & (FLOD_OPT_TRACE | FLOD_OPT_TRACE2)))))
+#define TMSG_F2sub(ofp,args) do {if (TMSG_FB2(ofp))dcc_trace_msg args;} while(0)
+#define TMSG_FLOD2(ofp,p) TMSG_F2sub(ofp,(p))
+#define TMSG1_FLOD2(ofp,p,arg) TMSG_F2sub(ofp,(p,arg))
+#define TMSG2_FLOD2(ofp,p,arg1,arg2) TMSG_F2sub(ofp,(p,arg1,arg2))
+#define TMSG3_FLOD2(ofp,p,arg1,arg2,arg3) TMSG_F2sub(ofp,(p,arg1,arg2,arg3))
+
+
+#define Q_CIP(q) dcc_su2str_err(&(q)->clnt_su)
+
+
+static inline void
+db_ptr2flod_pos(DCC_FLOD_POS bp, DB_PTR pos)
+{
+	bp[7] = pos;     bp[6] = pos>>8;
+	bp[5] = pos>>16; bp[4] = pos>>24;
+	bp[3] = pos>>32; bp[2] = pos>>40;
+	bp[1] = pos>>48; bp[0] = pos>>56;
+}
+
+
+static inline DB_PTR
+flod_pos2db_ptr(const DCC_FLOD_POS pos)
+{
+	return ((DB_PTR)pos[7]		 + (((DB_PTR)pos[6])<<8)
+		+ (((DB_PTR)pos[5])<<16) + (((DB_PTR)pos[4])<<24)
+		+ (((DB_PTR)pos[3])<<32) + (((DB_PTR)pos[2])<<40)
+		+ (((DB_PTR)pos[1])<<48) + (((DB_PTR)pos[0])<<56));
+}
+
+
+/* multiplicative hash function after Knuth vol. 3 */
+static inline u_int32_t
+mhash(u_int32_t hashin, u_int nbins)
+{
+	u_int64_t v;
+
+	v = 0x9ccf9319;
+	v *= hashin;			/* v=(hashin * Knuth's 0.6125423371 */
+	v &= 0xffffffff;		/* fractional part or modulo 1 */
+	v *= nbins;
+	return v >> 32;
+}
+
+
+/* dccd.c */
+extern void free_q(QUEUE *);
+extern void after_fork(void);
+extern void set_dbclean_timer(void);
+extern void bad_stop(const char *, ...) PATTRIB(1,2);
+
+/* iflod.c */
+extern ID_MAP_RESULT id_map(DCC_SRVR_ID, const OFLOD_OPTS *);
+extern const char * ifp_rem_str(const IFLOD_INFO *);
+#define CK_FLOD_CNTERR(lc) (++(lc)->cur <= ((lc)->lim + FLOD_LIM_COMPLAINTS))
+extern void flod_cnterr(const FLOD_LIMCNT *, const char *, ...) PATTRIB(2,3);
+extern const char * ofp_rem_str(const OFLOD_INFO *);
+extern void rpt_err(OFLOD_INFO *, u_char, u_char,
+		    const char *, ...) PATTRIB(4,5);
+extern u_char set_flod_socket(OFLOD_INFO *, u_char, int,
+			      const char *, const DCC_SOCKU *);
+extern u_char flod_names_resolve_ck(void);
+extern u_char flod_names_resolve_start(void);
+extern void iflod_listen_close(SRVR_SOC *);
+extern void iflods_stop(const char *, u_char);
+extern void iflod_start(SRVR_SOC *);
+extern void iflods_listen(void);
+extern void iflod_socks_start(OFLOD_INFO *);
+extern u_char dccd_db_open(u_char);
+extern void iflod_close(IFLOD_INFO *, u_char, u_char, u_char,
+			const char *, ...) PATTRIB(5,6);
+extern u_char iflod_read(IFLOD_INFO *);
+extern int iflod_send_pos(IFLOD_INFO *, u_char);
+extern int flods_list(char *, int, u_char);
+extern int flod_stats(char *, int, u_int32_t, u_char);
+
+/* oflod.c */
+extern void oflods_clear(void);
+extern void oflod_open(OFLOD_INFO *);
+extern u_char load_flod(u_char);
+extern void save_flod_cnts(OFLOD_INFO *);
+extern void oflod_close(OFLOD_INFO *, u_char);
+extern int oflod_parse_eof(OFLOD_INFO *, u_char, const DCC_FLOD_END *, int);
+extern void oflod_read(OFLOD_INFO *);
+extern void oflod_write(OFLOD_INFO *);
+extern void flods_stop(const char *, u_char);
+extern const char *version_str(OFLOD_INFO *);
+extern void flod_try_again(OFLOD_INFO *);
+extern const char *flod_sign(OFLOD_INFO *, u_char, void *, int);
+extern u_char oflod_connect_fin(OFLOD_INFO *);
+extern void flods_restart(const char *, u_char);
+extern int check_load_ids(u_char);
+extern void flods_ck(u_char);
+extern void flods_init(void);
+
+/* rl.c */
+extern void rl_inc(RL *, const RL_RATE *);
+extern void clients_save(void);
+extern void clients_load(void);
+extern u_char ck_ip_bl(RL **, DCC_CLNT_ID, const struct in6_addr *);
+extern void clients_get_id(DCC_ADMN_RESP_VAL *, int *, u_int, int, u_char,
+			   const struct in6_addr *, const struct in6_addr *);
+extern int clients_get(DCC_ADMN_RESP_VAL *, int *, u_int, int, u_char,
+		       const struct in6_addr *, const struct in6_addr *);
+#define CLIENTS_AGE	    (24*60*60)
+#define CLIENTS_SAVE_AGE    (14*CLIENTS_AGE)
+extern void clients_clear(void);
+extern u_char ck_sign(const ID_TBL **, DCC_PASSWD, DCC_CLNT_ID,
+		      const void *, u_int);
+extern u_char ck_clnt_srvr_id(QUEUE *);
+extern u_char ck_clnt_id(QUEUE *);
+extern const char *qop2str(const QUEUE *);
+extern void check_blacklist_file(void);
+
+extern u_long dccd_tracemask;
+
+extern const char *from_id_ip(const QUEUE *, u_char);
+extern const char *op_id_ip(const QUEUE *);
+extern void vanon_msg(const char *, va_list);
+extern void anon_msg(const char *, ...) PATTRIB(1,2);
+extern void vclnt_msg(const QUEUE *, const char *, va_list);
+extern void clnt_msg(const QUEUE *, const char *, ...) PATTRIB(2,3);
+extern void drop_msg(QUEUE *, const char *, ...) PATTRIB(2,3);
+
+/* work.c */
+extern int find_srvr_rcd(const DCC_SUM, const char *);
+extern int find_srvr_rcd_type(DCC_SRVR_ID);
+extern ID_TBL *find_srvr_type(DCC_SRVR_ID);
+extern void refresh_srvr_rcd(const DCC_SUM, DCC_SRVR_ID, const char *);
+extern void stats_clear(void);
+extern u_char summarize_dly(void);
+extern u_char add_dly_rcd(DB_RCD *, u_char);
+extern void do_work(QUEUE *);
+extern void do_grey(QUEUE *);
+extern void do_grey_spam(QUEUE *);
+extern void do_nop(QUEUE *);
+extern void do_admn(QUEUE *);
+extern void do_delete(QUEUE *);
+
+
+#endif /* DCCD_DEFS_H */
diff -r 000000000000 -r c7f6b056b673 dccd/dump-clients/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd/dump-clients/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,3 @@
+Makefile.in
+dump-clients.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 dccd/dump-clients/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd/dump-clients/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,51 @@
+# make the Distributed Checksum Clearinghouse server
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.3 $Revision$
+# @configure_input@
+
+DEPTH	=../..
+PROG	=dump-clients
+SRCS	=$(PROG).c
+
+CFLAGS	+=$(SRVRINC) -I..
+LDADD	+=$(SRVRLIBS)
+DPADD	+=$(SRVRLIBS)
+
+DCC_BINDIR=@installroot@@libexecdir@
+@MAKE_INC@
+@MAKE_PROG@
+@MAKE_INC2@
diff -r 000000000000 -r c7f6b056b673 dccd/dump-clients/dump-clients.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd/dump-clients/dump-clients.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,370 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * dump list of dccd clients
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.20 $Revision$
+ */
+
+#include "dccd_defs.h"
+
+static const char *homedir;
+
+u_char grey_on;
+static u_char nonames, quiet, avg;
+
+
+static void NRATTRIB
+usage(void)
+{
+	fprintf(stderr, "usage: [-anqg] [-h homedir] [ifile1 ifile2 ...]\n");
+	exit(1);
+}
+
+
+
+static void PATTRIB(1,2)
+error_msg(const char *p, ...)
+{
+	va_list args;
+
+	fflush(stdout);
+
+	va_start(args, p);
+	vfprintf(stderr, p, args);
+	va_end(args);
+	putchar('\n');
+}
+
+
+
+static u_char
+clients_read(const char *fname, FILE *f, off_t pos, void *buf, int buf_len)
+{
+	int i;
+
+	if (fseeko(f, pos, 0)) {
+		error_msg("fseeko(%s,%d): %s", fname, (int)pos, ERROR_STR());
+		fclose(f);
+		return 0;
+	}
+
+	i = fread(buf, buf_len, 1, f);
+	if (i == 1)
+		return 1;
+
+	if (feof(f))
+		return 0;
+
+	error_msg("fread(%s): %s", fname, ERROR_STR());
+	fclose(f);
+	return 0;
+}
+
+
+
+static void
+dump_file(const char *fname0)
+{
+#	define PAT_REQUESTS	L_DWPAT(7)" "L_DWPAT(6)" "
+#	define PAT_REQUESTS_B   "%7s %6s "
+#	define PAT_AVGS		"%7d %6d %4.1f "
+#	define PAT_AVGS_B	"   %14s   "
+#	define PAT_DATE_ID	"%-14s %6d%c"
+#	define PAT_DATE_ID_B	"%-14s %6s "
+#	define PAT_VERS		"%1d "
+#	define PAT_VERS_B	"%1s "
+#	define PAT_DELAY	"%5.3f "
+#	define PAT_DELAY_B	"%5s "
+	DCC_PATH fname;
+	FILE *f;
+	struct stat sb;
+	CLIENTS_HEADER header;
+	RL_DATA data;
+	struct {
+	    u_int   hosts;
+	    u_int   requests;
+	    u_int   nops;
+	}  versions[DCC_PKT_VERSION_MAX+1];
+	time_t secs;
+	struct tm tm;
+	char date_buf[40];
+	char date_buf2[40];
+	int inflate;
+	double delay_us;
+	DCC_SOCKU su;
+	char name[DCC_MAXDOMAINLEN];
+	char sustr[DCC_SU2STR_SIZE];
+	off_t pos;
+	int total, active, i;
+
+	if (!fnm2abs(fname, fname0, 0)) {
+		error_msg("name \"%s\" too long", fname0);
+		exit(1);
+	}
+
+	printf("%s\n", fname);
+
+	f = fopen(fname, "r");
+	if (!f) {
+		error_msg("stat(%s): %s",
+			  fname, ERROR_STR());
+		exit(1);
+	}
+	if (0 > fstat(fileno(f), &sb)) {
+		error_msg("stat(%s): %s", fname, ERROR_STR());
+		exit(1);
+	}
+	pos = sb.st_size;
+	if (pos < ISZ(header)
+	    || ((pos - ISZ(header)) % ISZ(RL_DATA)) != 0) {
+		error_msg("%s has invalid size %d", fname, (int)pos);
+		exit(1);
+	}
+
+	if (!clients_read(fname, f, 0, &header, sizeof(header)))
+		exit(1);
+	if (strcmp(header.magic, CLIENTS_MAGIC(0))
+	    && strcmp(header.magic, CLIENTS_MAGIC(1))) {
+		error_msg("unrecognized magic in %s", fname);
+		exit(1);
+	}
+	if (header.hash_len > RL_MIN_MAX_MAX) {
+		error_msg("unrecognized hash_len=%d in %s",
+			  header.hash_len, fname);
+		exit(1);
+	}
+	strftime(date_buf, sizeof(date_buf), "%m/%d %X",
+		 gmtime_r(&header.now, &tm));
+	strftime(date_buf2, sizeof(date_buf2), "%m/%d %X",
+		 gmtime_r(&header.cleared, &tm));
+	printf("recorded %s  cleared %s  anon delay=", date_buf, date_buf2);
+	if (header.anon_delay_us == DCC_ANON_DELAY_FOREVER) {
+		printf("forever");
+	} else {
+		printf("%d", header.anon_delay_us/1000);
+		if (header.anon_delay_inflate != DCC_ANON_INFLATE_OFF)
+			printf(",%d", header.anon_delay_inflate);
+	}
+	putchar('\n');
+
+	if (avg)
+		printf(PAT_REQUESTS_B
+		       PAT_AVGS_B
+		       PAT_DATE_ID_B
+		       PAT_VERS_B
+		       PAT_DELAY_B "\n",
+		       "ops", "nops",
+		       "averages",
+		       "  last seen", "ID",
+		       "V",
+		       "delay");
+	else
+		printf(PAT_REQUESTS_B
+		       PAT_DATE_ID_B
+		       PAT_VERS_B
+		       PAT_DELAY_B "\n",
+		       "ops", "nops",
+		       "  last seen", "ID",
+		       "V",
+		       "delay");
+
+	total = 0;
+	active = 0;
+	memset(versions, 0, sizeof(versions));
+	while ((pos -= ISZ(RL_DATA)) >= ISZ(header)) {
+		if (!clients_read(fname, f, pos, &data, sizeof(data)))
+			break;
+
+		++total;
+		if (data.requests)
+			++active;
+
+		if (data.pkt_vers < DIM(versions))
+			i = data.pkt_vers;
+		else
+			i = 0;
+		++versions[i].hosts;
+		versions[i].requests += data.requests;
+		versions[i].nops += data.nops;
+
+		if (quiet)
+			continue;
+
+		if (data.requests != 0 || data.nops != 0) {
+			printf(PAT_REQUESTS,
+			       data.requests, data.nops);
+		} else {
+			printf(PAT_REQUESTS_B, "-", "-");
+		}
+
+		if (avg) {
+			if (data.requests_avg != 0 || data.nops_avg != 0) {
+				secs = header.now - data.requests_avg_start;
+				printf(PAT_AVGS,
+				       data.requests_avg, data.nops_avg,
+				       secs / (60*60*1.0));
+			} else {
+				printf(PAT_AVGS_B, "");
+			}
+		}
+
+		strftime(date_buf, sizeof(date_buf), "%m/%d %X",
+			 gmtime_r(&data.last_used, &tm));
+		if (data.clnt_id == DCC_ID_ANON)
+			printf(PAT_DATE_ID_B, date_buf, "");
+		else if (data.clnt_id == DCC_ID_SRVR_ROGUE)
+			printf(PAT_DATE_ID_B, date_buf, "server");
+		else
+			printf(PAT_DATE_ID, date_buf, data.clnt_id,
+			       (data.flags & RL_FG_ANON) ? '*' : ' ');
+
+		if (data.pkt_vers != 0)
+			printf(PAT_VERS, data.pkt_vers);
+		else if (data.clnt_id == DCC_ID_SRVR_ROGUE)
+			printf(PAT_VERS_B, "");
+		else
+			printf(PAT_VERS_B, "?");
+
+		if (data.flags & RL_FG_PASSWD) {
+			printf(PAT_DELAY_B, "pass");
+		} else if (data.flags & RL_FG_UKN_ID) {
+			printf(PAT_DELAY_B, "ID");
+		} else if (data.flags & RL_FG_BL_ADDR) {
+			printf(PAT_DELAY_B, "A BL");
+		} else if (data.flags & RL_FG_BL_ID) {
+			printf(PAT_DELAY_B, "ID BL");
+		} else if (data.flags & RL_FG_BL_BAD) {
+			printf(PAT_DELAY_B, "BAD");
+		} else if ((data.flags & RL_FG_ANON)
+			   && header.anon_delay_inflate != 0) {
+			inflate = 1 + (RL_REQUESTS_AVG(&data)
+				       / header.anon_delay_inflate);
+			if (inflate > (DCC_ANON_DELAY_MAX
+				       / header.anon_delay_us)) {
+				delay_us = DCC_ANON_DELAY_MAX;
+			} else {
+				delay_us = inflate*header.anon_delay_us;
+			}
+			printf(PAT_DELAY, delay_us/DCC_US);
+		} else {
+			printf(PAT_DELAY_B, "");
+		}
+
+		dcc_mk_su(&su, AF_INET6, &data.clnt_addr, 0);
+		if (nonames) {
+			printf("%s\n",
+			       dcc_su2str2(sustr, sizeof(sustr), &su));
+		} else {
+			printf("%-16s %s\n",
+			       dcc_su2str2(sustr, sizeof(sustr), &su),
+			       dcc_su2name(name, sizeof(name), &su));
+		}
+	}
+
+	fclose(f);
+
+	printf("%d of %d records active\n\n", active, total);
+
+	printf("version  hosts requests     nops\n");
+	for (i = 0; i < DIM(versions); ++i) {
+		if (versions[i].hosts == 0)
+			continue;
+		printf("%6d %6d %8d %8d\n",
+		       i, versions[i].hosts,
+		       versions[i].requests, versions[i].nops);
+	}
+
+	printf("\n       *  anonymous\n");
+	printf("    pass  bad password\n");
+	printf("      ID  unknown ID\n");
+	printf("    A BL  address blacklist\n");
+	printf("   ID BL  ID blacklist\n");
+	printf("     BAD  otherwise bad\n");
+}
+
+
+
+int NRATTRIB
+main(int argc, char **argv)
+{
+	int i;
+
+	avg = 0;
+	nonames = 0;
+	quiet = 0;
+
+	while ((i = getopt(argc, argv, "anqgh:")) != -1) {
+		switch (i) {
+		case 'a':
+			++avg;
+			break;
+
+		case 'n':
+			nonames = 1;
+			break;
+
+		case 'q':
+			quiet = 1;
+			break;
+
+		case 'g':
+			grey_on = 1;
+			break;
+
+		case 'h':
+			homedir = optarg;
+			break;
+
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	dcc_cdhome(0, homedir, 1);
+
+	if (argc == 0) {
+		dump_file(CLIENTS_NM());
+	} else {
+		for (i = 0; i < argc; ++i) {
+			dump_file(argv[i]);
+		}
+	}
+
+	exit(0);
+}
diff -r 000000000000 -r c7f6b056b673 dccd/iflod.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd/iflod.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,2627 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * deal with incoming floods of checksums
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.249 $Revision$
+ */
+
+#include "dccd_defs.h"
+#include <sys/wait.h>
+
+
+IFLODS iflods;
+
+u_int complained_many_iflods;
+
+time_t got_hosts;
+pid_t resolve_hosts_pid = -1;
+
+time_t iflods_ok_timer;			/* incoming flooding ok since then */
+
+int flod_trace_gen;			/* unsuppress tracing */
+
+static u_char iflod_write(IFLOD_INFO *, void *, int, const char *, u_char);
+
+
+static DCC_TS future;			/* timestamp sanity */
+
+
+
+ID_MAP_RESULT
+id_map(DCC_SRVR_ID srvr, const OFLOD_OPTS *opts)
+{
+	int i;
+	ID_MAP_RESULT result;
+
+	/* apply the ***first*** server-ID map that matches, if any */
+	for (i = 0; i < opts->num_maps; ++i) {
+		if (opts->srvr_map[i].from_lo <= srvr
+		    && opts->srvr_map[i].from_hi >= srvr) {
+			result = opts->srvr_map[i].result;
+			if (result == ID_MAP_SELF
+			    && srvr == my_srvr_id)
+				return ID_MAP_NO;
+			return result;
+		}
+	}
+	return ID_MAP_NO;
+}
+
+
+
+static const char *
+rem_str(const char *hostname, const DCC_SOCKU *su)
+{
+	static int bufno;
+	static struct {
+	    char    str[90];
+	} bufs[4];
+	char *s;
+	char sustr[DCC_SU2STR_SIZE];
+	int i;
+
+	s = bufs[bufno].str;
+	bufno = (bufno+1) % DIM(bufs);
+
+	STRLCPY(s, hostname, sizeof(bufs[0].str));
+
+	if (su->sa.sa_family == AF_UNSPEC
+	    && *DCC_SU_PORTP(su) == 0)
+		return s;
+
+	dcc_su2str2(sustr, sizeof(sustr), su);
+	if (strcmp(s, sustr)) {
+		STRLCAT(s, " ", sizeof(bufs[0].str));
+		STRLCAT(s, sustr, sizeof(bufs[0].str));
+	}
+	if (*DCC_SU_PORTP(su) != DCC_GREY2PORT(grey_on)) {
+		i = strlen(s);
+		snprintf(s+i, sizeof(bufs[0].str)-i,
+			 ",%d", ntohs(*DCC_SU_PORTP(su)));
+	}
+	return s;
+}
+
+
+
+const char *
+ifp_rem_str(const IFLOD_INFO *ifp)
+{
+	if (!ifp)
+		return "(null ifp)";
+	return rem_str(ifp->rem_hostname, &ifp->rem_su);
+}
+
+
+
+const char *
+ofp_rem_str(const OFLOD_INFO *ofp)
+{
+	if (!ofp)
+		return "(null ofp)";
+	return rem_str(ofp->rem_hostname, &ofp->rem_su);
+}
+
+
+
+static const char *
+rpt_id(const char *type, const DB_RCD* rcd,
+       const IFLOD_INFO *ifp)
+{
+	static int bufno;
+	static struct {
+	    char    str[120];
+	} bufs[4];
+	char *s;
+	char id_buf[30];
+
+	s = bufs[bufno].str;
+	bufno = (bufno+1) % DIM(bufs);
+
+	snprintf(s, sizeof(bufs[0].str), "%s%s %s ID=%s %s%s",
+		 type ? "flooded " : "",
+		 type ? type : "",
+		 ts2str_err(&rcd->ts),
+		 id2str(id_buf, sizeof(id_buf), rcd->srvr_id_auth),
+		 ifp ? "from " : "",
+		 ifp ? ifp_rem_str(ifp) : "");
+	return s;
+}
+
+
+
+void PATTRIB(2,3)
+flod_cnterr(const FLOD_LIMCNT *lc, const char *p, ...)
+{
+	char buf[200];
+	va_list args;
+
+	va_start(args, p);
+	if (lc->cur < lc->lim + FLOD_LIM_COMPLAINTS) {
+		dcc_verror_msg(p, args);
+	} else {
+		vsnprintf(buf, sizeof(FLOD_EMSG), p, args);
+		dcc_error_msg("%s; stop complaints", buf);
+	}
+	va_end(args);
+}
+
+
+
+static void
+date_err_msg(FLOD_EMSG out, int len, const char *in)
+{
+	memcpy(out, in, len);
+	if (len >= ISZ(FLOD_EMSG)-LITZ(" at hh:mm:ss")-1) {
+		out[len] = '\0';
+	} else {
+		dcc_time2str(&out[len], ISZ(FLOD_EMSG)-len, " at %T",
+			     db_time.tv_sec);
+	}
+}
+
+
+
+/* remove extra quotes and strings from a message to or from a peer */
+static void
+trim_err_msg(FLOD_EMSG out, const FLOD_EMSG in)
+{
+	char *q1, *q2;
+	int len;
+
+	q1 = strchr(in, '\'');
+	if (q1) {
+		q2 = strrchr(++q1, '\'');
+		if (q2) {
+			len = q2-q1;
+			if (len > LITZ(DCC_FLOD_OK_STR)
+			    && !LITCMP(q1, DCC_FLOD_OK_STR)) {
+				len -= LITZ(DCC_FLOD_OK_STR);
+				q1 += LITZ(DCC_FLOD_OK_STR);
+			}
+			date_err_msg(out, len, q1);
+			return;
+		}
+	}
+	date_err_msg(out, strlen(in), in);
+}
+
+
+
+/* report a flooding error */
+void PATTRIB(4,5)
+rpt_err(OFLOD_INFO *ofp,
+	u_char trace,			/* 0=error, 1=trace, 2=no dup trace */
+	u_char in,			/* 0=output 1=input */
+	const char *p, ...)
+{
+	FLOD_MMAP *mp;
+	LAST_ERROR *ep;
+	FLOD_EMSG tmp, trimmed;
+	va_list args;
+
+	va_start(args, p);
+	vsnprintf(tmp, sizeof(FLOD_EMSG), p, args);
+	va_end(args);
+
+	mp = ofp ? ofp->mp : 0;
+
+	if (trace != 0) {
+		if (!mp) {
+			TMSG_FLOD(ofp, tmp);
+			return;
+		}
+
+		trim_err_msg(trimmed, tmp);
+		ep = in ? &mp->iflod_err : &mp->oflod_err;
+		if (TMSG_FB(ofp)
+		    && (trace < 2
+			|| strcmp(trimmed, ep->trace_msg)
+			|| ep->trace_gen != flod_trace_gen)) {
+			/* suppress some duplicate flooding messages */
+			dcc_trace_msg(tmp);
+			ep->trace_gen = flod_trace_gen;
+		}
+		strncpy(ep->trace_msg, trimmed, sizeof(ep->trace_msg));
+		ep->complained = 0;
+
+	} else {
+		if (!mp) {
+			dcc_error_msg(tmp);
+			return;
+		}
+
+		dcc_error_msg(tmp);
+
+		ep = in ? &mp->iflod_err : &mp->oflod_err;
+		trim_err_msg(ep->msg, tmp);
+		ep->trace_msg[0] = '\0';
+		ep->complained = 0;
+	}
+}
+
+
+
+u_char
+set_flod_socket(OFLOD_INFO *ofp, u_char in, int s,
+		const char *hostname, const DCC_SOCKU *sup)
+{
+#if IP_TOS
+	static u_char tos_ok = 1;
+#endif
+	int on;
+
+	if (0 > fcntl(s, F_SETFD, FD_CLOEXEC))
+		rpt_err(ofp, 0, in, "fcntl(%s, F_SETFD, FD_CLOEXEC): %s",
+			rem_str(hostname, sup), ERROR_STR());
+
+	if (-1 == fcntl(s, F_SETFL,
+			fcntl(s, F_GETFL, 0) | O_NONBLOCK)) {
+		rpt_err(ofp, 0, in, "fcntl(%s, O_NONBLOCK): %s",
+			rem_str(hostname, sup), ERROR_STR());
+		return 0;
+	}
+
+	on = 1;
+	if (0 > setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
+			   &on, sizeof(on)))
+		rpt_err(ofp, 0, in, "setsockopt(flod %s, SO_KEEPALIVE): %s",
+			rem_str(hostname, sup), ERROR_STR());
+
+	if (in) {
+		/* Ensure that we have enough socket buffer space to send
+		 * complaints about the input flood.  Normally little or
+		 * nothing is sent upstream, but bad clocks or other
+		 * problems can cause many complaints. */
+		if (0 > setsockopt(s, SOL_SOCKET, SO_SNDBUF,
+				   &srvr_rcvbuf, sizeof(srvr_rcvbuf)))
+			rpt_err(ofp, 0, in, "setsockopt(%s, SO_SNDBUF): %s",
+				rem_str(hostname, sup), ERROR_STR());
+	}
+
+#ifdef IP_TOS
+	/* It would be nice and clean to use netinet/ip.h for the definition
+	 * of IPTOS_THROUGHPUT.  However, it is hard to use netinet/ip.h
+	 * portably because in_sysm.h is required for n_long on some
+	 * systems and not others.  A bunch of messy ./configure fiddling
+	 * might patch that hassle, but the bit really ought to be the same
+	 * as the old 0x08 in the IPv4 header. */
+	if (sup->sa.sa_family == AF_INET
+	    && tos_ok) {
+		on = 0x08;		/* IPTOS_THROUGHPUT */
+		if (0 > setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(on))) {
+			rpt_err(ofp, 0, in,
+				"setsockopt(IP_TOS, IPTOS_THROUGHPUT, %s): %s",
+				rem_str(hostname, sup), ERROR_STR());
+			tos_ok = 0;
+		}
+	}
+#endif
+
+	return 1;
+}
+
+
+
+/* see if the host name resolution process is still running */
+u_char					/* 1=not running 0=please wait */
+flod_names_resolve_ck(void)
+{
+	int status;
+
+	if (resolve_hosts_pid < 0)
+		return 1;
+
+	if (resolve_hosts_pid == waitpid(resolve_hosts_pid, &status, WNOHANG)) {
+		resolve_hosts_pid = -1;
+		return 1;
+	}
+
+	RUSH_NEXT_FLODS_CK();
+	return 0;
+}
+
+
+
+static void
+flod_names_resolve(void)
+{
+	FLOD_MMAP *mp;
+	u_char ipv6, ok;
+	const DCC_SOCKU *sup;
+
+	for (mp = flod_mmaps->mmaps; mp <= LAST(flod_mmaps->mmaps); ++mp) {
+		if (mp->rem_hostname[0] == '\0'
+		    || (mp->flags & FLODMAP_FG_PASSIVE))
+			continue;
+		ipv6 = ((mp->flags & FLODMAP_FG_IPv4) ? 0
+			: (mp->flags & FLODMAP_FG_IPv6) ? 1
+			: use_ipv6 ? 2 : 0);
+		dcc_host_lock();
+		if (mp->flags & FLODMAP_FG_SOCKS)
+			ok = dcc_get_host_SOCKS(mp->rem_hostname, ipv6,
+						&mp->host_error);
+		else
+			ok = dcc_get_host(mp->rem_hostname, ipv6,
+					  &mp->host_error);
+		if (!ok) {
+			TMSG2(FLOD, "failed to resolve %s: %s",
+			      mp->rem_hostname, DCC_HSTRERROR(mp->host_error));
+		} else {
+			for (sup = dcc_hostaddrs;
+			     sup < dcc_hostaddrs_end;
+			     ++sup) {
+				if ((ipv6 == 0
+				     && sup->sa.sa_family != AF_INET)
+				    || (ipv6 == 1
+					&& sup->sa.sa_family != AF_INET6))
+					continue;
+				mp->rem_su = *sup;
+				*DCC_SU_PORTP(&mp->rem_su) = mp->rem_port;
+			}
+		}
+		dcc_host_unlock();
+	}
+}
+
+
+
+/* start a process to wait for the domain name system or other
+ * hostname system to get the IP addresses of our flooding peers */
+u_char					/* 1=finished 0=please wait */
+flod_names_resolve_start(void)
+{
+	FLOD_MMAP *mp;
+
+	if (!flod_mmaps)
+		return 0;
+
+	/* wait for background job to finish */
+	if (!flod_names_resolve_ck())
+		return 0;
+
+	/* we're finished if we have recent address for all of the names */
+	if (!DB_IS_TIME(got_hosts, FLOD_NAMES_RESOLVE_SECS))
+		return 1;
+
+	got_hosts = db_time.tv_sec + FLOD_NAMES_RESOLVE_SECS;
+	for (mp = flod_mmaps->mmaps; mp <= LAST(flod_mmaps->mmaps); ++mp) {
+		mp->rem_su.sa.sa_family = AF_UNSPEC;
+		mp->host_error = 0;
+	}
+	flod_mmap_sync(0, 1);
+
+	if (!background) {
+		TMSG(FLOD, "resolving hostnames in the foreground");
+		flod_names_resolve();
+		return 1;
+	}
+
+	resolve_hosts_pid = fork();
+	if (resolve_hosts_pid > 0) {
+		/* check again soon */
+		RUSH_NEXT_FLODS_CK();
+		return 0;
+	}
+
+	if (resolve_hosts_pid == -1) {
+		dcc_error_msg("fork(flood names resolve start): %s;"
+			      " fall back to foreground resolving",
+			      ERROR_STR());
+		flod_names_resolve();
+		return 1;
+	}
+
+	TMSG(FLOD, "resolving hostnames started");
+	/* close files and sockets to avoid interfering with parent */
+	db_close(-1);
+	after_fork();
+
+	flod_names_resolve();
+
+	TMSG(FLOD, "resolving hostnames finished");
+
+	exit(0);
+}
+
+
+
+static void
+iflod_clear(IFLOD_INFO *ifp,
+	    u_char fail)		/* 1=problems so delay restart */
+{
+	OFLOD_INFO *ofp;
+	FLOD_MMAP *mp;
+
+	ofp = ifp->ofp;
+	if (fail && ofp != 0) {
+		mp = ofp->mp;
+		if (mp->itimers.retry_secs < FLOD_RETRY_SECS)
+			mp->itimers.retry_secs = FLOD_RETRY_SECS;
+		mp->itimers.retry = db_time.tv_sec + mp->itimers.retry_secs;
+		if ((mp->flags & FLODMAP_FG_ACT) != 0)
+			TMSG3_FLOD(ofp,
+				   "postpone restarting %s flood from"
+				   " %s for %d seconds",
+				   (mp->flags & FLODMAP_FG_SOCKS)
+				   ? "SOCKS"
+				   : (mp->flags & FLODMAP_FG_NAT)
+				   ? "NAT"
+				   : "auto-NAT",
+				   ofp_rem_str(ofp), mp->itimers.retry_secs);
+	}
+
+	/* do not close the socket here, because it may be a passive outgoing
+	 * stream that is being converted  */
+	if (ifp->soc >= 0)
+		--iflods.open;
+
+	memset(ifp, 0, sizeof(*ifp));
+	ifp->soc = -1;
+
+	if (iflods.open == 0
+	    && oflods.open == 0
+	    && flods_st != FLODS_ST_ON)
+		oflods_clear();
+}
+
+
+
+void PATTRIB(5,6)
+iflod_close(IFLOD_INFO *ifp,
+	    u_char fail,		/* 1=already sick; more is no problem */
+	    u_char complain,		/* 1=error not just trace message */
+	    u_char send_reason,
+	    const char *pat, ...)
+{
+	struct {
+	    DCC_FLOD_POS    last_pos;
+	    DCC_FLOD_RESP   e;
+	    u_char	    null;	/* to eat '\0' for e.msg */
+	} resp;
+	void *wp;
+	va_list args;
+	OFLOD_INFO *ofp;
+	struct linger nowait;
+	int wlen;
+
+	ofp = ifp->ofp;
+
+	db_ptr2flod_pos(resp.e.end.pos, DCC_FLOD_POS_END);
+	va_start(args, pat);
+	/* Throw away the last byte of resp.e.end.msg because the too smart
+	 * by half gcc Fortify nonsense won't allow ISZ(resp.e.end.msg)+1.
+	 * That put the would the unwanted '\0' from vsnprintf() into
+	 * resp.null*/
+	wlen = vsnprintf(resp.e.end.msg, ISZ(resp.e.end.msg), pat, args);
+	va_end(args);
+	if (wlen > ISZ(resp.e.end.msg))
+		wlen = ISZ(resp.e.end.msg);
+	wlen += FLOD_END_OVHD;
+
+	/* If useful, prefix our final message with our final position
+	 * The peer will see our position and final operation as two
+	 * separate responses. */
+	if (memcmp(ifp->pos, ifp->pos_sent, ISZ(ifp->pos))) {
+		memcpy(resp.last_pos, ifp->pos, ISZ(resp.last_pos));
+		wlen += ISZ(resp.last_pos);
+		wp = &resp.last_pos;
+	} else {
+		wp = &resp.e;
+	}
+
+	if (send_reason) {
+		rpt_err(ofp, !complain, 1,
+			"stop incoming flood; %ssend '%s' to %s",
+			fail ? "error, " : "",
+			resp.e.end.msg,
+			ifp_rem_str(ifp));
+
+		/* send the final status report to the sending flooder */
+		iflod_write(ifp, wp, wlen, "stop incoming flood", fail ? 2 : 1);
+	} else {
+		rpt_err(ofp, !complain, 1,
+			"stop incoming flood; %s'%s'",
+			fail ? "error, " : "", resp.e.end.msg);
+	}
+
+	if (ifp->soc >= 0) {
+		if (stopint
+		    && !(ifp->flags & IFLOD_FG_FAST_LINGER)) {
+			ifp->flags |= IFLOD_FG_FAST_LINGER;
+			nowait.l_onoff = 1;
+			nowait.l_linger = SHUTDOWN_DELAY;
+			if (0 > setsockopt(ifp->soc, SOL_SOCKET, SO_LINGER,
+					   &nowait, sizeof(nowait))
+			    && !fail)
+				dcc_error_msg("setsockopt(SO_LINGER %s): %s",
+					      ifp_rem_str(ifp), ERROR_STR());
+		}
+
+		if (0 > close(ifp->soc)
+		    && !fail) {
+			if (errno == ECONNRESET)
+				TMSG2_FLOD(ofp, "close(flood from %s): %s",
+					   ifp_rem_str(ifp), ERROR_STR());
+			else
+				dcc_error_msg("close(flood from %s): %s",
+					      ifp_rem_str(ifp), ERROR_STR());
+		}
+	}
+
+	/* if this was not a new duplicate connection being discarded,
+	 * break the association with the outgoing stream */
+	if (ofp != 0 && ofp->ifp == ifp) {
+		save_flod_cnts(ofp);
+		ofp->ifp = 0;
+	}
+
+	iflod_clear(ifp, fail);
+}
+
+
+
+/* can close the incoming flood and so clear things */
+static u_char				/* 0=failed & should be or is closed */
+iflod_write(IFLOD_INFO *ifp,
+	    void *buf, int buf_len,
+	    const char *type,		/* string describing operation */
+	    u_char close_it)		/* 0=iflod_close() on error, */
+{					/* 1=complain, 2=ignore error */
+	int i;
+
+	if (!(ifp->flags & IFLOD_FG_CONNECTED))
+		return 1;
+
+	if (ifp->ofp
+	    && (ifp->ofp->o_opts.flags & FLOD_OPT_SOCKS)) {
+		i = Rsend(ifp->soc, buf, buf_len, 0);
+	} else {
+		/* If we don't know the corresponding output stream because we
+		 * have not yet seen any authentication, we at least know the
+		 * connection did not involve SOCKS because we did not
+		 * originate it. */
+		i = send(ifp->soc, buf, buf_len, 0);
+	}
+	if (i == buf_len) {
+		ifp->iflod_alive = db_time.tv_sec;
+		return 1;
+	}
+
+	if (i < 0) {
+		if (close_it == 0) {
+			iflod_close(ifp, 1, 0, 0, "send(%s %s): %s",
+				    type, ifp_rem_str(ifp), ERROR_STR());
+		} else if (close_it == 1) {
+			dcc_error_msg("send(%s %s): %s",
+				      type, ifp_rem_str(ifp), ERROR_STR());
+		}
+	} else {
+		if (close_it == 0) {
+			iflod_close(ifp, 1, 0, 0, "send(%s %s)=%d not %d",
+				    type, ifp_rem_str(ifp), i, buf_len);
+		} else if (close_it == 1) {
+			dcc_error_msg("send(%s %s)=%d not %d",
+				      type, ifp_rem_str(ifp), i, buf_len);
+		}
+	}
+	return 0;
+}
+
+
+
+/* send our current position to the peer
+ *	the peer must be well known so that ifp->ofp->mp!=0, usually
+ *	    because (ifp->flags & IFLOD_FG_VERS_CK)
+ *	can close the incoming flood and so clear things */
+int					/* -1=fail 0=nothing to send, 1=sent */
+iflod_send_pos(IFLOD_INFO *ifp,
+	       u_char force)		/* say just anything */
+{
+	DCC_FLOD_POS req;
+	OFLOD_INFO *ofp;
+	FLOD_MMAP *mp;
+
+	ofp = ifp->ofp;
+	mp = ofp->mp;
+
+	/* ask peer to start over if our database has been cleared */
+	if (mp->flags & FLODMAP_FG_FFWD_IN) {
+		mp->flags &= ~(FLODMAP_FG_FFWD_IN
+			       | FLODMAP_FG_NEED_REWIND);
+		memcpy(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent));
+		db_ptr2flod_pos(req, DCC_FLOD_POS_FFWD_IN);
+		dcc_trace_msg("ask %s to FFWD flood to us",
+			      ifp_rem_str(ifp));
+		if (!iflod_write(ifp, req, sizeof(req), "ffwd request", 0))
+			return -1;
+		return 1;
+	}
+	if (mp->flags & FLODMAP_FG_NEED_REWIND) {
+		mp->flags &= ~FLODMAP_FG_NEED_REWIND;
+		memcpy(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent));
+		db_ptr2flod_pos(req, DCC_FLOD_POS_REWIND);
+		dcc_trace_msg("ask %s to rewind flood to us",
+			      ifp_rem_str(ifp));
+		if (!iflod_write(ifp, req, sizeof(req), "rewind request", 0))
+			return -1;
+		return 1;
+	}
+
+	if ((force && flod_pos2db_ptr(ifp->pos) >= DCC_FLOD_POS_MIN)
+	    || memcmp(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent))) {
+		memcpy(ifp->pos_sent, ifp->pos, sizeof(ifp->pos_sent));
+		if (!iflod_write(ifp, ifp->pos_sent, sizeof(ifp->pos_sent),
+				 "confirmed pos", 0))
+			return -1;
+
+		/* reset the no-connection-from-peer complaint delay */
+		mp->itimers.msg_secs = FLOD_IN_COMPLAIN1;
+		mp->itimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN1;
+
+		/* things are going well, so forget old input complaints */
+		if (!(mp->flags & FLODMAP_FG_IN_SRVR)) {
+			mp->iflod_err.msg[0] = '\0';
+			mp->iflod_err.trace_msg[0] = '\0';
+		}
+
+		/* limit the backoff for outgoing connection attempts
+		 * while the incoming connection is working */
+		DB_ADJ_TIMER(&mp->otimers.retry, &mp->otimers.retry_secs,
+			     FLOD_RETRY_SECS);
+
+		return 1;
+	}
+
+	/* Say just anything if we are doing a keepalive probe before
+	 * any checksums have been sent by the peer and so before we
+	 * have a position to confirm. */
+	if (force) {
+		DCC_FLOD_RESP buf;
+
+		db_ptr2flod_pos(buf.note.op, DCC_FLOD_POS_NOTE);
+		strcpy(buf.note.str, "are you there?");
+		buf.note.len = sizeof("are you there?") + FLOD_NOTE_OVHD;
+		TMSG1_FLOD(ofp, "flood note to %s: \"are you there?\"",
+			   ifp_rem_str(ifp));
+		if (!iflod_write(ifp, &buf.note, buf.note.len, buf.note.str, 0))
+			return -1;
+		return 1;
+	}
+
+	return 0;
+}
+
+
+
+void
+iflod_listen_close(SRVR_SOC *sp)
+{
+	if (sp->listen < 0)
+		return;
+
+	TMSG1(FLOD, "stop flood listening on %s", dcc_su2str_err(&sp->su));
+	if (0 > close(sp->listen))
+		TMSG2(FLOD, "close(flood listen on %s): %s",
+		      dcc_su2str_err(&sp->su), ERROR_STR());
+	sp->listen = -1;
+}
+
+
+
+/* send stop requests to DCC servers flooding to us
+ *	can close the incoming flood and so clear things */
+void
+iflods_stop(const char *reason,
+	    u_char force)		/* 1=now */
+{
+	SRVR_SOC *sp;
+	IFLOD_INFO *ifp;
+	DCC_FLOD_POS end_req;
+
+	/* stop listening for new connections */
+	for (sp = srvr_socs; sp; sp = sp->fwd) {
+		iflod_listen_close(sp);
+	}
+
+	for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
+		if (ifp->soc < 0)
+			continue;
+
+		/* start shutting down each real, still alive connection */
+		if (!(ifp->flags & IFLOD_FG_END_REQ)
+		    && (ifp->flags & IFLOD_FG_VERS_CK)) {
+			if (!reason || !*reason)
+				rpt_err(ifp->ofp, 1, 1,
+					"flood from %s stopping",
+					ifp_rem_str(ifp));
+			else
+				rpt_err(ifp->ofp, 1, 1,
+					"flood from %s stopping: '%s'",
+					ifp_rem_str(ifp), reason);
+
+			/* send any delay position and then a stop request */
+			if (0 <= iflod_send_pos(ifp, 0)) {
+				db_ptr2flod_pos(end_req, DCC_FLOD_POS_END_REQ);
+				iflod_write(ifp, end_req, sizeof(end_req),
+					    "flood stop req", 0);
+				ifp->flags |= IFLOD_FG_END_REQ;
+			}
+
+			/* done if the socket died */
+			if (ifp->soc < 0)
+				continue;
+		}
+
+		/* break the connection if forced or never authenticated */
+		if (force || !(ifp->flags & IFLOD_FG_VERS_CK)) {
+			if (!reason || !*reason)
+				iflod_close(ifp, 1, 0, 1,
+					    "flooding off at %s",
+					    our_hostname);
+			else
+				iflod_close(ifp, 1, 0, 1,
+					    "flooding off at %s; %s",
+					    our_hostname, reason);
+			continue;
+		}
+
+		/* break the conneciton if the peer is too slow */
+		if ((ifp->flags & IFLOD_FG_END_REQ)
+		    && IFP_DEAD(ifp, stopint
+				? SHUTDOWN_DELAY : KEEPALIVE_IN_STOP)) {
+			if (!reason || !*reason)
+				iflod_close(ifp, 1, 0, 1,
+					    DCC_FLOD_OK_STR" force close from"
+					    " %s",
+					    ifp_rem_str(ifp));
+			else
+				iflod_close(ifp, 1, 0, 1,
+					    DCC_FLOD_OK_STR" force close from"
+					    " %s; %s",
+					    ifp_rem_str(ifp), reason);
+			continue;
+		}
+	}
+}
+
+
+
+/* start receiving checksums from another DCC server */
+void
+iflod_start(SRVR_SOC *sp)
+{
+	IFLOD_INFO *ifp;
+	DCC_SOCKLEN_T l;
+	struct in6_addr peer_addr;
+	int count;
+	const struct in6_addr *cap;
+	RL *rl;
+
+	/* accept all waiting connections to avoid starving any	*/
+	for (count = 0; count <= DCCD_MAX_FLOODS; ++count) {
+		/* find a free input flooding slot */
+		for (ifp = iflods.infos; ifp->soc >= 0; ++ifp) {
+			if (ifp > LAST(iflods.infos)) {
+				if (!(complained_many_iflods++))
+					dcc_error_msg("too many floods");
+				goto again;
+			}
+		}
+
+		l = sizeof(ifp->rem_su);
+		ifp->soc = accept(sp->listen, &ifp->rem_su.sa, &l);
+		if (ifp->soc < 0) {
+			if (!DCC_BLOCK_ERROR() || count == 0)
+				dcc_error_msg("accept(flood): %s", ERROR_STR());
+			return;
+		}
+
+		/* use the IP address as the host name until we know which
+		 * peer it is */
+		dcc_su2str2(ifp->rem_hostname, sizeof(ifp->rem_hostname),
+			    &ifp->rem_su);
+		if (!set_flod_socket(0, 1, ifp->soc, ifp->rem_hostname,
+				     &ifp->rem_su)) {
+			close(ifp->soc);
+			ifp->soc = -1;
+			continue;
+		}
+
+		/* quietly forget this peer if it is blacklisted */
+		if (ifp->rem_su.sa.sa_family == AF_INET6) {
+			cap = &ifp->rem_su.ipv6.sin6_addr;
+		} else {
+			dcc_ipv4toipv6(&peer_addr, ifp->rem_su.ipv4.sin_addr);
+			cap = &peer_addr;
+		}
+		rl = 0;
+		if (ck_ip_bl(&rl, DCC_ID_SRVR_ROGUE, cap)) {
+			char buf[120];
+			int len;
+
+			rl_inc(rl, &rl_anon_rate);
+			len = snprintf(buf, sizeof(buf)-1,
+				       "rejected blacklisted flood from %s",
+				       ifp_rem_str(ifp));
+			if (len > ISZ(buf))
+				len = sizeof(buf);
+			if ((rl->d.flags & RL_FG_TRACE)
+			    || (dccd_tracemask & DCC_TRACE_FLOD_BIT))
+				dcc_trace_msg(buf);
+			buf[len++] = '\n';
+			send(ifp->soc, buf, len, 0);
+			close(ifp->soc);
+			ifp->soc = -1;
+			continue;
+		}
+
+		/* reset timer that delays responses to clients while flooding
+		 * is stopped */
+		if (++iflods.open == 1)
+			iflods_ok_timer = db_time.tv_sec + IFLODS_OK_SECS;
+
+		ifp->flags |= IFLOD_FG_CONNECTED;
+		ifp->iflod_alive = db_time.tv_sec;
+
+		TMSG1(FLOD, "start flood from %s", ifp_rem_str(ifp));
+again:;
+	}
+}
+
+
+
+static void
+iflod_socks_backoff(OFLOD_INFO *ofp)
+{
+	FLOD_MMAP *mp;
+
+	mp = ofp->mp;
+	mp->itimers.retry_secs *= 2;
+	if (mp->itimers.retry_secs > FLOD_MAX_RETRY_SECS)
+		mp->itimers.retry_secs = FLOD_MAX_RETRY_SECS;
+	else if (mp->itimers.retry_secs < FLOD_RETRY_SECS)
+		mp->itimers.retry_secs = FLOD_RETRY_SECS;
+}
+
+
+
+/* Start an incoming SOCKS flood by connecting to the other system.
+ *	We will eventually turn the connection around and pretend that
+ *	the other system initiated the TCP connection.
+ *	This can close the flood and so clear things */
+static int				/* -1=failure, 0=not yet, 1=done */
+iflod_socks_connect(IFLOD_INFO *ifp)
+{
+	OFLOD_INFO *ofp;
+	DCC_SRVR_ID id;
+	DCC_FLOD_VERSION_HDR buf;
+	DCC_FNM_LNO_BUF fnm_buf;
+	const char *emsg;
+	int i;
+
+	ofp = ifp->ofp;
+
+	memset(&buf, 0, sizeof(buf));
+	strcpy(buf.body.str, version_str(ofp));
+	id = htons(my_srvr_id);
+	memcpy(buf.body.sender_srvr_id, &id, sizeof(buf.body.sender_srvr_id));
+	buf.body.turn = 1;
+	emsg = flod_sign(ofp, 1, &buf, sizeof(buf));
+	if (emsg) {
+		iflod_socks_backoff(ofp);
+		iflod_close(ifp, 1, 1, 1, "%s %d%s",
+			    emsg, ofp->out_passwd_id,
+			    fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		return -1;
+	}
+
+	if (ofp->o_opts.flags & FLOD_OPT_SOCKS) {
+		i = Rconnect(ifp->soc, &ifp->rem_su.sa,
+			     DCC_SU_LEN(&ifp->rem_su));
+	} else {
+		/* must be NAT or assumed NAT */
+		i = connect(ifp->soc, &ifp->rem_su.sa,
+			    DCC_SU_LEN(&ifp->rem_su));
+	}
+	if (0 > i && errno != EISCONN) {
+		if (errno == EAGAIN
+		    || errno == EINPROGRESS
+		    || errno == EALREADY) {
+			rpt_err(ofp, 1, 0, "starting flood from %s",
+				ifp_rem_str(ifp));
+			return 0;
+		}
+
+		/* it is lame to only trace instead of reporting EINVAL as
+		 * an error, but several UNIX-like systems return EINVAL for
+		 * the second connect() after a Unreachable ICMP message
+		 * or after a timeout */
+		rpt_err(ofp,
+			(errno == EINVAL || errno == ECONNABORTED
+			 || errno == ECONNRESET || errno == ETIMEDOUT
+			 || errno == ECONNREFUSED),
+			1, "connect(SOCKS FLOD %s): %s",
+			ifp_rem_str(ifp),
+			errno == EINVAL
+			? "likely connection refused or local firewall"
+			: ERROR_STR());
+		close(ifp->soc);
+		iflod_socks_backoff(ofp);
+		iflod_clear(ifp, 1);
+
+		return -1;
+	}
+
+	ifp->flags |= (IFLOD_FG_CONNECTED | IFLOD_FG_CLIENT);
+
+	rpt_err(ofp, 1, 1, "starting SOCKS from %s", ifp_rem_str(ifp));
+
+	/* After the SOCKS incoming flood socket is connected,
+	 *	send our authentication to convince the peer to send its
+	 *	authentication and then its checksums. */
+	if (!iflod_write(ifp, &buf, sizeof(buf),
+			 "flood SOCKS authentication", 0))
+		return -1;
+	return 1;
+}
+
+
+
+/* Request the start of an input flood via SOCKS if it is not already flowing,
+ *	by connecting to the remote system.
+ *	This can close the flood and so clear things */
+void
+iflod_socks_start(OFLOD_INFO *ofp)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	IFLOD_INFO *ifp, *ifp1;
+
+	/* do nothing if it is already running or is not using SOCKS */
+	if (ofp->ifp
+	    || (ofp->mp->flags & (FLODMAP_FG_SOCKS | FLODMAP_FG_NAT)) == 0
+	    || IFLOD_OPT_OFF_ROGUE(ofp))
+		return;
+	if (!DB_IS_TIME(ofp->mp->itimers.retry, ofp->mp->itimers.retry_secs))
+		return;
+
+	/* look for a free slot or an existing slot for the incoming flood */
+	ifp = 0;
+	for (ifp1 = iflods.infos; ifp1 <= LAST(iflods.infos); ++ifp1) {
+		if (ifp1->soc < 0) {
+			if (!ifp)
+				ifp = ifp1;
+		}
+		/* there is nothing to do if it already exists */
+		if (ifp1->ofp == ofp)
+			return;
+	}
+	if (!ifp) {
+		rpt_err(ofp, ++complained_many_iflods == 1 ? 0 : 2, 1,
+			"too many incoming floods to start SOCKS from %s",
+			ofp->rem_hostname);
+		iflod_socks_backoff(ofp);
+		return;
+	}
+
+	if (!flod_names_resolve_start())
+		return;			/* wait for name resolution */
+	if (ofp->mp->rem_su.sa.sa_family == AF_UNSPEC) {
+		rpt_err(ofp, 0, 1, "SOCKS flood peer name %s: %s%s",
+			ofp->rem_hostname, DCC_HSTRERROR(ofp->mp->host_error),
+			fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		iflod_socks_backoff(ofp);
+		return;
+	}
+	if (!LITCMP(ofp->mp->oflod_err.msg, "SOCKS flood peer name "))
+		ofp->mp->oflod_err.msg[0] = '\0';
+
+	ifp->ofp = ofp;
+	ifp->rem_su = ofp->rem_su = ofp->mp->rem_su;
+	STRLCPY(ifp->rem_hostname, ofp->rem_hostname,
+		sizeof(ifp->rem_hostname));
+
+	ifp->soc = socket(ifp->rem_su.sa.sa_family, SOCK_STREAM, 0);
+	if (ifp->soc < 0) {
+		rpt_err(ofp, 0, 1, "socket(SOCKS flood %s): %s",
+			ifp_rem_str(ifp), ERROR_STR());
+		iflod_socks_backoff(ofp);
+		return;
+	}
+
+	if (!set_flod_socket(ofp, 1, ifp->soc,
+			     ifp->rem_hostname, &ifp->rem_su)) {
+		close(ifp->soc);
+		ifp->soc = -1;
+		iflod_socks_backoff(ofp);
+		return;
+	}
+
+	/* reset timer that delays responses to clients while flooding is
+	 * not working */
+	if (++iflods.open == 1)
+		iflods_ok_timer = db_time.tv_sec + IFLODS_OK_SECS;
+
+	iflod_socks_connect(ifp);
+}
+
+
+
+/* See if the new report is a duplicate of an old record in the database
+ *	db_sts.rcd.d points to the old record */
+static int				/* -1=continue, 0=not dup, 1=dup */
+ck_dup_rcd(IFLOD_INFO *ifp,
+	   const DB_RCD *new, DCC_TGTS new_tgts_raw)
+{
+	DCC_TGTS old_tgts;
+	const DB_RCD_CK *new_ck, *old_ck;
+	int new_num_cks, old_num_cks;
+	int new_unique, old_unique;
+
+	/* ignore reports for deleted checksums
+	 * unless they are new reports or delete requests */
+	old_tgts = DB_TGTS_RCD_RAW(db_sts.rcd.d.r);
+	if (old_tgts == DCC_TGTS_DEL
+	    && new_tgts_raw != DCC_TGTS_DEL
+	    && !dcc_ts_newer_ts(&new->ts, &db_sts.rcd.d.r->ts)) {
+		if (CK_FLOD_CNTERR(&ifp->ofp->lc.stale)
+		    && TMSG_FB2(ifp->ofp))
+			flod_cnterr(&ifp->ofp->lc.stale,
+				    "ignore deleted %s after %s",
+				    rpt_id("report", new, ifp),
+				    rpt_id(0, db_sts.rcd.d.r, 0));
+		return 1;
+	}
+
+	/* not duplicate if the server-IDs or timestamps differ */
+	if (DB_RCD_ID(new) != DB_RCD_ID(db_sts.rcd.d.r)
+	    || memcmp(&new->ts, &db_sts.rcd.d.r->ts, sizeof(new->ts)))
+		return -1;
+
+	/* We know we have a duplicate
+	 * Stop looking if the old record has been deleted */
+	if (old_tgts == 0)
+		return 0;
+
+	/* look for the first real checksum in the new record */
+	for (new_num_cks = DB_NUM_CKS(new), new_ck = new->cks;
+	     new_num_cks != 0;
+	     --new_num_cks, ++new_ck) {
+		if (DB_CK_TYPE(new_ck) != DCC_CK_FLOD_PATH)
+			break;
+	}
+
+	/* do not even count duplicate server-ID declarations */
+	if (DB_CK_TYPE(new_ck) == DCC_CK_SRVR_ID)
+		return 1;
+
+	/* See if one record is a subset of the other */
+	new_unique = 0;
+	old_unique = 0;
+	old_num_cks = DB_NUM_CKS(db_sts.rcd.d.r);
+	old_ck = db_sts.rcd.d.r->cks;
+	while (old_num_cks != 0 && new_num_cks != 0) {
+		if (DB_CK_TYPE(old_ck) == DCC_CK_FLOD_PATH) {
+			/* skip paths in the old record */
+			++old_ck;
+			--old_num_cks;
+		} else if (DB_CK_TYPE(old_ck) == DB_CK_TYPE(new_ck)) {
+			/* skip identical checksums */
+			++old_ck;
+			--old_num_cks;
+			++new_ck;
+			--new_num_cks;
+		} else if (DB_CK_TYPE(old_ck) < DB_CK_TYPE(new_ck)) {
+			/* skip unique checksum in the old record,
+			 * using the ordering of checksums in records */
+			++old_unique;
+			++old_ck;
+			--old_num_cks;
+		} else {
+			/* skip unique checksum in the new record */
+			++new_unique;
+			++new_ck;
+			--new_num_cks;
+		}
+	}
+
+	/* forget the new record if it has nothing unique */
+	if (new_unique+new_num_cks == 0) {
+		if (CK_FLOD_CNTERR(&ifp->ofp->lc.dup)
+		    && TMSG_FB2(ifp->ofp))
+			flod_cnterr(&ifp->ofp->lc.dup, "%sduplicate %s",
+				    old_unique+old_num_cks == 0
+				    ? "" : "subset ",
+				    rpt_id("report", new, ifp));
+
+		return 1;
+	}
+
+	/* Keep both records if each has unique checksums
+	 * This inflates the count for common checksums, so hope it
+	 * is rare. */
+	if (old_unique+old_num_cks != 0) {
+		if (CK_FLOD_CNTERR(&ifp->ofp->lc.dup)
+		    && TMSG_FB2(ifp->ofp))
+			flod_cnterr(&ifp->ofp->lc.dup, "partial duplicate %s",
+				    rpt_id("report", new, ifp));
+
+		return 0;
+	}
+
+	/* Delete the original report if it has nothing unique
+	 *	This results in doubling the contribution to the total for
+	 *	each checksum from this report in our database until the
+	 *	next time dbclean is run.  At worst this could push a
+	 *	DCC client over its bulk threshold */
+	DB_TGTS_RCD_SET(db_sts.rcd.d.r, 0);
+	SET_FLUSH_RCD_HDR(&db_sts.rcd, 1);
+
+	if (CK_FLOD_CNTERR(&ifp->ofp->lc.dup)
+	    && TMSG_FB2(ifp->ofp))
+		flod_cnterr(&ifp->ofp->lc.dup, "superset duplicate %s",
+			    rpt_id("report", new, ifp));
+	return 0;
+}
+
+
+
+/* see if the new record is a duplicate of any existing records containing
+ * the specified checksum
+ *	use db_sts.rcd for the initial record and the chain */
+static int				/* -1=fail,  0=not dup,  1=duplicate */
+ck_dup_ck_chain(IFLOD_INFO *ifp, const DB_RCD *new, DCC_TGTS new_tgts_raw,
+		DCC_CK_TYPES type, const DB_RCD_CK *found_ck)
+{
+	DB_PTR next_rcd_pos;
+	int cnt, i;
+
+	for (cnt = 0; ; ++cnt) {
+		i = ck_dup_rcd(ifp, new, new_tgts_raw);
+		if (i >= 0) {
+			if (cnt >= 50 && (dccd_tracemask & DCC_TRACE_DB_BIT))
+				dcc_trace_msg("long duplicate chain of %d"
+					      " from %s at %s",
+					      cnt, ifp->rem_hostname,
+					      rpt_id("report",new,ifp));
+			return i;
+		}
+
+		next_rcd_pos = DB_PTR_EX(found_ck->prev);
+		if (next_rcd_pos == DB_PTR_NULL)
+			return 0;
+		if (next_rcd_pos >= db_sts.rcd.s.rptr) {    /* prevent loops */
+			db_error_msg(__LINE__,__FILE__,
+				     "bad %s link of "L_HPAT" at "L_HPAT,
+				     DB_TYPE2STR(type),
+				     db_sts.rcd.s.rptr, next_rcd_pos);
+			return -1;
+		}
+
+		found_ck = db_map_rcd_ck(dcc_emsg, &db_sts.rcd,
+					 next_rcd_pos, type);
+		if (!found_ck) {
+			iflod_close(ifp, 1, 1, 1, "%s", dcc_emsg);
+			DB_ERROR_MSG(dcc_emsg);
+			return -1;
+		}
+	}
+}
+
+
+
+/* complain about a received flooded report,
+ *	and possibly close and so clear things */
+static u_char PATTRIB(6,7)		/* 0=flood closed */
+iflod_rpt_complain(IFLOD_INFO *ifp,
+		   const DB_RCD *new,	/* complain about this report */
+		   u_char serious,	/* 0=send mere note, 1=send complaint */
+		   FLOD_LIMCNT *lc,	/* limit complaints with this */
+		   const char *str,	/* type of report */
+		   const char *pat,...)	/* the complaint */
+{
+	DCC_FLOD_RESP buf;
+	const char *sc;
+	va_list args;
+	int i, len;
+
+	if (!lc && ifp->ofp)
+		lc = &ifp->ofp->lc.iflod_bad;
+	if (!lc) {
+		sc = "";
+	} else {
+		i = ++lc->cur - (lc->lim + FLOD_LIM_COMPLAINTS);
+		if (i > 0)
+			return 1;
+		sc = i < 0 ? "" : "; stop complaints";
+	}
+
+	va_start(args, pat);
+	len = vsnprintf(buf.note.str, sizeof(buf.note.str), pat, args);
+	if (len >= ISZ(buf.note.str))
+		len = sizeof(buf.note.str)-1;
+	va_end(args);
+
+	if (serious) {
+		dcc_error_msg("%s %s%s",
+			      buf.note.str, rpt_id(str, new, ifp), sc);
+		db_ptr2flod_pos(buf.note.op, DCC_FLOD_POS_COMPLAINT);
+	} else {
+		TMSG3_FLOD2(ifp->ofp, "%s %s%s",
+			    buf.note.str, rpt_id(str, new, ifp), sc);
+		db_ptr2flod_pos(buf.note.op, DCC_FLOD_POS_NOTE);
+	}
+
+	len += snprintf(&buf.note.str[len], sizeof(buf.note.str)-len, " %s%s",
+			rpt_id(str, new, 0), sc);
+	if (len >= ISZ(buf.note.str))
+		len = ISZ(buf.note.str)-1;
+
+	buf.note.len = len+1 + FLOD_NOTE_OVHD;
+	return iflod_write(ifp, &buf, buf.note.len, buf.note.str, 0);
+}
+
+
+
+static u_char
+parse_srvr_id(const DB_RCD_CK *ck, const IFLOD_INFO *ifp,
+	      const DB_RCD *new, DCC_SRVR_ID srvr_id)
+{
+	DCC_SRVR_ID tgt_id, type_id;
+	char buf1[24];
+	OPT_FLAGS opt_flags;
+	const OFLOD_INFO *ofp1;
+	ID_TBL *tp;
+
+	/* notice server-ID type announcements */
+	tgt_id = (ck->sum[1] << 8) + ck->sum[2];
+	type_id = srvr_id;
+	switch (type_id) {
+	case DCC_ID_SRVR_REP_OK:
+		type_id = DCC_ID_SRVR_SIMPLE;
+		opt_flags = 0;
+		break;
+	case DCC_ID_SRVR_SIMPLE:
+		opt_flags = FLOD_OPT_SIMPLE;
+		break;
+	case DCC_ID_SRVR_IGNORE:
+		opt_flags = 0;
+		break;
+	case DCC_ID_SRVR_ROGUE:
+		opt_flags = FLOD_OPT_ROGUE;
+		break;
+	default:
+		return 0;
+	}
+	if (ck->sum[0] != DCC_CK_SRVR_ID) {
+		return 0;
+	}
+
+	tp = find_srvr_type(tgt_id);
+	/* Restart flooding if the announced type of a peer changes. */
+	for (ofp1 = oflods.infos; ofp1 <= LAST(oflods.infos); ++ofp1) {
+		/* Wait a bit for more announcements before restarting
+		 * flooding.  When we restart flooding, we will
+		 * check the database. */
+		if (ofp1->rem_id == tgt_id) {
+			if (opt_flags != (ofp1->o_opts.flags
+					  & (FLOD_OPT_ROGUE
+					     | FLOD_OPT_SIMPLE))) {
+				if (TMSG_FB(ifp->ofp) || TMSG_FB(ofp1))
+					dcc_trace_msg("server-ID %d for %s"
+						      " changed to \"%s\""
+						      " in %s",
+						      tgt_id,
+						      ofp1->rem_hostname,
+						      id2str(buf1, sizeof(buf1),
+							  type_id),
+						      rpt_id("report",new,ifp));
+				if (flod_mtime > 1)
+					flod_mtime = 1;
+			}
+			break;
+		}
+	}
+
+	/* accept changes to our records */
+	tp->srvr_type = type_id;
+
+	return 1;
+}
+
+
+
+/* consider an incoming flooded report */
+static int				/* -1=failed, 0=not yet, else length */
+iflod_rpt(IFLOD_INFO *ifp, OFLOD_INFO *ofp,
+	  const DCC_FLOD_STREAM *stream, int max_len)
+{
+	DB_PTR pos;
+	DCC_TGTS new_tgts, found_tgts;
+	DB_RCD new;
+	DCC_SRVR_ID old_srvr, psrvr;
+	const DCC_CK *ck_lim, *ck;
+	const DB_RCD_CK *new_ck_lim, *srvr_id_ck;
+	DB_RCD_CK  *found_ck, *new_ck;
+	DCC_CK_TYPES type, prev_type;
+	DCC_FLOD_PATH_ID *new_path_id, *old_path_id;
+	int num_path_blocks;
+	char tgts_buf[DCC_XHDR_MAX_TGTS_LEN];
+	int ok2;
+	int rpt_len;
+	u_char stale;
+	ID_MAP_RESULT srvr_mapped;
+	ID_TBL *tp;
+	int i;
+
+	pos = flod_pos2db_ptr(stream->r.pos);
+	if (pos < DCC_FLOD_POS_MIN) {
+		iflod_close(ifp, 1, 1, 1,
+			    "bogus position "L_HPAT" in flooded report #%d",
+			    pos, ofp->cnts.total);
+		return -1;
+	}
+
+	/* wait for the header of the report */
+	if (max_len < DCC_FLOD_RPT_LEN(0)) {
+		return 0;
+	}
+
+	if (stream->r.num_cks == 0 || stream->r.num_cks > DCC_QUERY_MAX) {
+		iflod_close(ifp, 1, 1, 1,
+			    "impossible %d checksums in report #%d",
+			    stream->r.num_cks, ofp->cnts.total);
+		return -1;
+	}
+	rpt_len = DCC_FLOD_RPT_LEN(stream->r.num_cks);
+	if (rpt_len > max_len)
+		return 0;		/* wait for more */
+
+	if (db_failed_line)
+		return rpt_len;		/* can do nothing if database broken */
+
+	/* save the position to return to the sender */
+	memcpy(ifp->pos, stream->r.pos, sizeof(ifp->pos));
+
+	new.ts = stream->r.ts;
+	memcpy(&new.srvr_id_auth, stream->r.srvr_id_auth,
+	       sizeof(new.srvr_id_auth));
+	old_srvr = ntohs(new.srvr_id_auth) & ~DCC_SRVR_ID_AUTH;
+	new.srvr_id_auth = old_srvr;
+	new.fgs_num_cks = 0;
+
+	memcpy(&new_tgts, stream->r.tgts, sizeof(new_tgts));
+	new_tgts = ntohl(new_tgts);
+	if (new_tgts == DCC_TGTS_DEL) {
+		if (!(ofp->i_opts.flags & FLOD_OPT_DEL_OK)) {
+			if (!iflod_rpt_complain(ifp, &new, 1,
+						&ofp->lc.not_deleted,
+						"delete request", "refuse"))
+				return -1;
+			return rpt_len;
+		}
+	} else if (new_tgts == 0
+		   || (new_tgts > DCC_TGTS_FLOD_RPT_MAX
+		       && new_tgts != DCC_TGTS_TOO_MANY)) {
+		iflod_close(ifp, 1, 1, 1, "bogus target count %s in %s",
+			    dcc_tgts2str(tgts_buf, sizeof(tgts_buf),
+					 new_tgts, grey_on),
+			    rpt_id("report", &new, 0));
+		return -1;
+	} else if (ofp->i_opts.flags & FLOD_OPT_TRAPS) {
+		/* comply if the source watches only spam traps */
+		new_tgts = DCC_TGTS_TOO_MANY;
+	}
+
+	/* notice reports from the distant future */
+	if (dcc_ts_newer_ts(&new.ts, &future)) {
+		if (!iflod_rpt_complain(ifp, &new, 1, &ofp->lc.stale,
+					"report", "future"))
+			return -1;
+		return rpt_len;
+	}
+
+	DB_TGTS_RCD_SET(&new, new_tgts);
+	new.fgs_num_cks = 0;
+	srvr_id_ck = 0;
+	stale = 1;
+	ck_lim = &stream->r.cks[stream->r.num_cks];
+	new_ck = new.cks;
+	num_path_blocks = 0;
+
+	tp = 0;
+	srvr_mapped = id_map(old_srvr, &ofp->i_opts);
+	switch (srvr_mapped) {
+	case ID_MAP_NO:
+		tp = find_srvr_type(old_srvr);
+		break;
+	case ID_MAP_REJ:
+		if (!iflod_rpt_complain(ifp, &new, 0, 0,
+					"rejected server-ID in", "refuse"))
+			return -1;
+		return rpt_len;
+	case ID_MAP_SELF:
+		new.srvr_id_auth = my_srvr_id;
+		/* create path pointing to ourself if we translate the ID */
+		memset(new_ck, 0, sizeof(*new_ck));
+		new_ck->type_fgs = DCC_CK_FLOD_PATH;
+		new_path_id = (DCC_FLOD_PATH_ID *)new_ck->sum;
+		/* start the path with the ID of the previous hop because
+		 * we know it is defined */
+		new_path_id->hi = ofp->rem_id>>8;
+		new_path_id->lo = ofp->rem_id;
+		new.fgs_num_cks = 1;
+		++new_ck;
+		break;
+	}
+
+	for (prev_type = DCC_CK_INVALID, ck = stream->r.cks;
+	     ck < ck_lim;
+	     prev_type = type, ++ck) {
+		type = ck->type;
+		if (!DCC_CK_OK_FLOD(grey_on, type)) {
+			if (!iflod_rpt_complain(ifp, &new, 1, 0,
+						"report",
+						"unknown checksum type %s in",
+						DB_TYPE2STR(type)))
+				return -1;
+			continue;
+		}
+		if (ck->len != sizeof(*ck)) {
+			iflod_close(ifp, 1, 1, 1,
+				    "unknown checksum length %d in %s",
+				    ck->len, rpt_id("report", &new, 0));
+			return -1;
+		}
+		if (type <= prev_type && prev_type != DCC_CK_FLOD_PATH) {
+			if (!iflod_rpt_complain(ifp, &new, 1, 0,
+						"report",
+						"out of order %s checksum in",
+						DB_TYPE2STR(type)))
+				return -1;
+			return rpt_len;
+		}
+
+		new_ck->type_fgs = type;
+		new_ck->prev = DB_PTR_CP(DB_PTR_NULL);
+		memcpy(new_ck->sum, ck->sum, sizeof(new_ck->sum));
+		if (type == DCC_CK_FLOD_PATH) {
+			/* discard report if path is too long */
+			if (++num_path_blocks > DCC_MAX_FLOD_PATH_CKSUMS) {
+				TMSG2_FLOD(ofp, "%d path blocks in %s",
+					   num_path_blocks,
+					   rpt_id("report", &new, ifp));
+				return rpt_len;
+			}
+			/* don't add this path if we translated the origin */
+			if (srvr_mapped == ID_MAP_SELF)
+				continue;
+			old_path_id = (DCC_FLOD_PATH_ID *)ck->sum;
+			new_path_id = old_path_id;
+			for (i = 0; i < DCC_NUM_FLOD_PATH; ++i, ++old_path_id) {
+				psrvr = (old_path_id->hi<<8) | old_path_id->lo;
+				if (psrvr == DCC_ID_INVALID)
+					break;	/* end of path */
+				switch (id_map(psrvr, &ofp->i_opts)) {
+				case ID_MAP_NO:
+				case ID_MAP_REJ:
+					break;
+				case ID_MAP_SELF:
+					psrvr = my_srvr_id;
+					break;
+				}
+				new_path_id->hi = psrvr>>8;
+				new_path_id->lo = psrvr;
+				++new_path_id;
+			}
+
+		} else {
+			/* discard this checksum if we would not have kept
+			 * it if we had received the original report
+			 * and either its server-ID is translated
+			 * or it is not kept by default */
+			if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type)
+			    && (srvr_mapped == ID_MAP_SELF
+				|| DB_GLOBAL_NOKEEP(grey_on, type)))
+				continue;
+
+			/* server-ID declarations are never stale */
+			if (type == DCC_CK_SRVR_ID) {
+				stale = 0;
+				srvr_id_ck = new_ck;
+			}
+
+			/* Notice if this checksum makes the report timely
+			 * We cannot detect duplicates of reports that
+			 * have expired, so consider stale anything older
+			 * than our expiration.
+			 * Ignore reports of checksums from crazy servers */
+			if (stale
+			    && dcc_ts_newer_ts(&new.ts,
+					       new_tgts >= db_tholds[type]
+					       ? &db_parms.ex_spam[type]
+					       : &db_parms.ex_all[type])
+			    && (tp == 0
+				|| (tp->srvr_type != DCC_ID_SRVR_IGNORE
+				    && tp->srvr_type != DCC_ID_SRVR_ROGUE)))
+				stale = 0;
+		}
+
+		++new_ck;
+		++new.fgs_num_cks;
+	}
+	if (stale) {
+		if (CK_FLOD_CNTERR(&ofp->lc.stale)
+		    && TMSG_FB2(ofp))
+			flod_cnterr(&ofp->lc.stale, "stale %s",
+				    rpt_id("report", &new, ifp));
+		return rpt_len;
+	}
+
+	if (!DB_NUM_CKS(&new)) {
+		iflod_close(ifp, 1, 1, 1, "no known checksum types in %s",
+			    rpt_id("report", &new, 0));
+		return -1;
+	}
+
+	/* only now might we look at the database */
+	if (db_lock() < 0) {
+		iflod_close(ifp, 1, 1, 1, "iflod lock failure");
+		return -1;
+	}
+
+	/* See if the report is a duplicate.
+	 * Check all of the checksums to find one that is absent or
+	 * the one with the smallest total to minimize the number
+	 * of reports we must check to see if this is a duplicate */
+	ok2 = 0;
+	new_ck_lim = &new.cks[DB_NUM_CKS(&new)];
+	for (new_ck = new.cks; new_ck < new_ck_lim; ++new_ck) {
+		type = DB_CK_TYPE(new_ck);
+		if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+			continue;
+
+		switch (db_lookup(dcc_emsg, type, new_ck->sum,
+				  0, MAX_HASH_ENTRIES,
+				  &db_sts.hash, &db_sts.rcd, &found_ck)) {
+		case DB_FOUND_LATER:
+		case DB_FOUND_SYSERR:
+			iflod_close(ifp, 1, 1, 1, "%s", dcc_emsg);
+			DB_ERROR_MSG(dcc_emsg);
+			return -1;
+
+		case DB_FOUND_IT:
+			/* At least this checksum is already in the database */
+			i = ck_dup_ck_chain(ifp, &new, new_tgts,
+					    type, found_ck);
+			if (i < 0)
+				return -1;  /* broken database */
+			if (i > 0)
+				return rpt_len;	/* duplicate */
+
+			/* Maybe not a duplicate.
+			 * Notice reports of checksums on the local server's
+			 * whitelist.
+			 * An ordinary checksum is whitelisted by DCC_TGTS_OK
+			 * or two reports with DCC_TGTS_OK2.
+			 * Greylisting uses DCC_TGTS_GREY_WHITE=DCC_TGTS_OK2
+			 * and so one report of DCC_TGTS_GREY_WHITE is enough */
+			found_tgts = DB_TGTS_CK(found_ck);
+			if (found_tgts == DCC_TGTS_OK
+			    || (found_tgts == DCC_TGTS_GREY_WHITE
+				&& (++ok2 >= 2 || grey_on))) {
+				if (!iflod_rpt_complain(ifp, &new, 0,
+							&ofp->lc.wlist,
+							"report","whitelisted"))
+					return -1;
+				return rpt_len;
+			}
+			break;
+
+		case DB_FOUND_EMPTY:
+		case DB_FOUND_CHAIN:
+		case DB_FOUND_INTRUDER:
+			/* We will fail to find this checksum in our database
+			 * if the new report is not a duplicate
+			 * or if it is a duplicate superset report */
+			break;
+		}
+	}
+
+	/* If the new report is a delete request,
+	 * then we need to run dbclean to fix all of the
+	 * totals affected by the deleted reports. */
+	if (new_tgts == DCC_TGTS_DEL) {
+		if (!(ofp->i_opts.flags & FLOD_OPT_NO_LOG_DEL))
+			dcc_trace_msg("accept %s",
+				      rpt_id("delete request", &new, ifp));
+		if (!DCC_CK_IS_REP_OP(grey_on, type) && !grey_on)
+			need_del_dbclean = "flood checksum deletion";
+	}
+
+	if (srvr_id_ck) {
+		/* discard translated server-ID declarations */
+		if (srvr_mapped == ID_MAP_SELF) {
+			TMSG2_FLOD(ofp, "translated server-ID from %d in %s",
+				   old_srvr, rpt_id("report", &new, ifp));
+			return rpt_len;
+		}
+
+		/* notice claims by other servers to our ID */
+		if (old_srvr == my_srvr_id) {
+			if (memcmp(host_id_sum, srvr_id_ck->sum,
+				   sizeof(host_id_sum)))
+				dcc_error_msg("host %s used our server-ID"
+					      " %d at %s",
+					      dcc_ck2str_err(DCC_CK_SRVR_ID,
+							srvr_id_ck->sum, 0),
+					      my_srvr_id,
+					      ts2str_err(&new.ts));
+			return rpt_len;
+		}
+
+		if (old_srvr < DCC_SRVR_ID_MIN
+		    && !parse_srvr_id(srvr_id_ck, ifp, &new, old_srvr))
+			return rpt_len;
+	}
+
+	/* the report is ok and not a duplicate, so add it to our database */
+	if (!add_dly_rcd(&new, 1)) {
+		iflod_close(ifp, 1, 1, 1, "%s", dcc_emsg);
+		return -1;
+	}
+
+	++ofp->cnts.accepted;
+	return rpt_len;
+}
+
+
+
+static void
+bad_vers(IFLOD_INFO *ifp,
+	 u_char fail)			/* 1=complain */
+{
+	iflod_close(ifp, fail, fail, 1,
+		    DCC_FLOD_BAD_VER_MSG" need \""
+		    DCC_FLOD_VERSION_CUR_STR
+		    "\" not \"%.*s\"",
+		    LITZ(DCC_FLOD_VERSION_STR_BASE)+10,
+		    ifp->ibuf.s.v.body.str);
+}
+
+
+
+/* authenticate and otherwise check a new incoming flood */
+static u_char				/* 0=closed or switched to output */
+check_iflod_vers(IFLOD_INFO *ifp)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	const DCC_FLOD_VERSION_HDR *vp;
+	OFLOD_INFO *ofp;
+	IFLOD_INFO *ifp1;
+	const ID_TBL *tp;
+	DCC_SRVR_ID rem_id;
+	int iversion;
+	int i;
+
+	vp = &ifp->ibuf.s.v;
+	if (!strcmp(vp->body.str, DCC_FLOD_VERSION_CUR_STR)) {
+		iversion = DCC_FLOD_VERSION_CUR;
+		ifp->flags |= IFLOD_FG_VERS_CK;
+
+#ifdef DCC_FLOD_VERSION7
+	} else if (!strcmp(vp->body.str, DCC_FLOD_VERSION7_STR)) {
+		iversion = DCC_FLOD_VERSION7;
+		ifp->flags |= IFLOD_FG_VERS_CK;
+
+#endif /* DCC_FLOD_VERSION7 */
+
+	} else if (!strncmp(vp->body.str, DCC_FLOD_VERSION_STR_BASE,
+			    LITZ(DCC_FLOD_VERSION_STR_BASE))) {
+		/* it seems to be a DCC server,
+		 * so complain after identifying the peer */
+		iversion = 1;
+
+	} else {
+		/* junk, so complain and give up */
+		bad_vers(ifp, 1);
+		return 0;
+	}
+
+	/* require a sane and familiar server-ID from the prospective peer */
+	memcpy(&rem_id, vp->body.sender_srvr_id, sizeof(rem_id));
+	rem_id = ntohs(rem_id);
+	if (rem_id < DCC_SRVR_ID_MIN
+	    || rem_id > DCC_SRVR_ID_MAX) {
+		iflod_close(ifp, 1, 1, 1, DCC_FLOD_BAD_ID_MSG" %d",
+			    rem_id);
+		return 0;
+	}
+	for (ofp = oflods.infos; ; ++ofp) {
+		if (ofp > LAST(oflods.infos)) {
+			iflod_close(ifp, 1, 1, 1, DCC_FLOD_BAD_ID_MSG" %d",
+				    rem_id);
+			return 0;
+		}
+		if (ofp->rem_id == rem_id) {
+			ifp->ofp = ofp;
+			STRLCPY(ifp->rem_hostname, ofp->rem_hostname,
+				sizeof(ifp->rem_hostname));
+			break;
+		}
+	}
+
+	/* ofp and ofp->mp are not null, because we now know which peer
+	 * it claims to be
+	 *
+	 * check that it knows the password */
+	i = ck_sign(&tp, 0, ofp->in_passwd_id, vp, sizeof(*vp));
+	if (!i) {
+		if (!tp)
+			iflod_close(ifp, 1, 1, 1, DCC_FLOD_PASSWD_ID_MSG" %d%s",
+				    ofp->in_passwd_id,
+				    fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		else
+			iflod_close(ifp, 1, 1, 1, DCC_FLOD_BAD_AUTH_MSG" %d",
+				    ofp->in_passwd_id);
+		return 0;
+	}
+	if (i == 1)
+		ofp->mp->flags &= ~FLODMAP_FG_USE_2PASSWD;
+	else
+		ofp->mp->flags |= FLODMAP_FG_USE_2PASSWD;
+
+	/* no more assumed NAT games because it has contacted us */
+	ofp->mp->flags &= ~FLODMAP_FG_NAT_AUTO;
+
+	/* Note the version of the protocol it is using so that we can use that
+	 * version when connecting to it. */
+	ofp->mp->iversion = iversion;
+	/* if we do not like its version, reject the connection and hope
+	 * that it will retry  with a version we like */
+	if (!(ifp->flags & IFLOD_FG_VERS_CK)) {
+		bad_vers(ifp, iversion != 0);
+		return 0;
+	}
+	if (iversion != DCC_FLOD_VERSION_CUR)
+		TMSG2_FLOD(ofp, "version %d from %s",
+			   iversion, ifp_rem_str(ifp));
+
+	/* convert to a passive output flood as requested by the peer
+	 *	This works even if the peer is configured to use SOCKS or NAT
+	 *	but we are not using PASSIVE */
+	if (vp->body.turn) {
+		if (OFLOD_OPT_OFF_ROGUE(ofp)) {
+			iflod_close(ifp, 1, 0, 1,
+				    "passive output flooding off from %s%s",
+				    ifp_rem_str(ifp),
+				    fnm_lno(&fnm_buf, flod_path, ofp->lno));
+			return 0;
+		}
+
+		/* We have a duplicate passive outgoing flood.
+		 * See whether the old stream has broken. */
+		if (ofp->soc >= 0)
+			oflod_read(ofp);
+		/* If we still have a duplicate and we sent a shutdown request,
+		 * assume the response got lost */
+		if (ofp->soc >= 0
+		    && (ofp->flags & OFLOD_FG_SHUTDOWN_REQ)) {
+			rpt_err(ofp, 1, 0,
+				" assume response to shutdown lost from %s",
+				ofp_rem_str(ofp));
+			oflod_close(ofp, 0);
+		}
+		if (ofp->soc >= 0) {
+			/* We still have duplicates.
+			 * Reject the new one if the IP addresses differ */
+			if (!DCC_SU_EQ(&ofp->rem_su, &ifp->rem_su)) {
+				iflod_close(ifp, 1, 0, 1,
+					    "reject duplicate passive output"
+					    " flood from %s",
+					    ifp_rem_str(ifp));
+				return 0;
+			}
+			rpt_err(ofp, 1, 0,
+				"accept duplicate passive output flood from %s",
+				ifp_rem_str(ifp));
+			oflod_close(ofp, 0);
+		}
+
+		ofp->soc = ifp->soc;
+		ofp->rem_su = ifp->rem_su;
+		++oflods.open;
+		TMSG1_FLOD(ofp,
+			   "convert incoming flood to passive outgoing to %s",
+			   ofp_rem_str(ofp));
+		iflod_clear(ifp, 0);
+
+		ofp->mp->flags |= FLODMAP_FG_OUT_SRVR;
+
+		if (!oflod_connect_fin(ofp))
+			oflod_close(ofp, 0);
+		return 0;
+	}
+
+	if (IFLOD_OPT_OFF_ROGUE(ofp)) {
+		iflod_close(ifp, 1, 0, 1, "flood from %s turned off%s",
+			    ifp_rem_str(ifp),
+			    fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		return 0;
+	}
+
+	/* detect duplicate incoming floods */
+	for (ifp1 = iflods.infos; ifp1 <= LAST(iflods.infos); ++ifp1) {
+		if (ifp1->ofp != ofp || ifp1 == ifp)
+			continue;
+
+		/* We have a duplicate.  Either two servers are using the
+		 * same server-ID or the peer has restarted flooding without
+		 * our seeing  a clean shutdown.
+		 * If socket is in CLOSE_WAIT, then sending something will fail
+		 * immediately.  If the peer was rebooted, then sending will
+		 * not fail for at least a round trip time and possibly longer
+		 * if the peer has moved.
+		 *
+		 * Before trying to send anything, check for a FIN waiting
+		 * on the other socket */
+		for (i = 65536/FLOD_BUF_SIZE; i >= 0; --i) {
+			if (iflod_read(ifp1))
+				break;
+		}
+		/* forget it if reading closed the other stream */
+		if (ifp1->ofp != ofp)
+			break;
+
+		/* assume the it is ok if we have sent an end request */
+		if (ifp1->flags & IFLOD_FG_END_REQ) {
+			iflod_close(ifp1, 0, 0, 1,
+				    "missing end response;"
+				    " have replacement for flood from %s",
+				    ifp_rem_str(ifp1));
+			break;
+		}
+
+		/* assume we missed the shutdown
+		 * if the IP addresses are the same */
+		if (DCC_SU_EQ(&ifp->rem_su, &ifp1->rem_su)) {
+			iflod_close(ifp1, 0, 0, 1,
+				    "assumed dead link;"
+				    " have replacement for flood from %s",
+				    ifp_rem_str(ifp1));
+			break;
+		}
+
+		/* assume we do not have a duplicate server-ID and switch to
+		 * the new connection if sending a position or note
+		 * fails immediately, */
+		if (0 >= iflod_send_pos(ifp1, 1)) {
+			iflod_close(ifp1, 0, 0, 1,
+				    "have replacement for flood from %s",
+				    ifp_rem_str(ifp1));
+			break;
+		}
+
+		/* Otherwise, kill the new flood.  If it was legitimate,
+		 * sending the note will eventually kill the old stream
+		 * and the peer will get through with it tries later */
+		iflod_close(ifp, 1, 1, 1, "duplicate flood from %s",
+			    ifp_rem_str(ifp));
+		return 0;
+	}
+
+	ofp->ifp = ifp;
+	if (ifp->flags & IFLOD_FG_CLIENT)
+		ofp->mp->flags &= ~FLODMAP_FG_IN_SRVR;
+	else
+		ofp->mp->flags |= FLODMAP_FG_IN_SRVR;
+	save_flod_cnts(ofp);
+	ifp->iflod_alive = db_time.tv_sec;
+
+	/* Try to restart the corresponding output flood because a new
+	 * incoming flood might indicate that peer has awakened.
+	 * Kludge the backoff so that it does not increase. */
+	if (ofp->soc < 0
+	    && !(ofp->o_opts.flags & FLOD_OPT_PASSIVE)) {
+		ofp->mp->otimers.retry_secs /= 2;
+		ofp->mp->otimers.retry = 0;
+		oflod_open(ofp);
+	}
+
+	/* Send a rewind or fast forward request immediately if needed.
+	 * If not, send a keepalive message so that peer knows we have
+	 * accepted the connection and it can stop worrying about an
+	 * immediate rejection. */
+	if (0 > iflod_send_pos(ifp, 1))
+		return 0;
+
+	return 1;
+}
+
+
+
+/* A new SOCKS incoming stream that we originated has been closed by the
+ * peer without authenticating itself.  It could have responded to our
+ * authentication with an error message. */
+static void
+parse_socks_error(IFLOD_INFO *ifp)
+{
+	const DCC_FLOD_STREAM *stream;
+	int i, msg_len;
+	int fail;
+
+	stream = (DCC_FLOD_STREAM *)&ifp->ibuf.b[0];
+	msg_len = ifp->ibuf_len - FLOD_END_OVHD;
+
+	/* it must look like an end request with an entirely ASCII message */
+	if (flod_pos2db_ptr(stream->r.pos) != DCC_FLOD_POS_END
+	    || msg_len < 1 || msg_len > ISZ(stream->e.msg)) {
+		iflod_close(ifp, 1, 1, 1, "SOCKS rejected with \"%.*s\"",
+			    msg_len, stream->e.msg);
+		return;
+	}
+
+	for (i = 0; i < msg_len; ++i) {
+		if (stream->e.msg[i] < ' ' || stream->e.msg[i] > '~') {
+			iflod_close(ifp, 1, 1, 1,
+				    "SOCKS rejected with \"%.*s\"",
+				    msg_len, stream->e.msg);
+			return;
+		}
+	}
+
+	fail = oflod_parse_eof(ifp->ofp, 1, &stream->e, msg_len);
+	if (fail <= 0) {
+		iflod_socks_backoff(ifp->ofp);
+	} else {
+		/* try again immediately
+		 * with another protocol version or the 2nd password */
+		ifp->ofp->mp->itimers.retry = 0;
+	}
+	iflod_close(ifp, fail<=0, fail<=0, 0,
+		    "SOCKS rejected by %s with \"%.*s\"",
+		    ifp_rem_str(ifp), msg_len, stream->e.msg);
+}
+
+
+
+/* see what a distant flooder is telling us
+ *	can close the flood and so clear things */
+u_char					/* 1=kernel buffers empty */
+iflod_read(IFLOD_INFO *ifp)
+{
+	OFLOD_INFO *ofp;
+	int off, req_len, recv_len;
+	const DCC_FLOD_STREAM *stream;
+	int len, i;
+
+	/* if this is an incoming SOCKS or NAT stream that we originated,
+	 * and if Rconnect() said "not yet" when we first tried to connect,
+	 * then we must be here because select() says it is time to
+	 * try Rconnect() again to finish the connection */
+	if (!(ifp->flags & IFLOD_FG_CONNECTED)
+	    && iflod_socks_connect(ifp) <= 0)
+		return 1;
+
+	/* read only once before returning
+	 * to ensure we pay attention to other work */
+
+	req_len = sizeof(ifp->ibuf) - ifp->ibuf_len;
+	ofp = ifp->ofp;
+	if (ofp && (ofp->o_opts.flags & FLOD_OPT_SOCKS))
+		recv_len = Rrecv(ifp->soc, &ifp->ibuf.b[ifp->ibuf_len],
+				 req_len, 0);
+	else
+		recv_len = recv(ifp->soc, &ifp->ibuf.b[ifp->ibuf_len],
+				req_len, 0);
+	if (recv_len < 0) {
+		/* If kernel ran out of data, stop for now.
+		 * Give up on an I/O error */
+		if (!DCC_BLOCK_ERROR()) {
+			iflod_close(ifp, 1, 0, 0, "incoming flood recv(%s): %s",
+				    ifp_rem_str(ifp), ERROR_STR());
+		}
+		return 1;
+	}
+	ifp->ibuf_len += recv_len;
+
+	off = 0;
+
+	/* deal with a new connection */
+	if (!(ifp->flags & IFLOD_FG_VERS_CK)) {
+		if (ifp->ibuf_len >= ISZ(DCC_FLOD_VERSION_HDR)) {
+			if (!check_iflod_vers(ifp))
+				return 1;   /* stream closed or converted */
+			ofp = ifp->ofp;
+			off = ISZ(DCC_FLOD_VERSION_HDR);
+
+		} else if (recv_len != 0) {
+			return 1;	/* wait for rest of authentication */
+
+		} else if (ofp && ofp->mp
+			   && (ofp->mp->flags & FLODMAP_FG_ACT) != 0) {
+			parse_socks_error(ifp);
+			return 1;
+
+		} else {
+			iflod_close(ifp, 1, 1, 0, "garbage connection from %s",
+				    ifp_rem_str(ifp));
+			return 1;
+		}
+	}
+	/* ofp != 0 because check_iflod_vers() has found the peer */
+
+	/* deal with the data */
+	dcc_timeval2ts(&future, &db_time, MAX_FLOD_CLOCK_SKEW);
+	while ((len = ifp->ibuf_len - off) > 0) {
+		stream = (DCC_FLOD_STREAM *)&ifp->ibuf.b[off];
+		if (len < ISZ(stream->r.pos))
+			break;		/* need at least the position */
+		i = iflod_rpt(ifp, ofp, stream, len);
+		if (i < 0)
+			return 1;	/* stream closed */
+		if (i == 0)
+			break;		/* wait for rest of report */
+		off += i;
+		++ofp->cnts.total;
+	}
+
+	/* save unprocessed bytes for next time */
+	if (off != 0) {
+		ifp->ibuf_len -= off;
+		if (ifp->ibuf_len < 0)
+			dcc_logbad(EX_SOFTWARE, "ifp->ibuf_len=%d",
+				   ifp->ibuf_len);
+		if (ifp->ibuf_len > 0)
+			memmove(&ifp->ibuf.b[0], &ifp->ibuf.b[off],
+				ifp->ibuf_len);
+	}
+
+	if (recv_len == 0) {
+		/* We are at EOF and have processed all input that we can. */
+		if (ifp->ibuf_len != 0) {
+			/* Something is wrong if any input remains,  */
+			iflod_close(ifp, 1, 0, 1, "report %d truncated",
+				    ofp ? ofp->cnts.total : 0);
+		} else if (flods_st != FLODS_ST_ON
+			   || (ofp && IFLOD_OPT_OFF_ROGUE(ofp))) {
+			iflod_close(ifp, 0, 0, 1, DCC_FLOD_OK_STR"%s off",
+				    our_hostname);
+		} else {
+			iflod_close(ifp, 0, 0, 1, DCC_FLOD_OK_STR"%s off",
+				    ifp_rem_str(ifp));
+		}
+		return 1;
+	}
+
+	/* things are going ok, so reset the SOCKS restart backoff
+	 * and the no-connection complaint */
+	ofp->mp->itimers.retry_secs = FLOD_SOCKS_SOCKS_IRETRY;
+	ofp->mp->itimers.msg_secs = FLOD_IN_COMPLAIN1;
+	ofp->mp->itimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN1;
+
+	return (req_len > recv_len);
+}
+
+
+
+void
+iflods_listen(void)
+{
+	SRVR_SOC *sp;
+	DCC_SOCKU su;
+	const DCC_SOCKU *sup = 0;
+	int i, on;
+
+	for (sp = srvr_socs; sp; sp = sp->fwd) {
+		if (sp->flags & SRVR_SOC_ADDR) {
+			/* need to open a TCP listen socket for incoming floods
+			 * for each explicitly configured IP address */
+			sup = &sp->su;
+		} else if (sp->flags & SRVR_SOC_LISTEN) {
+			/* need to open one TCP INADDR_ANY listen socket for
+			 * the first implicitly configured interface */
+			sup = dcc_mk_su(&su, sp->su.sa.sa_family, 0,
+					sp->su.ipv6.sin6_port);
+		} else {
+			/* otherwise close unneeded socket */
+			iflod_listen_close(sp);
+			continue;
+		}
+
+		if (sp->listen >= 0)
+			continue;
+
+		/* don't need to listen if there is no flooding */
+		if (!oflods.total)
+			continue;
+
+		TMSG1(FLOD, "start flood listening on %s",
+		      dcc_su2str_err(sup));
+
+		sp->listen = socket(sup->sa.sa_family, SOCK_STREAM, 0);
+		if (sp->listen < 0) {
+			dcc_error_msg("socket(flood listen %s): %s",
+				      dcc_su2str_err(sup), ERROR_STR());
+			continue;
+		}
+
+		if (-1 == fcntl(sp->listen, F_SETFL,
+				fcntl(sp->listen, F_GETFL, 0) | O_NONBLOCK)) {
+			dcc_error_msg("fcntl(flood listen %s, O_NONBLOCK): %s",
+				      dcc_su2str_err(sup), ERROR_STR());
+		}
+		on = 1;
+		if (0 > setsockopt(sp->listen, SOL_SOCKET, SO_REUSEADDR,
+				   &on, sizeof(on)))
+			dcc_error_msg("setsockopt(flood listen %s,"
+				      " SO_REUSADDR): %s",
+				      dcc_su2str_err(sup), ERROR_STR());
+		if (0 > fcntl(sp->listen, F_SETFD, FD_CLOEXEC))
+			dcc_error_msg("fcntl(flood listen %s FD_CLOEXEC): %s",
+				      dcc_su2str_err(sup), ERROR_STR());
+
+		i = bind(sp->listen, &sup->sa, DCC_SU_LEN(sup));
+		if (0 > i) {
+			dcc_error_msg("bind(flood listen %s): %s",
+				      dcc_su2str_err(sup), ERROR_STR());
+			close(sp->listen);
+			sp->listen = -1;
+			continue;
+		}
+
+		if (0 > listen(sp->listen, DCCD_MAX_FLOODS+1)) {
+			dcc_error_msg("flood listen(%s): %s",
+				      dcc_su2str_err(sup), ERROR_STR());
+			close(sp->listen);
+			sp->listen = -1;
+		}
+	}
+}
+
+
+
+static const char *
+oflod_state_str(char outstr[DCC_SU2STR_SIZE], const OFLOD_INFO *ofp,
+		u_char anon)
+{
+	if (ofp->soc >= 0) {
+		if (ofp->flags & (OFLOD_FG_SHUTDOWN_REQ
+				  | OFLOD_FG_SHUTDOWN))
+			return "  (shutting)";
+		if (!(ofp->flags & OFLOD_FG_CONNECTED))
+			return "  (connecting)";
+		if (anon)
+			return "";
+		return dcc_su2str2(outstr, DCC_SU2STR_SIZE, &ofp->rem_su);
+	}
+
+	if (OFLOD_OPT_OFF_ROGUE(ofp))
+		return "  (output off)";
+	if (flods_st != FLODS_ST_ON)
+		return "  (flood off)";
+	return "  (no output)";
+}
+
+
+
+static const char *
+iflod_state_str(char instr[DCC_SU2STR_SIZE],
+		const OFLOD_INFO *ofp, const IFLOD_INFO *ifp,
+		u_char anon, u_char have_in, u_char distinct_in)
+{
+
+	if (have_in) {
+		if (!(ifp->flags & IFLOD_FG_VERS_CK))
+			return "  (connecting)";
+		if (anon)
+			return "";
+		if (distinct_in)
+			return dcc_su2str2(instr, DCC_SU2STR_SIZE, &ifp->rem_su);
+		return "\t";
+	}
+	if (IFLOD_OPT_OFF_ROGUE(ofp))
+	    return "  (input off)";
+	if (flods_st != FLODS_ST_ON)
+		return "  (flood off)";
+	return "  (no input)";
+}
+
+
+
+/* list the current flooders */
+int
+flods_list(char *buf, int buf_len, u_char anon)
+{
+#define FLODS_LIST_TOO_SHORT "buffer too short\n"
+#define FLODS_LIST_ALLOC(i) {					\
+	p += (i);						\
+	if ((buf_len -= (i)) <= 0) {				\
+		strcpy(p, FLODS_LIST_TOO_SHORT);		\
+		return (p-buf)+ISZ(FLODS_LIST_TOO_SHORT);	\
+	}}
+	IFLOD_INFO *ifp;
+	OFLOD_INFO *ofp;
+	char instr[DCC_SU2STR_SIZE], outstr[DCC_SU2STR_SIZE];
+	char hostname[60], fg_buf[60];
+	DCC_SOCKU in, out;
+	u_char have_in, distinct_in;
+	int i;
+	char *p;
+
+	if (buf_len < ISZ(FLODS_LIST_TOO_SHORT) +INET6_ADDRSTRLEN+1)
+		return 0;
+
+	buf_len -= ISZ(FLODS_LIST_TOO_SHORT);
+	p = buf;
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		if (ofp->rem_hostname[0] == '\0')
+			break;
+		have_in = 0;
+		distinct_in = 0;
+		for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
+			if (ifp->ofp == ofp) {
+				if (ifp->soc >= 0) {
+					have_in = 1;
+					dcc_ipv6sutoipv4(&in, &ifp->rem_su);
+					dcc_ipv6sutoipv4(&out, &ofp->rem_su);
+					if (ofp->soc < 0
+					    || !DCC_SU_EQ(&in, &out))
+					    distinct_in = 1;
+				}
+				break;
+			}
+		}
+		if (anon) {
+			i = snprintf(p, buf_len, "%5d %15s\t%s\n",
+				     ofp->rem_id,
+				     oflod_state_str(outstr, ofp, 1),
+				     iflod_state_str(instr, ofp, ifp,
+						     1, have_in, 0));
+		} else {
+			dcc_host_portname(hostname, sizeof(hostname),
+					  ofp->rem_hostname,
+					  ofp->rem_port == def_port
+					  ? 0 : ofp->rem_portname),
+			i = strlen(hostname);
+			flodmap_fg(fg_buf, sizeof(fg_buf),
+				   i < 16 ? "\t\t" : i > 24 ? "  " : "\t",
+				   ofp->mp);
+			i = snprintf(p, buf_len, "%5d %15s\t%s\t%s%s\n",
+				     ofp->rem_id,
+				     oflod_state_str(outstr, ofp, 0),
+				     iflod_state_str(instr, ofp, ifp,
+						     0, have_in, distinct_in),
+				     hostname,
+				     fg_buf);
+		}
+		FLODS_LIST_ALLOC(i);
+	}
+
+	for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
+		if (ifp->soc < 0 || ifp->ofp != 0)
+			continue;	/* already handled this one */
+
+		/* say something about an incomplete connection */
+		i = snprintf(p, buf_len, " ?    %s\n", ifp->rem_hostname);
+		FLODS_LIST_ALLOC(i);
+	}
+	if (p > buf)
+		--p;			/* trim trailing '\n' */
+	return p-buf;
+#undef FLODS_LIST_TOO_SHORT
+#undef FLODS_LIST_ALLOC
+}
+
+
+
+static PATTRIB(3,4) u_char		/* 0=no room */
+flod_stats_str(char **buf, int *buf_len,
+		const char *pat, ...)
+{
+	int i;
+	va_list args;
+
+	if (*buf_len <= 0)
+		return 0;
+
+	va_start(args, pat);
+	i = vsnprintf(*buf, *buf_len, pat, args);
+	va_end(args);
+
+	if ((*buf_len -= i) <= 0) {
+		*buf_len = 0;
+		return 0;
+	}
+	*buf += i;
+	return 1;
+}
+
+
+
+
+static u_char				/* 0=no room */
+flod_stats_time(char **buf, int *buf_len,
+		const char *str, const char *timepat, time_t when)
+{
+	char timebuf[40];
+
+	return flod_stats_str(buf, buf_len, "%s %s", str,
+			      dcc_time2str(timebuf, sizeof(timebuf), timepat,
+					   when));
+}
+
+
+
+static void
+flod_stats_conn_total(char **buf, int *buf_len,
+		      const char *label, int connected)
+{
+	int i;
+
+	if (*buf_len <= 0)
+		return;
+
+	i = snprintf(*buf, *buf_len,
+		     "\n  %s connected a total of %d days %d:%02d:%02d\n",
+		     label,
+		     connected/(24*60*60),
+		     (connected/(60*60)) % 24,
+		     (connected/60) % 60,
+		     connected % 60);
+	*buf += i;
+	*buf_len -= i;
+}
+
+
+
+static void
+flod_stats_conn_cur(char **buf, int *buf_len, const OFLOD_INFO *ofp, u_char in)
+{
+	u_char connected;
+	time_t conn_changed;
+	time_t flod_alive;
+	const FLOD_MMAP *mp;
+	const LAST_ERROR *ep;
+	DCC_FNM_LNO_BUF fnm_buf;
+	time_t deadline;
+	u_char passive;
+	const char *msg;
+
+	if (*buf_len <= 0)
+		return;
+
+	mp = ofp->mp;
+
+	if (in) {
+		connected = ofp->ifp != 0;
+		conn_changed = ofp->mp->cnts.in_conn_changed;
+		flod_alive = ofp->ifp ? ofp->ifp->iflod_alive : 0;
+	} else {
+		connected = (ofp->flags & OFLOD_FG_CONNECTED) != 0;
+		conn_changed = ofp->mp->cnts.out_conn_changed;
+		flod_alive = ofp->oflod_alive;
+	}
+
+	if (connected) {
+		if (conn_changed >= mp->cnts.cnts_cleared
+		    && !flod_stats_time(buf, buf_len, "     connected since",
+					"%b %d %X", conn_changed))
+			return;
+		flod_stats_time(buf, buf_len, "     last active", "%X",
+				flod_alive);
+		return;
+	}
+
+	if ((in && (mp->flags & FLODMAP_FG_IN_OFF))
+	    || (!in && (mp->flags & FLODMAP_FG_OUT_OFF))) {
+		flod_stats_str(buf, buf_len, "     off%s",
+			       fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		return;
+	}
+
+	if (!flod_stats_str(buf, buf_len, "     not connected"))
+		return;
+	if (conn_changed >= mp->cnts.cnts_cleared) {
+		if (!flod_stats_time(buf, buf_len, " since",
+				     "%b %d %X", conn_changed))
+			return;
+	}
+	ep = in ? &mp->iflod_err : &mp->oflod_err;
+	msg = ep->msg[0] != '\0' ? ep->msg : ep->trace_msg;
+	if (msg[0] != '\0') {
+		if (!flod_stats_str(buf, buf_len, "\n\t%s", msg))
+			return;
+	}
+
+	if (!FLODS_OK_ON()) {
+		flod_stats_str(buf, buf_len, "\n     flooding off");
+		return;
+	}
+
+	if (in) {
+		if ((ofp->mp->flags & FLODMAP_FG_ACT) != 0) {
+			passive = 0;
+			deadline = ofp->mp->itimers.retry;
+			if (DB_IS_TIME(deadline, ofp->mp->itimers.retry_secs))
+				deadline = 0;
+
+		} else {
+			passive = 1;
+			deadline = ofp->mp->itimers.msg;
+			if (DB_IS_TIME(deadline, ofp->mp->itimers.msg_secs))
+				deadline = 0;
+		}
+	} else {
+		if (ofp->mp->flags & FLODMAP_FG_PASSIVE) {
+			passive = 1;
+			deadline = ofp->mp->otimers.msg;
+			if (DB_IS_TIME(deadline, ofp->mp->otimers.msg_secs))
+				deadline = 0;
+		} else {
+			passive = 0;
+			deadline = ofp->mp->otimers.retry;
+			if (DB_IS_TIME(deadline, ofp->mp->otimers.retry_secs))
+				deadline = 0;
+		}
+	}
+	if (deadline == 0) {
+		flod_stats_str(buf, buf_len,
+			       passive
+			       ? "\n     complain soon"
+			       : "\n     try again soon");
+	} else {
+		flod_stats_time(buf, buf_len,
+				passive
+				? "\n     complain after"
+				: "\n     try again after",
+				"%b %d %X", deadline);
+	}
+}
+
+
+
+/* list the counts for a flood */
+int					/* -1 or buffer length */
+flod_stats(char *buf, int buf_len, u_int32_t tgt, u_char clear)
+{
+#define FLOD_STATS_TOO_SHORT "buffer too short\n"
+#define FLOD_STATS_ALLOC(i) (p += (i), len -= (i))
+	OFLOD_INFO *ofp, *ofp1;
+	FLOD_MMAP *mp;
+	char now_buf[26], time_buf[26], fg_buf[60];
+	DCC_SRVR_ID min_srvr, max_srvr;
+	u_char loaded;
+	int len, i;
+	char *p;
+
+	if (buf_len < ISZ(FLOD_STATS_TOO_SHORT))
+		return 0;
+	len = buf_len - ISZ(FLOD_STATS_TOO_SHORT);
+	p = buf;
+
+	if (flod_mmaps) {
+		loaded = 0;
+	} else if (!load_flod(0)) {
+		return -1;
+	} else {
+		loaded = 1;
+	}
+
+	if (tgt <= DCC_SRVR_ID_MAX) {
+		/* an explicit target server-ID was specified */
+		min_srvr = max_srvr = tgt;
+	} else {
+		/* look for next server-ID after the target value */
+		min_srvr = tgt - DCC_SRVR_ID_MAX;
+		max_srvr = DCC_SRVR_ID_MAX;
+	}
+	ofp = 0;
+	for (ofp1 = oflods.infos; ofp1 <= LAST(oflods.infos); ++ofp1) {
+		if (ofp1->rem_hostname[0] != '\0'
+		    && ofp1->rem_id >= min_srvr
+		    && ofp1->rem_id <= max_srvr) {
+			/* This peer fits and is the best so far. */
+			ofp = ofp1;
+			max_srvr = ofp->rem_id-1;
+		}
+	}
+	if (!ofp) {
+		i = snprintf(p, len,
+			     DCC_AOP_FLOD_STATS_ID"unknown remote server-ID",
+			     tgt);
+		FLOD_STATS_ALLOC(i);
+		if (loaded)
+			oflods_clear();
+		return p-buf;
+	}
+	mp = ofp->mp;
+
+	save_flod_cnts(ofp);
+	i = snprintf(p, len,
+		     DCC_AOP_FLOD_STATS_ID" %s%s  %s\n  status start %s",
+		     ofp->rem_id, mp->rem_hostname,
+		     flodmap_fg(fg_buf, sizeof(fg_buf), "  ", mp),
+		     dcc_time2str(now_buf, sizeof(now_buf), "%b %d %X %Z",
+				  db_time.tv_sec),
+		     dcc_time2str(time_buf, sizeof(time_buf), "%b %d %X %Z",
+				  mp->cnts.cnts_cleared));
+	FLOD_STATS_ALLOC(i);
+
+	flod_stats_conn_total(&p, &len, "output", mp->cnts.out_total_conn);
+	i = snprintf(p, len, "     "L_DPAT" reports sent\n",
+		     mp->cnts.out_reports+ofp->cnts.out_reports);
+	FLOD_STATS_ALLOC(i);
+	flod_stats_conn_cur(&p, &len, ofp, 0);
+	i = snprintf(p, len, "\n     position "L_HPAT, mp->confirm_pos);
+	FLOD_STATS_ALLOC(i);
+
+	flod_stats_conn_total(&p, &len, "input", mp->cnts.in_total_conn);
+	i = snprintf(p, len,
+		     "     "L_DPAT" reports received  "L_DPAT" accepted"
+		     "  "L_DPAT" duplicate  "L_DPAT" stale\n"
+		     "     "L_DPAT" bad whitelist  "L_DPAT" not deleted\n",
+		     mp->cnts.total+ofp->cnts.total,
+		     mp->cnts.accepted+ofp->cnts.accepted,
+		     mp->cnts.dup+ofp->lc.dup.cur,
+		     mp->cnts.stale+ofp->lc.stale.cur,
+		     mp->cnts.wlist+ofp->lc.wlist.cur,
+		     mp->cnts.not_deleted+ofp->lc.not_deleted.cur);
+	FLOD_STATS_ALLOC(i);
+	flod_stats_conn_cur(&p, &len, ofp, 1);
+
+	if (len <= 0) {
+		strcpy(buf, FLOD_STATS_TOO_SHORT);
+		if (loaded)
+			oflods_clear();
+		return ISZ(FLOD_STATS_TOO_SHORT);
+	}
+
+	if (clear) {
+		flod_try_again(ofp);
+		save_flod_cnts(ofp);
+		ofp->limit_reset = 0;
+		memset(&mp->cnts, 0, sizeof(mp->cnts));
+		mp->cnts.cnts_cleared = db_time.tv_sec;
+	}
+
+	if (loaded)
+		oflods_clear();
+	return p-buf;
+#undef FLOD_STATS_TOO_SHORT
+#undef FLOD_STATS_ALLOC
+}
diff -r 000000000000 -r c7f6b056b673 dccd/oflod.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd/oflod.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,2483 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * deal with outgoing floods of checksums
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.151 $Revision$
+ */
+
+#include "dccd_defs.h"
+#include "dcc_ck.h"
+
+
+int flods_off;				/* # of reasons flooding is off */
+
+time_t next_flods_ck;
+time_t flod_mtime = 1;
+enum FLODS_ST flods_st = FLODS_ST_OFF;
+OFLODS oflods;
+
+/* records after this have not been flooded
+ *	0 if invalid */
+DB_PTR oflods_max_cur_pos;
+
+int summarize_delay_secs;		/* delay summaries by this */
+
+
+static void oflod_fill(OFLOD_INFO *);
+
+
+/* the socket must already be closed */
+static void
+oflod_clear(OFLOD_INFO *ofp)
+{
+	memset(ofp, 0, sizeof(*ofp));
+	ofp->soc = -1;
+}
+
+
+
+void
+oflods_clear(void)
+{
+	OFLOD_INFO *ofp;
+
+	iflods_stop("", 1);		/* paranoia */
+
+	flod_unmap(0, &dccd_stats);
+
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp)
+		oflod_clear(ofp);
+	oflods.total = 0;
+	complained_many_iflods = 0;
+	oflods_max_cur_pos = 0;
+}
+
+
+
+/* parse ID1->tgt in a flood file entry */
+static char
+oflod_parse_map(OFLOD_OPTS *opts, const char *str0, int lno)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	const char *str;
+	OFLOD_SRVR_ID_MAP *imp;
+
+	if (opts->num_maps >= DIM(opts->srvr_map)) {
+		dcc_error_msg("too many ID mappings with\"%s\"%s",
+			      str0, fnm_lno(&fnm_buf, flod_path, lno));
+		return 0;
+	}
+	imp = &opts->srvr_map[opts->num_maps];
+
+	if (!CLITCMP(str0, "self->")) {
+		str = str0+LITZ("self->");
+		imp->from_lo = imp->from_hi = my_srvr_id;
+	} else if (!CLITCMP(str0, "all->")) {
+		str = str0+LITZ("all->");
+		imp->from_lo = DCC_SRVR_ID_MIN;
+		imp->from_hi = DCC_SRVR_ID_MAX;
+	} else {
+		/* get ID1 */
+		str = dcc_get_srvr_id(0, &imp->from_lo, str0, "-",
+				      flod_path, lno);
+		if (!str)
+			return 0;
+		if (str[0] == '-' && str[1] == '>') {
+			/* ID1 is not a range */
+			imp->from_hi = imp->from_lo;
+		} else {
+			/* ID1 is a range of IDs */
+			str = dcc_get_srvr_id(0, &imp->from_hi,
+					      str+1, "-", flod_path, lno);
+			if (!str)
+				return 0;
+			if (imp->from_hi < imp->from_lo) {
+				dcc_error_msg("invalid ID mapping range "
+					      "\"%d-%d\"%s",
+					      imp->from_lo, imp->from_hi,
+					      fnm_lno(&fnm_buf,
+						      flod_path, lno));
+				return 0;
+			}
+		}
+		if (*str++ != '-' || *str++ != '>') {
+			dcc_error_msg("invalid server-ID mapping \"%s\"%s",
+				      str0, fnm_lno(&fnm_buf, flod_path, lno));
+			return 0;
+		}
+	}
+	if (!strcasecmp(str, "self")) {
+		imp->result = ID_MAP_SELF;
+	} else if (!strcasecmp(str, "reject")) {
+		imp->result = ID_MAP_REJ;
+	} else if (!strcasecmp(str, "ok")) {
+		imp->result = ID_MAP_NO;
+	} else {
+		dcc_error_msg("invalid ID mapping result \"%s\"%s",
+			      str, fnm_lno(&fnm_buf, flod_path, lno));
+		return 0;
+	}
+
+	++opts->num_maps;
+	return 1;
+}
+
+
+
+static u_char
+ck_socks_flags(OFLOD_INFO *ofp, OPT_FLAGS new,
+	       DCC_FNM_LNO_BUF fnm_buf, int lno)
+{
+	if (0 != (ofp->o_opts.flags & (FLOD_OPT_PASSIVE | FLOD_OPT_SOCKS
+				       | FLOD_OPT_NAT)
+		  & ~new)) {
+		dcc_error_msg("only one of \"passive\", \"SOCKS\", or \"NAT\""
+			      " allowed%s",
+			      fnm_lno(&fnm_buf, flod_path, lno));
+		return 0;
+	}
+
+	if ((new & (FLOD_OPT_SOCKS | FLOD_OPT_NAT)) != 0
+	    && (ofp->loc_hostname[0] != '\0' || ofp->loc_port != 0)) {
+		dcc_error_msg("source host name or port number"
+			      " and \"SOCKS\" or \"NAT\""
+			      " cannot both be set%s",
+			      fnm_lno(&fnm_buf, flod_path, lno));
+		ofp->loc_hostname[0] = '\0';
+		ofp->loc_port = 0;
+	}
+
+	ofp->o_opts.flags |= new;
+	return 1;
+}
+
+
+
+/* parse remote or local options that can be any of
+ *  a "off", "del", "no-del", "log-del", "passive", ID->map, etc. */
+static const char *			/* rest of the line */
+parse_flod_opts(OFLOD_INFO *ofp,
+		OFLOD_OPTS *opts,
+		const char *buf, int lno)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	char opts_buf[200];
+	char opt[20];
+	const char *buf_ptr, *p;
+	char *end;
+	unsigned long l;
+	u_int olen;
+
+	/* pick out the blank delimited string of options */
+	buf = dcc_parse_word(0, opts_buf, sizeof(opts_buf),
+			     buf, "flood options", flod_path, lno);
+	if (!buf)
+		return 0;
+
+	opts->path_len = DCC_MAX_FLOD_PATH;
+	if (grey_on)
+		opts->flags |= (FLOD_OPT_DEL_OK
+				| FLOD_OPT_NO_LOG_DEL
+				| FLOD_OPT_DEL_SET);
+
+	/* parse the options */
+	buf_ptr = opts_buf;
+	while (*buf_ptr != '\0') {
+		if (*buf_ptr == ',') {
+			++buf_ptr;
+			continue;
+		}
+		olen = strcspn(buf_ptr, ",");
+		if (olen >= sizeof(opt))
+			olen = sizeof(opt)-1;
+		strncpy(opt, buf_ptr, olen);
+		opt[olen] = '\0';
+		buf_ptr += olen;
+
+		/* ignore "-" */
+		if (!strcmp(opt, "-"))
+			continue;
+
+		if (!strcasecmp(opt, "off")) {
+			opts->flags |= FLOD_OPT_OFF;
+			continue;
+		}
+
+		if (!grey_on) {
+			if (!strcasecmp(opt, "traps")) {
+				opts->flags |= FLOD_OPT_TRAPS;
+				continue;
+			}
+			if (!strcasecmp(opt, "no-del")) {
+				opts->flags &= ~FLOD_OPT_DEL_OK;
+				opts->flags |= FLOD_OPT_DEL_SET;
+				continue;
+			}
+			if (!strcasecmp(opt, "del")) {
+				opts->flags |= FLOD_OPT_DEL_OK;
+				opts->flags |= FLOD_OPT_DEL_SET;
+				continue;
+			}
+		}
+
+		/* put some options in one or the other flag word no matter
+		 * for which they are specified */
+		if (!strcasecmp(opt, "trace")) {
+			ofp->o_opts.flags |= FLOD_OPT_TRACE;
+			continue;
+		}
+		if (!strcasecmp(opt, "trace2")) {
+			ofp->o_opts.flags |= FLOD_OPT_TRACE2;
+			continue;
+		}
+
+		if (!strcasecmp(opt, "no-log-del")) {
+			ofp->i_opts.flags |= FLOD_OPT_NO_LOG_DEL;
+			continue;
+		}
+		if (!strcasecmp(opt, "log-del")) {
+			ofp->i_opts.flags &= ~FLOD_OPT_NO_LOG_DEL;
+			continue;
+		}
+		if (!strcasecmp(opt, "passive")) {
+			if (!ck_socks_flags(ofp, FLOD_OPT_PASSIVE,
+					    fnm_buf, lno))
+				return 0;
+			continue;
+		}
+		if (!strcasecmp(opt, "socks")) {
+			if (!ck_socks_flags(ofp, FLOD_OPT_SOCKS,
+					    fnm_buf, lno))
+				return 0;
+			continue;
+		}
+		if (!strcasecmp(opt, "nat")) {
+			if (!ck_socks_flags(ofp, FLOD_OPT_NAT,
+					    fnm_buf, lno))
+				return 0;
+			continue;
+		}
+		if (!strcasecmp(opt, "IPv4")) {
+			if (ofp->o_opts.flags & FLOD_OPT_IPv6) {
+				dcc_error_msg("\"IPv4\" and \"IPv6\";"
+					      " cannot both be%s",
+					      fnm_lno(&fnm_buf,
+						      flod_path, lno));
+				return 0;
+			}
+			ofp->o_opts.flags |= FLOD_OPT_IPv4;
+			continue;
+		}
+		if (!strcasecmp(opt, "IPv6")) {
+			if (ofp->o_opts.flags & FLOD_OPT_IPv4) {
+				dcc_error_msg("\"IPv4\" and \"IPv6\";"
+					      " cannot both be%s",
+					      fnm_lno(&fnm_buf,
+						      flod_path, lno));
+				return 0;
+			}
+			ofp->o_opts.flags |= FLOD_OPT_IPv6;
+			continue;
+		}
+		if (!CLITCMP(opt, "leaf=")
+		    && (l = strtoul(opt+LITZ("leaf="), &end, 10),
+			*end == '\0')) {
+			if (l > DCC_MAX_FLOD_PATH)
+				l = DCC_MAX_FLOD_PATH;
+			ofp->o_opts.path_len = l;
+			continue;
+		}
+
+#ifdef DCC_FLOD_VERSION7
+		if (!strcasecmp(opt, "version7")) {
+			ofp->oversion = DCC_FLOD_VERSION7;
+			continue;
+		}
+
+#endif
+		/* parse an ID->map */
+		p = strchr(opt, '>');
+		if (p && p > opt && *(p-1) == '-') {
+			if (!oflod_parse_map(opts, opt, lno))
+				return 0;
+			continue;
+		}
+
+		dcc_error_msg("unknown option \"%s\"%s",
+			      opt, fnm_lno(&fnm_buf, flod_path, lno));
+		return 0;
+	}
+
+	return buf;
+}
+
+
+
+static const char *			/* rest of a flod file line */
+oflod_parse_id(DCC_SRVR_ID *id,
+	       const char *buf, const char *type, int lno)
+{
+	char id_buf[20];
+
+	buf = dcc_parse_word(0, id_buf, sizeof(id_buf),
+			     buf, type, flod_path, lno);
+	if (!buf)
+		return 0;
+
+	if (!strcmp(id_buf, "-")
+	    || id_buf[0] == '\0') {
+		*id = DCC_ID_INVALID;
+		return buf;
+	}
+
+	if (!dcc_get_srvr_id(0, id, id_buf, 0, flod_path, lno))
+		return 0;
+
+	/* do not check whether we know the local ID here, because
+	 * changes in the ids file can make that check moot */
+
+	return buf;
+}
+
+
+
+/* compute the maximum position among all floods */
+static void
+get_oflods_max_cur_pos(void)
+{
+	OFLOD_INFO *ofp;
+
+	oflods_max_cur_pos = DB_PTR_BASE;
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		if (ofp->rem_hostname[0] != '\0'
+		    && oflods_max_cur_pos < ofp->cur_pos)
+			oflods_max_cur_pos = ofp->cur_pos;
+	}
+}
+
+
+
+static void
+copy_opts2mp(OFLOD_INFO *ofp)
+{
+	FLOD_MMAP *mp;
+	FLOD_MMAP_FLAGS new_flags;
+	u_char changed;
+
+	mp = ofp->mp;
+	changed = 0;
+	new_flags = mp->flags & (FLODMAP_FG_REWINDING
+				 | FLODMAP_FG_NEED_REWIND
+				 | FLODMAP_FG_FFWD_IN
+				 | FLODMAP_FG_OUT_SRVR
+				 | FLODMAP_FG_IN_SRVR
+				 | FLODMAP_FG_NAT_AUTO
+				 | FLODMAP_FG_USE_2PASSWD);
+
+	if (db_parms.flags & DB_PARM_FG_CLEARED) {
+		new_flags |= FLODMAP_FG_NEED_REWIND;
+		new_flags &= ~FLODMAP_FG_FFWD_IN;
+		changed = 1;
+	}
+
+	if (ofp->o_opts.flags & FLOD_OPT_ROGUE)
+		new_flags |= FLODMAP_FG_ROGUE;
+
+	if ((ofp->o_opts.flags & FLOD_OPT_OFF))
+		new_flags |= FLODMAP_FG_OUT_OFF;
+
+	if (ofp->i_opts.flags & FLOD_OPT_OFF)
+		new_flags |= FLODMAP_FG_IN_OFF;
+
+	if (ofp->o_opts.flags & FLOD_OPT_IPv4) {
+		new_flags |= FLODMAP_FG_IPv4;
+		if (mp->flags & FLODMAP_FG_IPv6)
+			got_hosts = 0;
+	} else if (ofp->o_opts.flags & FLOD_OPT_IPv6) {
+		new_flags |= FLODMAP_FG_IPv6;
+		if (mp->flags & FLODMAP_FG_IPv4)
+			got_hosts = 0;
+	}
+
+	if (ofp->o_opts.flags & FLOD_OPT_PASSIVE) {
+		new_flags |= FLODMAP_FG_PASSIVE;
+	} else if (ofp->o_opts.flags & FLOD_OPT_SOCKS) {
+		new_flags |= FLODMAP_FG_SOCKS;
+	} else if (ofp->o_opts.flags & FLOD_OPT_NAT) {
+		new_flags |= FLODMAP_FG_NAT;
+	}
+
+	if (ofp->o_opts.path_len != DCC_MAX_FLOD_PATH)
+		new_flags |= FLODMAP_FG_LEAF;
+
+	if (ofp->o_opts.num_maps != 0
+	    || ofp->i_opts.num_maps != 0)
+		new_flags |= FLODMAP_FG_MAPPED;
+
+	if ((mp->flags ^ new_flags) && (FLODMAP_FG_ROGUE
+					| FLODMAP_FG_OUT_OFF
+					| FLODMAP_FG_IN_OFF
+					| FLODMAP_FG_IPv6
+					| FLODMAP_FG_IPv4
+					| FLODMAP_FG_PASSIVE
+					| FLODMAP_FG_SOCKS
+					| FLODMAP_FG_NAT
+					| FLODMAP_FG_LEAF
+					| FLODMAP_FG_MAPPED)) {
+		mp->flags = new_flags;
+		changed = 1;
+	}
+
+	/* get new hostname if it changes */
+	if (strcasecmp(mp->rem_hostname, ofp->rem_hostname)) {
+		BUFCPY(mp->rem_hostname, ofp->rem_hostname);
+		got_hosts = 0;		/* force name resolution for new name */
+		changed = 1;
+	}
+	/* always get new port name
+	 * in case the name but not the number changes */
+	BUFCPY(mp->rem_portname, ofp->rem_portname);
+	if (mp->rem_port != ofp->rem_port) {
+		mp->rem_port = ofp->rem_port;
+		changed = 1;
+	}
+
+	if (mp->ids_mtime != ids_mtime) {
+		mp->ids_mtime = ids_mtime;
+		mp->flags &= ~FLODMAP_FG_USE_2PASSWD;
+		changed = 1;
+	}
+
+	if (mp->in_passwd_id != ofp->in_passwd_id) {
+		mp->in_passwd_id = ofp->in_passwd_id;
+		changed = 1;
+	}
+	if (mp->out_passwd_id != ofp->out_passwd_id) {
+		mp->out_passwd_id = ofp->out_passwd_id;
+		changed = 1;
+	}
+
+	if (changed)
+		flod_try_again(ofp);
+}
+
+
+
+/* Load the hostnames of DCC server peers and their output flood positions.
+ *	flod_names_resolve_ck() must say ok before this function is called,
+ *	to avoid races with changing host names.
+ *
+ *	Parse lines of the form
+ *	  name,port;name,port rem-ID [passwd-id [out-opts [in-opts [versionX]]]]
+ */
+u_char					/* 1=ok to start flooding */
+load_flod(u_char complain)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	OFLOD_INFO *ofp, *ofp1;
+	FILE *f;
+	struct stat sb;
+	int lno;
+	char buf[200];
+	char hostname[60];
+	const char *bufp, *bufp1;
+	FLOD_MMAP *mp, *mp1;
+	union {
+	    OFLOD_INFO	info;
+	    FLOD_MMAP	map;
+	} swap;
+	const ID_TBL *tp;
+	char *p;
+	int i;
+
+	/* the caller should use RUSH_NEXT_FLODS_CK() or similar, but
+	 * just in case ... */
+	if (!flod_names_resolve_ck())
+		return 0;
+
+	/* forget everything about flooding */
+	oflods_clear();
+
+	/* keep the map open and locked most of the time */
+	if (!flod_mmap(dcc_emsg, &db_parms.sn, &dccd_stats, 1,
+		       (DCC_TRACE_FLOD_BIT & dccd_tracemask) != 0)) {
+		if (complain)
+			dcc_error_msg("%s", dcc_emsg);
+		flod_mtime = 0;
+		return 0;
+	}
+
+	f = fopen(flod_path, "r");
+	if (!f) {
+		if (flod_mtime != 0) {
+			dcc_error_msg("fopen(%s): %s",
+				      flod_path, ERROR_STR());
+			flod_mtime = 0;
+		}
+
+		flod_unmap(0, &dccd_stats);
+		return 0;
+	}
+	if (0 > fstat(fileno(f), &sb)) {
+		if (flod_mtime!= 0)
+			dcc_error_msg("stat(%s): %s",
+				      flod_path, ERROR_STR());
+		fclose(f);
+		flod_unmap(0, &dccd_stats);
+		flod_mtime = 0;
+		return 0;
+	}
+	flod_mtime = sb.st_mtime;
+
+	/* Parse the ASCII file of names and parameters first so that we do not
+	 * destroy the position information if there is a problem with names */
+	ofp = oflods.infos;
+	lno = 0;
+	for (;;) {
+		/* clear the entry in case we started to set it with the
+		 * preceding line from the /var/dcc/flod file */
+		if (ofp <= LAST(oflods.infos))
+			oflod_clear(ofp);
+
+		++lno;
+		bufp = fgets(buf, sizeof(buf), f);
+		if (!bufp) {
+			if (ferror(f)) {
+				dcc_error_msg("fgets(%s): %s",
+					      flod_path, ERROR_STR());
+				break;
+			}
+			if (fclose(f) == EOF) {
+				dcc_error_msg("fclose(%s): %s",
+					      flod_path, ERROR_STR());
+			}
+			f = 0;
+			break;
+		}
+		i = strlen(bufp);
+		if (i >= ISZ(buf)-1) {
+			dcc_error_msg("too many characters%s",
+				      fnm_lno(&fnm_buf, flod_path, lno));
+			do {
+				i = getc(f);
+			} while (i != '\n' && i != EOF);
+			continue;
+		}
+		/* ignore comments */
+		p = strchr(bufp, '#');
+		if (p)
+			*p = '\0';
+		else
+			p = &buf[i];
+		/* trim trailing blanks */
+		while (--p > bufp && (*p == ' ' || *p == '\t' || *p == '\n'))
+			*p = '\0';
+		/* skip blank lines */
+		bufp += strspn(bufp, DCC_WHITESPACE);
+		if (*bufp == '\0')
+			continue;
+
+		if (oflods.total >= DIM(oflods.infos)) {
+			dcc_error_msg("too many DCC peers in %s; max=%d",
+				      flod_path, DIM(oflods.infos));
+			continue;
+		}
+
+		ofp->lno = lno;
+
+		/* get IP address and port number of remote DCC server */
+		bufp1 = bufp+strcspn(bufp, DCC_WHITESPACE";");
+		if (*bufp1 != ';') {
+			bufp1 = 0;
+		} else {
+			/* Allow the local or client TCP IP address and
+			 * port number to be specified. */
+			buf[bufp1++ - buf] = '\0';
+		}
+		bufp = dcc_parse_nm_port(0, bufp, def_port,
+					 ofp->rem_hostname,
+					 sizeof(ofp->rem_hostname),
+					 &ofp->rem_port,
+					 ofp->rem_portname,
+					 sizeof(ofp->rem_portname),
+					 flod_path, lno);
+		if (!bufp)
+			continue;
+		if (bufp1) {
+			/* parse the local IP address first */
+			bufp = dcc_parse_nm_port(0, bufp1, 0,
+						 ofp->loc_hostname,
+						 sizeof(ofp->loc_hostname),
+						 &ofp->loc_port, 0, 0,
+						 flod_path, lno);
+			if (!bufp)
+				continue;
+		}
+
+		bufp = oflod_parse_id(&ofp->rem_id, bufp,
+				      "rem-id", lno);
+		if (!bufp)
+			continue;
+		if (ofp->rem_id == DCC_ID_INVALID) {
+			dcc_error_msg("missing rem-id%s",
+				      fnm_lno(&fnm_buf, flod_path, lno));
+			continue;
+		}
+
+		bufp = oflod_parse_id(&ofp->out_passwd_id, bufp,
+				      "passwd-id", lno);
+		if (!bufp)
+			continue;
+		if (ofp->out_passwd_id == DCC_ID_INVALID) {
+			ofp->out_passwd_id = my_srvr_id;
+			ofp->in_passwd_id = ofp->rem_id;
+		} else {
+			ofp->in_passwd_id = ofp->out_passwd_id;
+		}
+
+		ofp->oversion = DCC_FLOD_VERSION_DEF;
+		bufp = parse_flod_opts(ofp, &ofp->o_opts, bufp, lno);
+		if (!bufp)
+			continue;
+		bufp = parse_flod_opts(ofp, &ofp->i_opts, bufp, lno);
+		if (!bufp)
+			continue;
+		if (*bufp != '\0')
+			dcc_error_msg("trailing garbage \"%s\" ignored%s",
+				      bufp, fnm_lno(&fnm_buf, flod_path, lno));
+
+		tp = find_srvr_type(ofp->rem_id);
+		if (0 < find_srvr_rcd_type(ofp->rem_id)) {
+			switch (DB_RCD_ID(db_sts.rcd2.d.r)) {
+			case DCC_ID_SRVR_REP_OK:
+			case DCC_ID_SRVR_SIMPLE:
+				ofp->o_opts.flags |= FLOD_OPT_SIMPLE;
+				break;
+			case DCC_ID_SRVR_ROGUE:
+				ofp->o_opts.flags |= FLOD_OPT_ROGUE;
+				break;
+			default:
+				dcc_error_msg("unknown state for server-ID %d",
+					      ofp->rem_id);
+				break;
+			}
+		}
+
+		/* both servers having spam traps and assuming the other
+		 * doesn't makes no sense */
+		if (((ofp->o_opts.flags & FLOD_OPT_TRAPS)
+		     || (ofp->i_opts.flags & FLOD_OPT_TRAPS))
+		    && !(ofp->i_opts.flags & FLOD_OPT_OFF)
+		    && !(ofp->o_opts.flags & FLOD_OPT_OFF)) {
+			dcc_error_msg("symmetric trap-only link%s",
+				      fnm_lno(&fnm_buf, flod_path, lno));
+			continue;
+		}
+
+		for (ofp1 = oflods.infos; ofp1 < ofp; ++ofp1) {
+			if ((!strcmp(ofp1->rem_hostname, ofp->rem_hostname)
+			     && ofp1->rem_port == ofp->rem_port)
+			    || ofp1->rem_id == ofp->rem_id)
+				break;
+		}
+		if (ofp1 != ofp) {
+			dcc_error_msg("duplicate DCC peer%s",
+				      fnm_lno(&fnm_buf, flod_path, lno));
+			continue;
+		}
+
+		/* ignore ourself */
+		if (ofp->rem_id == my_srvr_id)
+			continue;
+
+		ofp->limit_reset = db_time.tv_sec + FLOD_LIM_CLEAR_SECS;
+
+		++ofp;
+		++oflods.total;
+	}
+	if (f)
+		fclose(f);
+
+	/* sort new list by server-ID so that `cdcc "flood list"` is sorted */
+	ofp = oflods.infos;
+	while (ofp < LAST(oflods.infos)) {
+		ofp1 = ofp+1;
+		if (ofp1->rem_hostname[0] == '\0')
+			break;
+		if (ofp->rem_id <= ofp1->rem_id) {
+			ofp = ofp1;
+			continue;
+		}
+		/* bubble sort because the list is usually already
+		 * ordered and almost always tiny */
+		memcpy(&swap.info, ofp1, sizeof(swap.info));
+		memcpy(ofp1, ofp, sizeof(*ofp1));
+		memcpy(ofp, &swap.info, sizeof(*ofp));
+		ofp = oflods.infos;
+	}
+
+	mp = flod_mmaps->mmaps;
+
+	/* Bubble sort the list in the /var/dcc/flod/map file so that is
+	 * sorted for `dblist -Hv`.  The file will usually already be sorted
+	 * and is tiny. */
+	mp1 = mp+1;
+	while (mp1 <= LAST(flod_mmaps->mmaps)) {
+		if (mp1->rem_hostname[0] == '\0') {
+			++mp1;
+			continue;
+		}
+		if (mp->rem_hostname[0] == '\0'
+		    || mp->rem_id <= mp1->rem_id) {
+			mp = mp1++;
+			continue;
+		}
+		memcpy(&swap.map, mp1, sizeof(swap.map));
+		memcpy(mp1, mp, sizeof(*mp1));
+		memcpy(mp, &swap.map, sizeof(*mp));
+		mp = flod_mmaps->mmaps;
+		mp1 = mp+1;
+	}
+
+	/* combine our list that is based on the ASCII file /var/dcc/flod
+	 * with the memory mapped /var/dcc/flod.map list of what has
+	 * been sent to each peer */
+	for (mp = flod_mmaps->mmaps; mp <= LAST(flod_mmaps->mmaps); ++mp)
+		mp->flags &= ~FLODMAP_FG_MARK;
+
+	/* make one pass matching old names with their slots in the
+	 * mapped file */
+	mp = flod_mmaps->mmaps;
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		if (ofp->rem_hostname[0] == '\0')
+			break;
+		for (i = 0; i < DIM(flod_mmaps->mmaps); ++i) {
+			if (++mp > LAST(flod_mmaps->mmaps))
+				mp = flod_mmaps->mmaps;
+			if (mp->rem_hostname[0] == '\0')
+				continue;
+			if (!(mp->flags & FLODMAP_FG_MARK)
+			    && ofp->rem_id == mp->rem_id) {
+				/* found the old slot */
+				if (DB_PTR_IS_BAD(mp->confirm_pos)
+				    || mp->confirm_pos > db_csize) {
+					dcc_error_msg("bogus position "L_HPAT
+						    " for %s in %s",
+						    mp->confirm_pos,
+						    ofp->rem_hostname,
+						    flod_mmap_path);
+					mp->rem_hostname[0] = '\0';
+					continue;
+				}
+				ofp->cur_pos = mp->confirm_pos;
+				ofp->rewind_pos = db_csize;
+				ofp->mp = mp;
+				copy_opts2mp(ofp);
+				mp->flags |= FLODMAP_FG_MARK;
+				break;
+			}
+		}
+	}
+
+
+	/* use a free or obsolete slot in the mapped file for new entries */
+	mp = flod_mmaps->mmaps;
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		/* find the next new peer without a mapped file slot */
+		if (ofp->rem_hostname[0] == '\0')
+			break;
+		if (ofp->mp != 0)
+			continue;
+
+		/* find a free or no longer used slot */
+		while (mp->flags & FLODMAP_FG_MARK) {
+			if (++mp > LAST(flod_mmaps->mmaps)) {
+				bad_stop("too few oflod mmap slots");
+				goto out;
+			}
+		}
+		if (mp->rem_hostname[0] != '\0')
+			dcc_error_msg("forget flood to %s %d",
+				      dcc_host_portname(hostname,
+							sizeof(hostname),
+							mp->rem_hostname,
+							mp->rem_portname),
+				      mp->rem_id);
+
+		/* we have found a free slot */
+		memset(mp, 0, sizeof(*mp));
+		ofp->mp = mp;
+		mp->rem_su.sa.sa_family = AF_UNSPEC;
+		mp->rem_id = ofp->rem_id;
+		copy_opts2mp(ofp);
+
+		mp->cnts.cnts_cleared = db_time.tv_sec;
+
+		if (flod_mmaps->delay_pos < DB_PTR_BASE) {
+			/* do not rewind if repairing a broken file */
+			ofp->cur_pos = mp->confirm_pos = db_csize;
+			ofp->recv_pos = ofp->xmit_pos = db_csize;
+		} else {
+			ofp->cur_pos = mp->confirm_pos = DB_PTR_BASE;
+			ofp->recv_pos = ofp->xmit_pos = DB_PTR_BASE;
+		}
+
+		mp->flags |= FLODMAP_FG_MARK;
+
+		dcc_error_msg("initialize flood to %s %d%s",
+			      dcc_host_portname(hostname, sizeof(hostname),
+						mp->rem_hostname,
+						mp->rem_portname),
+			      mp->rem_id,
+			      fnm_lno(&fnm_buf, flod_path, ofp->lno));
+	}
+out:;
+
+	/* clear the slots that contain forgotten hosts */
+	for (mp = flod_mmaps->mmaps; mp <= LAST(flod_mmaps->mmaps); ++mp) {
+		if (!(mp->flags & FLODMAP_FG_MARK)) {
+			if (mp->rem_hostname[0] != '\0')
+				dcc_error_msg("forget flood to %s %d",
+					      dcc_host_portname(hostname,
+							sizeof(hostname),
+							mp->rem_hostname,
+							mp->rem_portname),
+					      mp->rem_id);
+			memset(mp, 0, sizeof(*mp));
+		}
+	}
+
+	flod_mmap_sync(0, 1);
+
+	db_parms.flags &= ~DB_PARM_FG_CLEARED;
+	db_flush_parms(0);
+
+	get_oflods_max_cur_pos();
+	return 1;
+}
+
+
+
+/* put the flood counters in stable storage */
+void
+save_flod_cnts(OFLOD_INFO *ofp)
+{
+	FLOD_MMAP *mp;
+	time_t delta;
+	FLOD_LIMCNT *lc;
+
+	dccd_stats.iflod_total += ofp->cnts.total;
+	dccd_stats.iflod_accepted += ofp->cnts.accepted;
+	dccd_stats.iflod_stale += ofp->lc.stale.cur;
+	dccd_stats.iflod_dup += ofp->lc.dup.cur;
+	dccd_stats.iflod_wlist += ofp->lc.wlist.cur;
+	dccd_stats.iflod_not_deleted += ofp->lc.not_deleted.cur;
+
+	mp = ofp->mp;
+	if (mp) {
+		if (ofp->xmit_pos == ofp->recv_pos)
+			ofp->mp->confirm_pos = ofp->cur_pos;
+
+		mp->cnts.total += ofp->cnts.total;
+		mp->cnts.accepted += ofp->cnts.accepted;
+		mp->cnts.stale += ofp->lc.stale.cur;
+		mp->cnts.dup += ofp->lc.dup.cur;
+		mp->cnts.wlist += ofp->lc.wlist.cur;
+		mp->cnts.not_deleted += ofp->lc.not_deleted.cur;
+
+		mp->cnts.out_reports += ofp->cnts.out_reports;
+
+		delta = db_time.tv_sec - ofp->cnts.saved;
+		if (delta < 0)
+			delta = 0;
+		if (ofp->ifp) {
+			if (mp->flags & FLODMAP_FG_IN_CONN) {
+				mp->cnts.in_total_conn += delta;
+			} else {
+				mp->flags |= FLODMAP_FG_IN_CONN;
+				mp->cnts.in_conn_changed = db_time.tv_sec;
+			}
+		} else {
+			if (mp->flags & FLODMAP_FG_IN_CONN) {
+				mp->flags &= ~FLODMAP_FG_IN_CONN;
+				mp->cnts.in_conn_changed = db_time.tv_sec;
+			}
+		}
+
+		if (ofp->flags & OFLOD_FG_CONNECTED) {
+			if (mp->flags & FLODMAP_FG_OUT_CONN) {
+				mp->cnts.out_total_conn += delta;
+			} else {
+				mp->flags |= FLODMAP_FG_OUT_CONN;
+				mp->cnts.out_conn_changed = db_time.tv_sec;
+			}
+		} else {
+			if (mp->flags & FLODMAP_FG_OUT_CONN) {
+				mp->flags &= ~FLODMAP_FG_OUT_CONN;
+				mp->cnts.out_conn_changed = db_time.tv_sec;
+			}
+		}
+	}
+
+	memset(&ofp->cnts, 0, sizeof(ofp->cnts));
+	for (lc = (FLOD_LIMCNT *)&ofp->lc;
+	     lc < (FLOD_LIMCNT *)(sizeof(ofp->lc)+(char *)&ofp->lc);
+	     ++lc) {
+		lc->lim -= lc->cur;
+		lc->cur = 0;
+	}
+
+	ofp->cnts.saved = db_time.tv_sec;
+}
+
+
+
+void
+oflod_close(OFLOD_INFO *ofp, u_char fail)
+{
+	u_char need_oflods_clear = 0;
+
+	if (ofp->rem_hostname[0] == '\0')
+		return;
+
+	if (ofp->soc >= 0) {
+		if (0 > close(ofp->soc)
+		    && !fail) {
+			if (errno == ECONNRESET)
+				TMSG2_FLOD(ofp, "close(flood to %s): %s",
+					   ofp_rem_str(ofp), ERROR_STR());
+			else
+				dcc_error_msg("close(flod to %s): %s",
+					      ofp_rem_str(ofp), ERROR_STR());
+		}
+		ofp->soc = -1;
+		ofp->flags &= ~(OFLOD_FG_CONNECTED
+				| OFLOD_FG_SHUTDOWN_REQ
+				| OFLOD_FG_SHUTDOWN);
+		ofp->obuf_len = 0;
+		ofp->cnts.out_reports = 0;
+
+		save_flod_cnts(ofp);
+
+		if (--oflods.open == 0
+		    && iflods.open == 0
+		    && flods_st != FLODS_ST_ON)
+			need_oflods_clear = 1;
+	}
+
+	if (fail) {
+		if (ofp->mp->otimers.retry_secs < FLOD_RETRY_SECS/2)
+			ofp->mp->otimers.retry_secs = FLOD_RETRY_SECS/2;
+		ofp->mp->otimers.retry = (db_time.tv_sec
+					  + ofp->mp->otimers.retry_secs);
+		if (!(ofp->mp->flags & FLODMAP_FG_PASSIVE))
+			TMSG2_FLOD(ofp,
+				   "postpone restarting flood to %s"
+				   " for %d seconds",
+				   ofp_rem_str(ofp),
+				   ofp->mp->otimers.retry_secs);
+	}
+
+	if (need_oflods_clear)
+		oflods_clear();
+}
+
+
+
+/* get ready to shut down */
+static void
+start_shutdown(OFLOD_INFO *ofp)
+{
+	if (ofp->flags & (OFLOD_FG_SHUTDOWN_REQ
+			  | OFLOD_FG_SHUTDOWN))
+		return;
+
+	/* arrange to ask the peer to ask us to stop */
+	ofp->flags |= OFLOD_FG_SHUTDOWN_REQ;
+	ofp->flags &= ~OFLOD_FG_NEW;
+	oflod_fill(ofp);
+
+	/* oflod_write() might set this again, but that will either be soon
+	 * or a good thing if delayed */
+	ofp->oflod_alive = db_time.tv_sec;
+}
+
+
+
+/* Half-close the TCP connection.
+ *	The other DCC server will notice and send our final position
+ *	to acknowledge dealing with our reports. */
+static void
+oflod_shutdown(OFLOD_INFO *ofp)
+{
+	struct linger nowait;
+
+	/* wait until the output buffer is empty */
+	if (ofp->obuf_len != 0)
+		return;
+
+	/* do it only once */
+	if ((ofp->flags & OFLOD_FG_SHUTDOWN))
+		return;
+	ofp->flags |= OFLOD_FG_SHUTDOWN;
+
+	/* on Solaris and Linux you must set SO_LINGER before shutdown() */
+	nowait.l_onoff = 1;
+	nowait.l_linger = SHUTDOWN_DELAY;
+	if (0 > setsockopt(ofp->soc, SOL_SOCKET, SO_LINGER,
+			   &nowait, sizeof(nowait)))
+		rpt_err(ofp, 0, 0,
+			"setsockopt(SO_LINGER flood to %s): %s",
+			ofp_rem_str(ofp), ERROR_STR());
+
+	if (0 > shutdown(ofp->soc, 1)) {
+		rpt_err(ofp, errno == ECONNRESET, 0,
+			"shutdown(flood to %s): %s",
+			ofp_rem_str(ofp), ERROR_STR());
+		oflod_close(ofp, 1);
+	}
+}
+
+
+
+/* see if a report should be put into the output buffer for a flood
+ *  db_sts.rcd.d.r points to the current record.
+ *  ofp->cur_pos has already been advanced */
+static u_char				/* 1=flood this report, 0=don't */
+oflod_ck_put(void)
+{
+	const DB_RCD_CK *cur_rcd_ck;
+	DCC_TGTS rcd_tgts, ck_tgts;
+	int num_cks;
+	DCC_CK_TYPES type;
+	u_char obs_lvl, result;
+
+	/* skip padding, whitelisted, compressed, trimmed
+	 * and deleted entries */
+	if (DB_RCD_ID(db_sts.rcd.d.r) == DCC_ID_WHITE
+	    || DB_RCD_ID(db_sts.rcd.d.r) == DCC_ID_COMP
+	    || (rcd_tgts = DB_TGTS_RCD_RAW(db_sts.rcd.d.r)) == 0
+	    || DB_RCD_TRIMMED(db_sts.rcd.d.r))
+		return 0;
+
+	/* Skip reports that should not be flooded yet
+	 * The flooding thresholds are used to set the delay flag.
+	 * Small reports are marked with the delay flag when they are added
+	 * to the database.  If later it seems they should be flooded,
+	 * they are summarized in a new report that is flooded. */
+	if (DB_RCD_DELAY(db_sts.rcd.d.r))
+		return 0;
+
+	result = 0;
+	obs_lvl = 0;
+	cur_rcd_ck = db_sts.rcd.d.r->cks;
+	for (num_cks = DB_NUM_CKS(db_sts.rcd.d.r);
+	     num_cks != 0;
+	     ++cur_rcd_ck, --num_cks) {
+		type = DB_CK_TYPE(cur_rcd_ck);
+
+		/* ignore junk for deciding whether we can send this report. */
+		if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+			continue;
+
+		if (DB_CK_OBS(cur_rcd_ck)) {
+			/* an obsolete fuzzier checksum
+			 * makes less fuzzy checksums obsolete */
+			if (obs_lvl < db_ck_fuzziness[type]) {
+				obs_lvl = db_ck_fuzziness[type];
+				result = 0;
+			}
+			continue;
+		}
+
+		/* send server-ID declarations
+		 * unless they are delete requests */
+		if (type == DCC_CK_SRVR_ID) {
+			if (rcd_tgts == DCC_TGTS_DEL)
+				continue;
+			return 1;
+		}
+
+		/* do not send whitelisted reports */
+		ck_tgts = DB_TGTS_CK(cur_rcd_ck);
+		if (ck_tgts == DCC_TGTS_OK || ck_tgts == DCC_TGTS_OK2)
+			return 0;
+
+		/* send non-obsolete results */
+		if (obs_lvl <= db_ck_fuzziness[type]) {
+			obs_lvl = db_ck_fuzziness[type];
+			result = 1;
+		}
+	}
+	return result;
+}
+
+
+
+/* put report into the output buffer for a flood if appropriate
+ *  db_sts.rcd.d.r points to the current record.
+ *  ofp->cur_pos has already been advanced */
+static void
+put_rcd_obuf(OFLOD_INFO *ofp, const DB_RCD *cur_rcd)
+{
+	DCC_FLOD_RPT *rp;
+	DCC_TGTS tgts;
+	DCC_SRVR_ID srvr, psrvr;
+	const DB_RCD_CK *cur_rcd_ck;
+	DCC_CK *buf_ck;
+	DCC_FLOD_PATH_ID *new_path_idp, *new_path_id_limp, *rcd_path_id;
+	int path_id_max;
+	DCC_CK_TYPES type;
+	ID_MAP_RESULT srvr_mapped;
+	u_char reflecting;		/* 1=report is pointed at its source */
+	u_char non_path, all_spam;
+	int num_cks, j;
+
+	/* decide whether to send this report */
+	if (!oflod_ck_put())
+		return;			/* skip it */
+
+	rp = (DCC_FLOD_RPT *)&ofp->obuf.b[ofp->obuf_len];
+	db_ptr2flod_pos(rp->pos, ofp->cur_pos);
+	tgts = DB_TGTS_RCD_RAW(cur_rcd);
+	if (tgts == DCC_TGTS_DEL) {
+		/* don't send delete requests to systems that don't want them */
+		if (!(ofp->o_opts.flags & FLOD_OPT_DEL_OK))
+			return;
+	} else if (ofp->o_opts.flags & FLOD_OPT_TRAPS) {
+		tgts = DCC_TGTS_TOO_MANY;
+	}
+
+	srvr = DB_RCD_ID(cur_rcd);
+	/* translate the source server-ID */
+	srvr_mapped = id_map(srvr, &ofp->o_opts);
+	switch (srvr_mapped) {
+	case ID_MAP_NO:
+		break;
+	case ID_MAP_REJ:
+		return;
+	case ID_MAP_SELF:
+		srvr = my_srvr_id;
+		break;
+	}
+	/* this loses the DCC_SRVR_ID_AUTH bit */
+	rp->srvr_id_auth[0] = srvr>>8;
+	rp->srvr_id_auth[1] = srvr;
+
+	reflecting = (srvr == ofp->rem_id);
+	non_path = 0;
+
+	rp->ts = cur_rcd->ts;
+
+	cur_rcd_ck = cur_rcd->cks;
+
+	/* Add a path if we are not the source of the report
+	 * or if it already has a path */
+	buf_ck = rp->cks;
+	if (srvr != my_srvr_id
+	    || DB_CK_TYPE(cur_rcd_ck) == DCC_CK_FLOD_PATH) {
+		/* Add a checksum entry for a path consisting of only our
+		 * server-ID.  If the report contains a path, we will
+		 * concatenate to this entry */
+		memset(buf_ck, 0, sizeof(*buf_ck));
+		buf_ck->len = sizeof(*buf_ck);
+		buf_ck->type = DCC_CK_FLOD_PATH;
+		new_path_idp = (DCC_FLOD_PATH_ID *)buf_ck->sum;
+		new_path_idp->hi = my_srvr_id>>8;
+		new_path_idp->lo = my_srvr_id;
+		new_path_id_limp = new_path_idp + DCC_NUM_FLOD_PATH;
+		path_id_max = ofp->o_opts.path_len-1;
+		++new_path_idp;
+		++buf_ck;
+		rp->num_cks = 1;
+	} else {
+		/* do not add a path */
+		new_path_idp = new_path_id_limp = 0;
+		path_id_max = 0;
+		rp->num_cks = 0;
+	}
+
+	all_spam = 1;
+	for (num_cks = DB_NUM_CKS(cur_rcd);
+	     num_cks != 0;
+	     ++cur_rcd_ck, --num_cks) {
+		type = DB_CK_TYPE(cur_rcd_ck);
+		if (type == DCC_CK_FLOD_PATH) {
+			rcd_path_id = (DCC_FLOD_PATH_ID *)&cur_rcd_ck->sum;
+			for (j = 0; j < DCC_NUM_FLOD_PATH; ++j, ++rcd_path_id) {
+				psrvr = ((rcd_path_id->hi<<8)
+					 | rcd_path_id->lo);
+				/* stop copying the path at its end */
+				if (psrvr == DCC_ID_INVALID)
+					break;
+				/* don't send report if its path is too long */
+				if (--path_id_max < 0)
+					return;
+				/* add another "checksum" to continue path */
+				if (new_path_idp >= new_path_id_limp) {
+					memset(buf_ck, 0, sizeof(*buf_ck));
+					buf_ck->len = sizeof(*buf_ck);
+					buf_ck->type = DCC_CK_FLOD_PATH;
+					new_path_idp = (DCC_FLOD_PATH_ID *
+							)buf_ck->sum;
+					new_path_id_limp = (new_path_idp
+							+ DCC_NUM_FLOD_PATH);
+					++buf_ck;
+					++rp->num_cks;
+				}
+				/* Do not send reports from the target back
+				 * to the target unless the report is a
+				 * server-ID declaration */
+				if (psrvr == ofp->rem_id)
+					reflecting = 1;
+				switch (id_map(psrvr, &ofp->o_opts)) {
+				case ID_MAP_NO:
+					break;
+				case ID_MAP_REJ:
+					return;
+				case ID_MAP_SELF:
+					psrvr = my_srvr_id;
+					break;
+				}
+				new_path_idp->hi = psrvr>>8;
+				new_path_idp->lo = psrvr;
+				++new_path_idp;
+			}
+
+		} else {
+			/* Do not send translated server-ID declarations
+			 * or checksums in our own or in translated server-ID
+			 * reports that we wouldn't have kept if we had
+			 * received the original reports */
+			if (srvr_mapped == ID_MAP_SELF) {
+				if (type == DCC_CK_SRVR_ID)
+					return;
+				if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+					continue;
+			}
+			/* Do not send reports from the target to the target
+			 * unless the report is a Server-ID declaration */
+			if (reflecting && type != DCC_CK_SRVR_ID)
+				return;
+
+			/* send everything else */
+			buf_ck->type = type;
+			buf_ck->len = sizeof(*buf_ck);
+			memcpy(buf_ck->sum, cur_rcd_ck->sum,
+			       sizeof(buf_ck->sum));
+			++buf_ck;
+			++rp->num_cks;
+
+			non_path = 1;
+			if (all_spam
+			    && DB_TGTS_CK(cur_rcd_ck) != DCC_TGTS_TOO_MANY)
+				all_spam = 0;
+		}
+	}
+
+	/* quit if we found nothing but the path to send */
+	if (!non_path)
+		return;
+
+	if (all_spam && srvr == my_srvr_id)
+		tgts = DCC_TGTS_TOO_MANY;
+	tgts = htonl(tgts);
+	memcpy(&rp->tgts, &tgts, sizeof(rp->tgts));
+
+	ofp->obuf_len += (char *)buf_ck - (char *)rp;
+	++ofp->cnts.out_reports;
+	ofp->xmit_pos = ofp->cur_pos;
+}
+
+
+
+/* send reports from the database to a peer DCC server
+ *	This routine only fills the buffer.  The buffer is eventually
+ *	written by oflod_write(). */
+static void
+oflod_fill(OFLOD_INFO *ofp)
+{
+	int cur_rcd_len;
+
+	/* stop when things are not ready or shutting down */
+	if (!(ofp->flags & OFLOD_FG_CONNECTED)
+	    || (ofp->flags & OFLOD_FG_SHUTDOWN)
+	    || (ofp->flags & OFLOD_FG_NEW))
+		return;
+
+	/* stop when we are about to clean the database for a deletion so
+	 * that we will not be shut down cleaning along with our neighbors */
+	if (need_del_dbclean)
+		return;
+
+	if (db_failed_line)
+		return;
+
+	while (ofp->obuf_len < sizeof(ofp->obuf) - sizeof(DCC_FLOD_RPT)) {
+		/* start a new entry unless we are shutting down */
+		if (ofp->flags & OFLOD_FG_SHUTDOWN_REQ) {
+			oflod_shutdown(ofp);
+			break;
+		}
+
+		if (ofp->cur_pos >= db_csize) {
+			/* nothing to send
+			 * shut down if needed */
+			if (ofp->xmit_pos == ofp->recv_pos)
+				ofp->mp->confirm_pos = ofp->cur_pos;
+			if (ofp->mp->confirm_pos >= ofp->rewind_pos)
+				ofp->mp->flags &= ~FLODMAP_FG_REWINDING;
+			break;
+		}
+
+		/* don't try to look at reports crossing page bounardies */
+		if (ofp->cur_pos%db_pagesize >= db_page_max) {
+			ofp->cur_pos += DB_RCD_HDR_LEN;
+			continue;
+		}
+
+		if (!db_map_rcd(dcc_emsg, &db_sts.rcd, ofp->cur_pos,
+				&cur_rcd_len)) {
+			dcc_error_msg("oflod_fill() starting at "L_HPAT
+				      " for %s: %s",
+				      ofp->cur_pos, ofp_rem_str(ofp), dcc_emsg);
+			ofp->cur_pos = db_csize;
+			break;
+		}
+
+		if (DB_NUM_CKS(db_sts.rcd.d.r) > DCC_DIM_CKS) {
+			dcc_error_msg("impossible %d checksums in "L_HPAT,
+				      DB_NUM_CKS(db_sts.rcd.d.r),
+				      ofp->cur_pos);
+			ofp->cur_pos = db_csize;
+			break;
+		}
+
+		/* send the record */
+		ofp->cur_pos += cur_rcd_len;
+		put_rcd_obuf(ofp, db_sts.rcd.d.r);
+	}
+
+	if (oflods_max_cur_pos < ofp->cur_pos)
+		oflods_max_cur_pos = ofp->cur_pos;
+}
+
+
+
+/* figure out what version to tell the peer */
+const char *
+version_str(OFLOD_INFO *ofp)
+{
+	if (ofp->oversion == 0)
+		return DCC_FLOD_VERSION_CUR_STR;
+#ifdef DCC_FLOD_VERSION7
+	if (ofp->oversion == DCC_FLOD_VERSION7)
+		return DCC_FLOD_VERSION_CUR_STR;
+#endif
+	dcc_logbad(EX_SOFTWARE, "unknown ofp->oversion=%d",
+		   ofp->oversion);
+}
+
+
+
+/* reset connect() or daily complaint timers and keep backoffs steady */
+void
+flod_try_again(OFLOD_INFO *ofp)
+{
+	FLOD_MMAP *mp = ofp->mp;
+
+	mp = ofp->mp;
+	if (!mp)
+		return;
+
+	/* ordinary connect() timer should fire immediately */
+	mp->otimers.retry_secs /= 2;
+	mp->otimers.retry = 0;
+
+	/* delay complaints for passive connections */
+	if (DCC_IS_TIME(db_time.tv_sec+FLOD_IN_COMPLAIN_NOW,
+			mp->otimers.msg, mp->otimers.msg_secs)) {
+		mp->otimers.msg_secs = FLOD_IN_COMPLAIN_NOW;
+		mp->otimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN_NOW;
+	}
+
+	mp->oflod_err.msg[0] = '\0';
+	mp->oflod_err.trace_msg[0] = '\0';
+
+	/* give the peer a chance to connect to us */
+	mp->itimers.retry_secs /= 2;
+	if (mp->itimers.retry_secs < FLOD_SOCKS_SOCKS_IRETRY)
+		mp->itimers.retry_secs = FLOD_SOCKS_SOCKS_IRETRY;
+	mp->itimers.retry = db_time.tv_sec + FLOD_SOCKS_SOCKS_IRETRY;
+
+	if (DCC_IS_TIME(db_time.tv_sec+FLOD_IN_COMPLAIN_NOW,
+			mp->itimers.msg, mp->itimers.msg_secs)) {
+		mp->itimers.msg_secs = FLOD_IN_COMPLAIN_NOW;
+		mp->itimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN_NOW;
+	}
+
+	mp->iflod_err.msg[0] = '\0';
+	mp->iflod_err.trace_msg[0] = '\0';
+}
+
+
+
+/* authenticate the outgoing start of a flood */
+const char *				/* error message */
+flod_sign(OFLOD_INFO *ofp, u_char in, void *buf, int buf_len)
+{
+	const ID_TBL *tp;
+
+	tp = find_id_tbl(ofp->out_passwd_id);
+	if (!tp)
+		return DCC_FLOD_PASSWD_ID_MSG;
+	if (tp->cur_passwd[0] == '\0')
+		return "no password for passwd-ID";
+
+	if (tp->next_passwd[0] != '\0') {
+		ofp->flags |= OFLOD_FG_HAVE_2PASSWD;
+	} else {
+		ofp->flags &= ~OFLOD_FG_HAVE_2PASSWD;
+		ofp->mp->flags &= ~FLODMAP_FG_USE_2PASSWD;
+	}
+	if (ofp->mp->flags & FLODMAP_FG_USE_2PASSWD) {
+		if (in)
+			ofp->flags |= OFLOD_FG_I_USED_2PASSWD;
+		else
+			ofp->flags |= OFLOD_FG_O_USED_2PASSWD;
+		TMSG1_FLOD(ofp, "try 2nd password to %s", ofp_rem_str(ofp));
+		dcc_sign(tp->next_passwd, sizeof(tp->next_passwd),
+			 buf, buf_len);
+	} else {
+		if (in)
+			ofp->flags &= ~OFLOD_FG_I_USED_2PASSWD;
+		else
+			ofp->flags &= ~OFLOD_FG_O_USED_2PASSWD;
+		dcc_sign(tp->cur_passwd, sizeof(tp->cur_passwd),
+			 buf, buf_len);
+	}
+	return 0;
+}
+
+
+
+/* finish connecting output flood by sending our version number and signature
+ *	to authenticate ourself */
+u_char					/* 1=ok, 0=close output stream */
+oflod_connect_fin(OFLOD_INFO *ofp)
+{
+	DCC_SRVR_ID id;
+	DCC_FNM_LNO_BUF fnm_buf;
+	const char *emsg;
+
+	ofp->oflod_alive = db_time.tv_sec;
+	ofp->flags |= (OFLOD_FG_CONNECTED | OFLOD_FG_NEW);
+	save_flod_cnts(ofp);
+
+	ofp->recv_pos = ofp->xmit_pos = ofp->cur_pos = ofp->mp->confirm_pos;
+	get_oflods_max_cur_pos();
+
+	ofp->ibuf_len = 0;
+
+	/* convince the peer we're sane by sending our version string */
+	ofp->obuf_len = sizeof(ofp->obuf.s.v);
+	memset(&ofp->obuf.s.v, 0, sizeof(ofp->obuf.s.v));
+	strcpy(ofp->obuf.s.v.body.str, version_str(ofp));
+	id = htons(my_srvr_id);
+	memcpy(ofp->obuf.s.v.body.sender_srvr_id, &id,
+	       sizeof(ofp->obuf.s.v.body.sender_srvr_id));
+
+	emsg = flod_sign(ofp, 0, &ofp->obuf.s.v, ofp->obuf_len);
+	if (emsg) {
+		rpt_err(ofp, 0, 0, "%s %d%s",
+			emsg, ofp->out_passwd_id,
+			fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		return 0;
+	}
+
+	TMSG1_FLOD(ofp, "start flood to %s", ofp_rem_str(ofp));
+
+	/* all is well, so forget old complaints */
+	if (!(ofp->mp->flags & FLODMAP_FG_OUT_SRVR)) {
+		ofp->mp->oflod_err.msg[0] = '\0';
+		ofp->mp->oflod_err.trace_msg[0] = '\0';
+	}
+
+	oflod_write(ofp);		/* send our authentication */
+
+	return 1;
+}
+
+
+
+static void
+oflod_backoff(OFLOD_INFO *ofp, u_char fast)
+{
+	FLOD_MMAP *mp;
+	int max;
+
+	mp = ofp->mp;
+	mp->otimers.retry_secs *= 2;
+	max = fast ? FLOD_SUBMAX_RETRY_SECS : FLOD_MAX_RETRY_SECS;
+	if (mp->otimers.retry_secs > max)
+		mp->otimers.retry_secs = max;
+	else if (mp->otimers.retry_secs < FLOD_RETRY_SECS)
+		mp->otimers.retry_secs = FLOD_RETRY_SECS;
+}
+
+
+
+/* start to connect an out-going flood */
+static int				/* -1=failure, 0=not yet, 1=done */
+oflod_connect_start(OFLOD_INFO *ofp, const char *syscall_name)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	int i;
+
+	ofp->mp->flags &= ~FLODMAP_FG_OUT_SRVR;
+
+	if (ofp->o_opts.flags & FLOD_OPT_SOCKS) {
+		i = Rconnect(ofp->soc, &ofp->rem_su.sa,
+			     DCC_SU_LEN(&ofp->rem_su));
+	} else {
+		/* must be NAT */
+		i = connect(ofp->soc, &ofp->rem_su.sa,
+			    DCC_SU_LEN(&ofp->rem_su));
+	}
+	if (0 > i && errno != EISCONN) {
+		if (errno == EAGAIN
+		    || errno == EINPROGRESS
+		    || errno == EALREADY) {
+			rpt_err(ofp, 1, 0, "starting flood to %s",
+				ofp_rem_str(ofp));
+			return 0;
+		}
+
+		/* Several UNIX-like systems return EINVAL for the second
+		 * connect() after a Unreachable ICMP message or timeout.
+		 * It is lame to obscure the real errno, but it is worse
+		 * to worry users and deal with their concerns. */
+		rpt_err(ofp,
+			(errno == EINVAL || errno == ECONNABORTED
+			 || errno == ECONNRESET || errno == ETIMEDOUT
+			 || errno == ECONNREFUSED),
+			0, "%s(%s%s): %s",
+			syscall_name,
+			ofp_rem_str(ofp),
+			fnm_lno(&fnm_buf, flod_path, ofp->lno),
+			errno == EINVAL
+			? "likely connection refused or local firewall"
+			: ERROR_STR());
+
+		/* do not back off connection attempts for SOCKS or NAT because
+		 * the peer cannot trigger anything by connecting to us */
+		oflod_backoff(ofp, (ofp->mp->flags & FLODMAP_FG_ACT) != 0);
+		oflod_close(ofp, 1);
+		return -1;
+	}
+
+	if (!oflod_connect_fin(ofp)) {
+		oflod_close(ofp, 1);
+		return -1;
+	}
+
+	return 1;
+}
+
+
+
+void
+oflod_open(OFLOD_INFO *ofp)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	DCC_SOCKU loc_su, su2;
+	const DCC_SOCKU *sup;
+	const SRVR_SOC *sp;
+	int error;
+
+	if (ofp->soc >= 0
+	    || ofp->rem_hostname[0] == '\0'
+	    || OFLOD_OPT_OFF_ROGUE(ofp)
+	    || flods_st != FLODS_ST_ON
+	    || (ofp->mp->flags & FLODMAP_FG_PASSIVE))
+		return;
+
+	if (!DB_IS_TIME(ofp->mp->otimers.retry, ofp->mp->otimers.retry_secs))
+		return;
+
+	if (!flod_names_resolve_start())
+		return;			/* wait for name resolution */
+
+	if (ofp->mp->rem_su.sa.sa_family == AF_UNSPEC) {
+		rpt_err(ofp, 0, 0, "peer name %s: '%s'%s",
+			ofp->rem_hostname,
+			DCC_HSTRERROR(ofp->mp->host_error),
+			fnm_lno(&fnm_buf, flod_path, ofp->lno));
+		oflod_backoff(ofp, 0);
+		oflod_close(ofp, 1);
+		return;
+	}
+	if (!LITCMP(ofp->mp->oflod_err.msg, "peer name "))
+		ofp->mp->oflod_err.msg[0] = '\0';
+
+	ofp->rem_su = ofp->mp->rem_su;
+
+	ofp->soc = socket(ofp->rem_su.sa.sa_family, SOCK_STREAM, 0);
+	if (ofp->soc < 0) {
+		rpt_err(ofp, 0, 0, "flood socket(%s): %s",
+			ofp_rem_str(ofp), ERROR_STR());
+		oflod_close(ofp, 1);
+		return;
+	}
+	++oflods.open;
+
+	if (!set_flod_socket(ofp, 0, ofp->soc,
+			     ofp->rem_hostname, &ofp->rem_su)) {
+		oflod_close(ofp, 1);
+		return;
+	}
+
+	memset(&loc_su, 0, sizeof(loc_su));
+	if (ofp->loc_hostname[0] != '\0'
+	    || ofp->loc_port != 0) {
+		/* Resolve the local host name.
+		 * This should not take significant time because
+		 * the local hostnames should be locally known.  That
+		 * implies that we don't need to use a separate thread. */
+		if (ofp->loc_hostname[0] != '\0') {
+			dcc_host_lock();
+			if (!dcc_get_host(ofp->loc_hostname,
+					  ofp->rem_su.sa.sa_family == AF_INET
+					  ? 0 : 1,
+					  &error)) {
+				dcc_error_msg("flood local name %s: %s%s",
+					      ofp->loc_hostname,
+					      DCC_HSTRERROR(error),
+					      fnm_lno(&fnm_buf, flod_path,
+						      ofp->lno));
+			} else {
+				/* match local address family to remote */
+				sup = dcc_hostaddrs;
+				for (;;) {
+					if (sup->sa.sa_family
+					    == ofp->rem_su.sa.sa_family) {
+					    loc_su = *sup;
+					    break;
+					}
+					if (++sup >= dcc_hostaddrs_end) {
+					    dcc_error_msg("family matching %s"
+							" not available for %s",
+							ofp_rem_str(ofp),
+							ofp->loc_hostname);
+					    ofp->loc_hostname[0] = '\0';
+					    break;
+					}
+				}
+			}
+			dcc_host_unlock();
+		}
+	}
+
+	/* If there is a single "-a address" other than localhost
+	 * and of the right family, then default to it.
+	 * but only if we are not trying to get past a firewall */
+	if ((ofp->mp->flags & (FLODMAP_FG_SOCKS | FLODMAP_FG_NAT)) == 0
+	    && loc_su.sa.sa_family == AF_UNSPEC) {
+		for (sp = srvr_socs; sp; sp = sp->fwd) {
+			if (dcc_ipv6sutoipv4(&su2, &sp->su)
+			    && su2.ipv4.sin_addr.s_addr == ntohl(0x7f000001))
+				continue;
+			if (sp->su.sa.sa_family != ofp->rem_su.sa.sa_family)
+				continue;
+			if (loc_su.sa.sa_family != AF_UNSPEC) {
+				/* more than one, so give up */
+				memset(&loc_su, 0, sizeof(loc_su));
+				break;
+			}
+			loc_su = sp->su;
+		}
+	}
+
+	if (loc_su.sa.sa_family != AF_UNSPEC
+	    || ofp->loc_port != 0) {
+		loc_su.sa.sa_family = ofp->rem_su.sa.sa_family;
+		*DCC_SU_PORTP(&loc_su) = ofp->loc_port;
+		if (0 > bind(ofp->soc, &loc_su.sa, DCC_SU_LEN(&loc_su)))
+			dcc_error_msg("bind(flood %s%s): %s",
+				      dcc_su2str_err(&loc_su),
+				      fnm_lno(&fnm_buf, flod_path, ofp->lno),
+				      ERROR_STR());
+	}
+
+	oflod_connect_start(ofp, "connect");
+}
+
+
+
+void
+oflod_write(OFLOD_INFO *ofp)
+{
+	int i;
+
+	if (ofp->obuf_len == 0) {
+		if (!(ofp->flags & OFLOD_FG_CONNECTED)
+		    && 0 >= oflod_connect_start(ofp, "connect2"))
+			return;
+		oflod_fill(ofp);
+		if (ofp->obuf_len == 0)
+			return;
+	}
+
+	if (ofp->o_opts.flags & FLOD_OPT_SOCKS)
+		i = Rsend(ofp->soc, &ofp->obuf.b, ofp->obuf_len, 0);
+	else
+		i = send(ofp->soc, &ofp->obuf.b, ofp->obuf_len, 0);
+	if (i > 0) {
+		ofp->obuf_len -= i;
+		if (ofp->obuf_len != 0)
+			memmove(&ofp->obuf.b[0], &ofp->obuf.b[i],
+				ofp->obuf_len);
+		ofp->oflod_alive = db_time.tv_sec;
+
+		/* fill buffer so that the main loop will
+		 * ask select() when we can send again */
+		oflod_fill(ofp);
+		return;
+	}
+
+	/* we had an error or EOF */
+	if (i < 0) {
+		/* oflod_write() is called only when select() has said that
+		 * we can send() and so we should never see the non-blocking
+		 * send() fail.
+		 * However, Solaris nevertheless sometimes says EAGAIN */
+		if (DCC_BLOCK_ERROR()) {
+			ofp->flags |= OFLOD_FG_EAGAIN;
+			TMSG2_FLOD(ofp, "pause after send(flood to %s): %s",
+				   ofp_rem_str(ofp), ERROR_STR());
+			return;
+		}
+
+		rpt_err(ofp, 0, 0, "send(flood to %s): %s",
+			ofp_rem_str(ofp), ERROR_STR());
+	} else {
+		rpt_err(ofp, 0, 0, "premature end of flood to %s",
+			ofp_rem_str(ofp));
+	}
+	oflod_read(ofp);		/* get any last error message */
+	oflod_close(ofp, 1);
+}
+
+
+/* parse end of transmission message for familiar complaints
+ *	to adjust retry timer */
+int					/* 1=try again soon, 0=ok, -1=failure */
+oflod_parse_eof(OFLOD_INFO *ofp, u_char in,
+		const DCC_FLOD_END *end, int msg_len)
+{
+	if (msg_len >= LITZ(DCC_FLOD_OK_STR)
+	    && !strncmp(end->msg, DCC_FLOD_OK_STR,
+			LITZ(DCC_FLOD_OK_STR))) {
+		return 0;		/* success */
+	}
+
+	if (msg_len >= LITZ(DCC_FLOD_BAD_AUTH_MSG)
+	    && !strncmp(end->msg, DCC_FLOD_BAD_AUTH_MSG,
+			LITZ(DCC_FLOD_BAD_AUTH_MSG))) {
+		/* try the second password if available
+		 * after the peer rejects the first */
+		if (in) {
+			if ((ofp->flags & OFLOD_FG_HAVE_2PASSWD)
+			    && !(ofp->flags & OFLOD_FG_I_USED_2PASSWD)) {
+				ofp->flags |= OFLOD_FG_I_USED_2PASSWD;
+				ofp->mp->flags |= FLODMAP_FG_USE_2PASSWD;
+				return 1;   /* try again soon */
+			}
+		} else {
+			if ((ofp->flags & OFLOD_FG_HAVE_2PASSWD)
+			    && !(ofp->flags & OFLOD_FG_O_USED_2PASSWD)) {
+				ofp->flags |= OFLOD_FG_O_USED_2PASSWD;
+				ofp->mp->flags |= FLODMAP_FG_USE_2PASSWD;
+				return 1;   /* try again soon */
+			}
+		}
+		return -1;
+	}
+
+	if (msg_len > LITZ(DCC_FLOD_BAD_VER_MSG)
+	    && !strncmp(end->msg, DCC_FLOD_BAD_VER_MSG,
+			LITZ(DCC_FLOD_BAD_VER_MSG))) {
+		/* notice if this peer demands a version
+		 * other than what we have been trying */
+		if (ofp->oversion != ofp->mp->iversion) {
+			ofp->oversion = ofp->mp->iversion;
+			return 1;	/* try again soon */
+		}
+		return -1;
+	}
+
+	return -1;
+}
+
+
+
+/* see what the target has to say about the reports we have been sending */
+void
+oflod_read(OFLOD_INFO *ofp)
+{
+	int used, req_len, recv_len;
+	DB_PTR pos;
+	int fail;
+
+again:;
+	req_len = sizeof(ofp->ibuf) - ofp->ibuf_len;
+	if (ofp->o_opts.flags & FLOD_OPT_SOCKS)
+		recv_len = Rrecv(ofp->soc, &ofp->ibuf.b[ofp->ibuf_len],
+				 req_len, 0);
+	else
+		recv_len = recv(ofp->soc, &ofp->ibuf.b[ofp->ibuf_len],
+				req_len, 0);
+	if (recv_len < 0) {
+		if (!DCC_BLOCK_ERROR()) {
+			rpt_err(ofp, 1, 0, "recv(outgoing flood %s): %s",
+				ofp_rem_str(ofp), ERROR_STR());
+			oflod_close(ofp, 1);
+		}
+		return;
+	}
+	if (recv_len > 0) {
+		/* the connection is alive and working */
+		if (!(ofp->flags & (OFLOD_FG_SHUTDOWN_REQ | OFLOD_FG_SHUTDOWN)))
+			ofp->oflod_alive = db_time.tv_sec;
+		ofp->flags &= ~OFLOD_FG_NEW;
+		if (!(ofp->flags & (OFLOD_FG_SHUTDOWN_REQ
+				    | OFLOD_FG_SHUTDOWN)))
+			ofp->oflod_alive = db_time.tv_sec;
+
+		/* limit the backoff for incoming SOCKS connection attempts
+		 * while the outgoing connection is working */
+		DB_ADJ_TIMER(&ofp->mp->itimers.retry,
+			     &ofp->mp->itimers.retry_secs,
+			     FLOD_RETRY_SECS);
+	}
+
+	ofp->ibuf_len += recv_len;
+	while (ofp->ibuf_len >= ISZ(ofp->ibuf.r.pos)) {
+		used = sizeof(ofp->ibuf.r.pos);
+
+		pos = flod_pos2db_ptr(ofp->ibuf.r.pos);
+		switch ((DCC_FLOD_POS_OPS)pos) {
+		case DCC_FLOD_POS_END:
+			/* Wait for all of the final status message or
+			 * until the target closes the TCP connection.
+			 * Do not worry if the target stops without
+			 * asking nicely, since at worst we will
+			 * resend whatever was in the pipe next time. */
+			if (ofp->ibuf_len <= ISZ(ofp->ibuf.r.end)
+			    && recv_len != 0)
+				goto again;
+			/* shut down after trying to recognize
+			 * a complaint from the target */
+			fail = oflod_parse_eof(ofp, 0,
+					       &ofp->ibuf.r.end,
+					       ofp->ibuf_len - FLOD_END_OVHD);
+			rpt_err(ofp, fail>=0, 0,
+				"outgoing flood end 'status from %s \"%.*s\"'",
+				ofp_rem_str(ofp),
+				ofp->ibuf_len - FLOD_END_OVHD,
+				ofp->ibuf.r.end.msg);
+			if (fail < 0)
+				oflod_backoff(ofp, 0);
+			oflod_close(ofp, fail<0);
+			return;
+
+		case DCC_FLOD_POS_END_REQ:
+			/* try to update our pointers and shutdown() */
+			start_shutdown(ofp);
+			ofp->mp->otimers.retry_secs = FLOD_RETRY_SECS;
+			ofp->mp->otimers.retry = (db_time.tv_sec
+						  + FLOD_RETRY_SECS);
+			if (!(ofp->mp->flags & FLODMAP_FG_PASSIVE))
+				TMSG2_FLOD(ofp, "postpone restarting flood to"
+					   " %s for %d"
+					   " seconds after end request",
+					   ofp_rem_str(ofp), FLOD_RETRY_SECS);
+			break;
+
+		case DCC_FLOD_POS_NOTE:
+			/* wait until we get the length of the complaint */
+			if (ofp->ibuf_len < FLOD_NOTE_OVHD)
+				goto again;
+			used = ofp->ibuf.r.note.len;
+			if (used > ISZ(ofp->ibuf.r.note)
+			    || used <= FLOD_NOTE_OVHD) {
+				rpt_err(ofp, 0,0,
+					"bogus outgoing flood note length"
+					" %d from %s",
+					used, ofp_rem_str(ofp));
+				oflod_close(ofp, 1);
+				return;
+			}
+			if (ofp->ibuf_len < used)
+				goto again;
+			TMSG3_FLOD(ofp, "outgoing flood note from %s: \"%.*s\"",
+				   ofp_rem_str(ofp),
+				   used-FLOD_NOTE_OVHD, ofp->ibuf.r.note.str);
+			break;
+
+		case DCC_FLOD_POS_COMPLAINT:
+			/* wait until we get the length of the complaint */
+			if (ofp->ibuf_len < FLOD_NOTE_OVHD)
+				goto again;
+			used = ofp->ibuf.r.note.len;
+			if (used > ISZ(ofp->ibuf.r.note)
+			    || used <= FLOD_NOTE_OVHD) {
+				rpt_err(ofp, 0, 0,
+					"bogus outgoing flood complaint length"
+					" %d from %s",
+					used, ofp_rem_str(ofp));
+				oflod_close(ofp, 1);
+				return;
+			}
+			if (ofp->ibuf_len < used)
+				goto again;
+			if (CK_FLOD_CNTERR(&ofp->lc.complaint))
+				flod_cnterr(&ofp->lc.complaint,
+					    "outgoing flood complaint from %s:"
+					    " %.*s",
+					    ofp_rem_str(ofp),
+					    used - FLOD_NOTE_OVHD,
+					    ofp->ibuf.r.note.str);
+			break;
+
+		case DCC_FLOD_POS_REWIND:
+			dcc_trace_msg("flood rewind request from %s",
+				      ofp_rem_str(ofp));
+			ofp->mp->flags |= FLODMAP_FG_REWINDING;
+			ofp->cur_pos = ofp->mp->confirm_pos = DB_PTR_BASE;
+			ofp->recv_pos = ofp->xmit_pos = DB_PTR_BASE;
+			ofp->rewind_pos = db_csize;
+			get_oflods_max_cur_pos();
+			oflod_fill(ofp);
+			break;
+
+		case DCC_FLOD_POS_FFWD_IN:
+			dcc_trace_msg("FFWD its input from %s",
+				      ofp_rem_str(ofp));
+			ofp->cur_pos = db_csize;
+			get_oflods_max_cur_pos();
+			break;
+
+
+		default:
+			/* The position from the peer must be one we sent,
+			 * and in the window we expect unless our
+			 * window has been broken by rewinding.
+			 * Even if our window is broken, the position must
+			 * be reasonable. */
+			if ((pos < ofp->recv_pos
+			     || pos > ofp->xmit_pos)
+			    && (!(ofp->mp->flags & FLODMAP_FG_REWINDING)
+				|| pos < DCC_FLOD_POS_MIN
+				|| pos > db_csize)) {
+				rpt_err(ofp, 0, 0,
+					"bogus confirmed flood position"
+					" "L_HPAT" from %s;"
+					" recv_pos="L_HPAT"  xmit_pos="L_HPAT,
+					pos, ofp_rem_str(ofp),
+					ofp->recv_pos, ofp->xmit_pos);
+				oflod_close(ofp, 1);
+				return;
+			}
+			ofp->recv_pos = pos;
+			if (ofp->xmit_pos == ofp->recv_pos)
+				ofp->mp->confirm_pos = ofp->cur_pos;
+			else if (ofp->mp->confirm_pos < ofp->recv_pos)
+				ofp->mp->confirm_pos = ofp->recv_pos;
+
+			/* things are going ok, so reset the connect() backoff
+			 * and the no-connection complaint */
+			ofp->mp->otimers.retry_secs = 0;
+			ofp->mp->otimers.msg_secs = FLOD_IN_COMPLAIN1;
+			ofp->mp->otimers.msg = (db_time.tv_sec
+						+ FLOD_IN_COMPLAIN1);
+			break;
+		}
+
+		ofp->ibuf_len -= used;
+		if (ofp->ibuf_len == 0)
+			return;
+		if (ofp->ibuf_len < 0)
+			dcc_logbad(EX_SOFTWARE, "ofp->ibuf_len=%d",
+				   ofp->ibuf_len);
+		/* assume there will rarely be more than one position
+		 * in the buffer */
+		memmove(ofp->ibuf.b, &ofp->ibuf.b[used], ofp->ibuf_len);
+	}
+
+	if (recv_len == 0) {
+		/* before closing, the peer is supposed to send a
+		 * "position" of DCC_FLOD_POS_END followed by
+		 * an ASCII message */
+		if (ofp->ibuf_len != 0)
+			rpt_err(ofp, 0, 0,
+				"truncated outgoing flood response from %s",
+				ofp_rem_str(ofp));
+		else
+			rpt_err(ofp, 0, 0,
+				"missing outgoing flood response from %s",
+				ofp_rem_str(ofp));
+		oflod_close(ofp, 1);
+	}
+}
+
+
+
+static void
+oflods_ck(void)
+{
+	OFLOD_INFO *ofp;
+
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		if (ofp->rem_hostname[0] == '\0')
+			break;
+
+		if (ofp->flags & OFLOD_FG_EAGAIN) {
+			TMSG1_FLOD(ofp, "resume flooding %s after EAGAIN",
+				   ofp->rem_hostname);
+			ofp->flags &= ~OFLOD_FG_EAGAIN;
+		}
+
+		if (!(ofp->flags & OFLOD_FG_CONNECTED))
+			continue;
+
+		/* close the peer has failed to respond to a shutdown request */
+		if (ofp->flags & (OFLOD_FG_SHUTDOWN_REQ | OFLOD_FG_SHUTDOWN)) {
+			if (stopint && OFP_DEAD(ofp, SHUTDOWN_DELAY)) {
+				rpt_err(ofp, 1, 0,
+					"stopping; force close flood to %s",
+					ofp_rem_str(ofp));
+				oflod_close(ofp, 0);
+
+			} else if (OFP_DEAD(ofp, KEEPALIVE_OUT_STOP)) {
+				rpt_err(ofp, 1, 0,
+					"off; force close flood to %s",
+					ofp_rem_str(ofp));
+				oflod_close(ofp, 0);
+			}
+			continue;
+		}
+
+		/* Shut down any streams that have been quiet for too long.
+		 * If the TCP connection is healthy we should at least have
+		 * received keep alive position repetitions or "are you there?"
+		 * notes from the peer. */
+		if (OFP_DEAD(ofp, KEEPALIVE_OUT)) {
+			rpt_err(ofp, 1, 0,
+				"keepalive start shutdown flood to %s",
+				ofp_rem_str(ofp));
+			start_shutdown(ofp);
+			continue;
+		}
+	}
+}
+
+
+
+static void
+oflods_stop(u_char force)
+{
+	OFLOD_INFO *ofp;
+
+	if (!flod_mmaps)
+		return;
+
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		if (ofp->rem_hostname[0] == '\0')
+			break;
+		if (ofp->soc < 0)
+			continue;
+		if (force || !(ofp->flags & OFLOD_FG_CONNECTED)) {
+			rpt_err(ofp, 1, 0, "halting flood to %s",
+				ofp_rem_str(ofp));
+			oflod_close(ofp, 0);
+		} else if (!(ofp->flags & (OFLOD_FG_SHUTDOWN_REQ
+					   | OFLOD_FG_SHUTDOWN))) {
+			rpt_err(ofp, 1, 0, "stopping flood to %s",
+				ofp_rem_str(ofp));
+			start_shutdown(ofp);
+		}
+	}
+
+	if (oflods.open == 0 && iflods.open == 0)
+		oflods_clear();
+}
+
+
+
+void
+flods_stop(const char *iflod_msg, u_char force)
+{
+	flods_st = FLODS_ST_OFF;
+	iflods_stop(iflod_msg, force);
+	oflods_stop(force);
+}
+
+
+
+/* (re)start listening for incoming floods and sending outgoing floods */
+void
+flods_restart(const char *msg, u_char force_ck)
+{
+	if (FLODS_OK())
+		flods_st = FLODS_ST_RESTART;
+	iflods_stop(msg, 0);
+	flods_ck(force_ck);
+}
+
+
+
+/* load the ids file if it has changed */
+int					/* -1=our ID missing,  0=sick file */
+check_load_ids(u_char mode)		/* 0=if needed, 1=reboot, 2=new db */
+{
+	const ID_TBL *tp;
+	int result;
+
+	result = load_ids(dcc_emsg, my_srvr_id, &tp, mode ? 1 : 0 );
+	if (result == 2)
+		return 1;
+	if (result <= 0)
+		return result;
+
+	if (mode == 0 || ( mode == 2 && db_debug))
+		dcc_trace_msg("reloaded %s", ids_path);
+
+	if (mode == 0) {
+		if (flod_mtime > 1)
+			flod_mtime = 1;
+		flods_restart("restart flooding with new IDs", 0);
+	}
+
+	return 1;
+}
+
+
+/* called periodically and at need */
+void
+flods_ck(u_char force)
+{
+	static int map_delayed;
+	IFLOD_INFO *ifp;
+	OFLOD_INFO *ofp;
+	struct stat flod_sb;
+	struct timeval;
+	DCC_TS past, present;
+	int rcd_len;
+	int work;
+	u_char loaded;			/* mapped flod.map file just for this */
+
+	if (force)			/* force hostname resolution */
+		got_hosts = 0;
+
+	for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
+		if (ifp->soc < 0)
+			continue;
+
+		/* end incoming connections that are not completed in time */
+		ofp = ifp->ofp;
+		if (!ofp) {
+			iflod_read(ifp);
+			if (ifp->soc < 0)
+				continue;
+			ofp = ifp->ofp;
+			if (!ofp) {
+				if (IFP_DEAD(ifp, KEEPALIVE_IN_STOP))
+					iflod_close(ifp, 1, 1, 0,
+						    "no authentication from %s",
+						    ifp_rem_str(ifp));
+				continue;
+			}
+		}
+
+		/* allow more complaints */
+		if (DB_IS_TIME(ofp->limit_reset, FLOD_LIM_CLEAR_SECS)
+		    || force) {
+			FLOD_LIMCNT *lc;
+
+			complained_many_iflods = 0;
+			for (lc = (FLOD_LIMCNT *)&ofp->lc;
+			     lc < (FLOD_LIMCNT *)(sizeof(ofp->lc)
+						  +(char *)&ofp->lc);
+			     ++lc) {
+				lc->lim = lc->cur;
+			}
+			ofp->limit_reset = db_time.tv_sec+FLOD_LIM_CLEAR_SECS;
+		}
+
+		if (!(ifp->flags & IFLOD_FG_VERS_CK))
+			continue;	/* done if peer not really known */
+
+		save_flod_cnts(ofp);
+
+		if (!IFP_DEAD(ifp, KEEPALIVE_IN)) {
+			/* The link is warm.
+			 * Send a delayed position update if needed. */
+			iflod_send_pos(ifp, 0);
+
+		} else if (ifp->flags & IFLOD_FG_END_REQ) {
+			/* The link is cold. If we have asked the peer to
+			 * stop but it has not, then break the link. */
+			iflod_close(ifp, 1, 0, 0, "%s ignored close request",
+				    ifp_rem_str(ifp));
+
+		} else {
+			/* The link is cold., so repeat our position or
+			 * send a note as a keepalive.  The will be closed if
+			 * that fails. */
+			iflod_send_pos(ifp, 1);
+		}
+	}
+
+	if (FLODS_OK()) {
+		/* stop and restart the pumps if the list of peers has
+		 * changed or if our map has disappeared
+		 * and if dbclean is not running */
+		if (0 > stat(flod_path, &flod_sb)) {
+			if (errno != ENOENT
+			    && flod_mtime != 0)
+				dcc_error_msg("stat(%s): %s",
+					      flod_path, ERROR_STR());
+			flod_sb.st_mtime = 0;
+		}
+		if (flod_mtime != 0
+		    && 0 > access(flod_mmap_path, W_OK | R_OK)) {
+			if (errno != ENOENT)
+				dcc_error_msg("access(%s): %s",
+					      flod_mmap_path, ERROR_STR());
+			flod_sb.st_mtime = 0;
+		}
+		if (flods_st != FLODS_ST_RESTART
+		    && flod_sb.st_mtime != flod_mtime) {
+			if (flod_mtime > 1) {
+				dcc_trace_msg("%s has changed", flod_path);
+				flod_mtime = 0;
+			}
+			flods_st = FLODS_ST_RESTART;
+		}
+	}
+
+	if (flods_st != FLODS_ST_ON) {
+		flods_stop("", 0);
+
+		/* wait until the previous floods have stopped and dbclean
+		 * is not running to restart flooding */
+		if (FLODS_OK()) {
+			if (oflods.open != 0 || iflods.open != 0
+			    || !flod_names_resolve_ck()) {
+				flods_st = FLODS_ST_RESTART;
+				/* check again soon but not immediately */
+				RUSH_NEXT_FLODS_CK();
+			} else {
+				if (load_flod(1))
+					flods_st = FLODS_ST_ON;
+			}
+		}
+	}
+
+	/* try to reap the hostname resolving child */
+	flod_names_resolve_ck();
+
+	/* that is all we can do if flooding is off or dbclean is running */
+	if (!FLODS_OK_ON()) {
+		oflods_ck();
+		return;
+	}
+
+	iflods_listen();
+
+	/* generate summaries of some of our delayed reports */
+	dcc_timeval2ts(&past, &db_time, -summarize_delay_secs);
+	dcc_timeval2ts(&present, &db_time, 0);
+	if (flod_mmaps) {
+		if (flod_mmaps->delay_pos > db_csize
+		    || flod_mmaps->delay_pos < DB_PTR_BASE)
+			flod_mmaps->delay_pos = DB_PTR_BASE;
+		work = 0;
+		while (flod_mmaps->delay_pos < db_csize) {
+			if (!db_map_rcd(0, &db_sts.sumrcd,
+					flod_mmaps->delay_pos,
+					&rcd_len)) {
+				flod_mmaps->delay_pos = db_csize;
+				break;
+			}
+			/* only our own reports are delayed */
+			if (DB_RCD_DELAY(db_sts.sumrcd.d.r)) {
+				/* wait until it is time */
+				if (dcc_ts_newer_ts(&db_sts.sumrcd.d.r->ts,
+						    &past)
+				    && !dcc_ts_newer_ts(&db_sts.sumrcd.d.r->ts,
+							&present))
+					break;
+				if (!summarize_dly()) {
+					flod_mmaps->delay_pos = db_csize;
+					break;
+				}
+			}
+			flod_mmaps->delay_pos += rcd_len;
+
+			if (++work >= 1000) {
+				/* spend at most 0.5 second at this
+				 * and then let other processes run*/
+				gettimeofday(&db_time, 0);
+				if (tv_diff2us(&db_time, &wake_time)>DCC_US/2) {
+					next_flods_ck = 0;
+					break;
+				}
+				work = 0;
+			}
+		}
+
+		/* prime the outgoing pumps */
+		for (ofp = oflods.infos;
+		     ofp <= LAST(oflods.infos);
+		     ++ofp) {
+			if (ofp->rem_hostname[0] == '\0')
+				break;
+
+			if (ofp->soc >= 0) {
+				/* The connection is no longer new if it has
+				 * been a while since it was completed */
+				if ((ofp->flags & OFLOD_FG_NEW)
+				    && DB_IS_TIME(ofp->mp->cnts.out_conn_changed
+						  + FLODS_CK_SECS,
+						  FLODS_CK_SECS))
+					ofp->flags &= ~OFLOD_FG_NEW;
+				oflod_fill(ofp);
+			} else {
+				oflod_open(ofp);
+			}
+
+			iflod_socks_start(ofp);
+		}
+	}
+
+	/* complain once per day about incoming links that are not working
+	 * even if dbclean is continually running. */
+	loaded = 0;
+	if (flod_mmaps) {
+		map_delayed = 0;
+	} else if ((force || ++map_delayed > 10) && load_flod(0)) {
+		loaded = 1;
+		map_delayed = 0;
+	}
+	for (ofp = oflods.infos;
+	     ofp->rem_hostname[0] != '\0' && ofp <= LAST(oflods.infos)
+	     && flod_mmaps;
+	     ++ofp) {
+		FLOD_MMAP *mp;
+		LAST_ERROR *ep;
+		const char *msg;
+
+		if (force) {
+			/* Force new outgoing connection attempts.  Also force
+			 * incoming error messages soon but not now to give new
+			 * connetions a chance to be triggered by outgoing
+			 * connections.  */
+			flod_try_again(ofp);
+		}
+
+		mp = ofp->mp;
+		if (ofp->soc < 0
+		    && (ofp->mp->flags & FLODMAP_FG_PASSIVE)
+		    && !OFLOD_OPT_OFF_ROGUE(ofp)
+		    && DB_IS_TIME(mp->otimers.msg, mp->otimers.msg_secs)) {
+			ep = &mp->oflod_err;
+			msg = (ep->complained
+			       ? ""
+			       : ep->msg[0] != '\0'
+			       ? ep->msg
+			       : ep->trace_msg);
+			dcc_error_msg("no passive connection to %s%s%s%s",
+				      ofp->rem_hostname,
+				      msg[0] ? ": \"" : "",
+				      msg,
+				      msg[0] ? "\"" : "");
+			ep->complained = 1;
+			mp->otimers.msg_secs = FLOD_IN_COMPLAIN;
+			mp->otimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN;
+		}
+
+		if ((ofp->mp->flags & (FLODMAP_FG_SOCKS | FLODMAP_FG_NAT)) == 0
+		    && !ofp->ifp
+		    && !IFLOD_OPT_OFF_ROGUE(ofp)
+		    && DB_IS_TIME(mp->itimers.msg, mp->itimers.msg_secs)) {
+			ep = &mp->iflod_err;
+			msg = (ep->complained
+			       ? ""
+			       : ep->msg[0] != '\0'
+			       ? ep->msg
+			       : ep->trace_msg);
+			dcc_error_msg("no incoming connection from %s%s%s%s",
+				      ofp->rem_hostname,
+				      msg[0] ? ": \"" : "",
+				      msg,
+				      msg[0] ? "\"" : "");
+			ep->complained = 1;
+			mp->itimers.msg_secs = FLOD_IN_COMPLAIN;
+			mp->itimers.msg = db_time.tv_sec + FLOD_IN_COMPLAIN;
+		}
+	}
+	if (loaded)
+		oflods_clear();
+
+	oflods_ck();
+}
+
+
+
+void
+flods_init(void)
+{
+	IFLOD_INFO *ifp;
+
+	for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp)
+		ifp->soc = -1;
+	oflods_clear();
+
+	flods_restart("", 1);
+}
diff -r 000000000000 -r c7f6b056b673 dccd/rl.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd/rl.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,1700 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * server rate limiting
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.142 $Revision$
+ */
+
+#include "dccd_defs.h"
+
+
+RL_RATE rl_sub_rate;
+RL_RATE rl_anon_rate;
+RL_RATE rl_all_anon_rate;
+RL_RATE rl_bugs_rate;
+#ifdef RL_MIN_MAX
+static int rl_min_max = RL_MIN_MAX;
+#else
+static int rl_min_max = 0;
+#endif
+
+static RL rl_all_anon;			/* limit all or anonymous clients */
+
+static RL *rl_newest, *rl_oldest;
+static RL **rl_hash;
+static int rl_hash_len;
+static u_char rl_too_many;
+
+time_t clients_cleared;
+
+time_t need_clients_save;
+
+typedef struct ip_bl {
+    struct ip_bl *fwd;
+    struct in6_addr addr;
+    struct in6_addr mask;
+    RL_DATA_FG  flags;			/* subset of RL_FG_* */
+#    define  IP_BL_ADDR	    RL_FG_BL_ADDR
+#    define  IP_BL_TRACE    RL_FG_TRACE
+} IP_BL;
+static IP_BL *ip_bl;
+
+static u_char rl_get(RL **, DCC_CLNT_ID, const struct in6_addr *);
+
+
+
+/* See if an IP address is evil
+ *	The blacklist is a simple linear list.  Entries from the file
+ *	are added to the front of the file, so that the last matching
+ *	entry determines the result.
+ *  This should be sped up if there are ever more than a very few entries */
+u_char					/* 1=blacklisted */
+ck_ip_bl(RL **rlp, DCC_CLNT_ID clnt_id, const struct in6_addr *pap)
+{
+	const IP_BL *bl;
+	RL *rl;
+
+	for (bl = ip_bl; bl; bl = bl->fwd) {
+		if (DCC_IN_BLOCK(*pap, bl->addr, bl->mask)) {
+			/* all blacklisted addresses including flooding peers
+			 * should have rate limit blocks so that they are
+			 * noticed */
+			rl = *rlp;
+			if (!rl) {
+				rl_get(rlp, clnt_id, pap);
+				rl = *rlp;
+			}
+			rl->d.flags &= ~(RL_FG_BL_ADDR | RL_FG_TRACE);
+			rl->d.flags |= bl->flags;
+			rl->d.flags |= RL_FG_CK_BL;
+			return (rl->d.flags & RL_FG_BL_ADDR) != 0;
+		}
+	}
+
+	rl = *rlp;
+	if (rl) {
+		rl->d.flags &= ~(RL_FG_BL_ADDR | RL_FG_TRACE);
+		rl->d.flags |= RL_FG_CK_BL;
+	}
+	return 0;
+}
+
+
+
+static void
+clear_bl(void)
+{
+	IP_BL *bl;
+	RL *rl;
+
+	while ((bl = ip_bl) != 0) {
+		ip_bl = ip_bl->fwd;
+		dcc_free(bl);
+	}
+
+	for (rl = rl_newest; rl != 0; rl = rl->older) {
+		rl->d.flags &= ~RL_FG_CK_BL;
+	}
+}
+
+
+
+void
+check_blacklist_file(void)
+{
+#define BL_NM "blacklist"
+	DCC_FNM_LNO_BUF fnm_buf;
+	static time_t prev_mtime, next_msg;
+#define BL_RECHECK (2*60*60)
+	static int serrno;
+	struct stat sb;
+	FILE *f;
+	char buf[120];
+	IP_BL *bl;
+	struct in6_addr addr, mask;
+	u_char flags;
+	char *p;
+	int lno, entries, i;
+
+	/* see if the file has changed */
+	if (0 > stat(BL_NM, &sb)) {
+		if (errno != ENOENT) {
+			prev_mtime = 1;
+			if (serrno != errno
+			    || DB_IS_TIME(next_msg, BL_RECHECK)) {
+				serrno = errno;
+				next_msg = db_time.tv_sec + BL_RECHECK;
+				dcc_error_msg("stat(%s): %s",
+					      DB_NM2PATH_ERR(BL_NM),
+					      ERROR_STR());
+			}
+
+		} else if (prev_mtime != 0) {
+			prev_mtime = 0;
+			next_msg = 0;
+			dcc_error_msg("%s disappeared", DB_NM2PATH_ERR(BL_NM));
+		}
+		clear_bl();
+		return;
+	}
+	if (prev_mtime == sb.st_mtime
+	    && (next_msg == 0 || !DB_IS_TIME(next_msg, BL_RECHECK)))
+		return;
+
+	/* the file has changed, so parse it */
+	clear_bl();
+	prev_mtime = 1;
+	next_msg = 0;
+	f = fopen(BL_NM, "r");
+	if (!f) {
+		if (serrno != errno) {
+			serrno = errno;
+			next_msg = db_time.tv_sec + BL_RECHECK;
+			dcc_error_msg("fopen(%s): %s",
+				      DB_NM2PATH_ERR(BL_NM), ERROR_STR());
+		}
+		return;
+	}
+	if (0 > fstat(fileno(f), &sb)) {
+		if (serrno != errno) {
+			serrno = errno;
+			next_msg = db_time.tv_sec + BL_RECHECK;
+			dcc_error_msg("fstat(%s): %s",
+				      DB_NM2PATH_ERR(BL_NM), ERROR_STR());
+		}
+		return;
+	}
+	prev_mtime = sb.st_mtime;
+
+	entries = 0;
+	for (lno = 1; ; ++lno) {
+		if (!fgets(buf, sizeof(buf), f)) {
+			if (ferror(f) && serrno != errno) {
+				serrno = errno;
+				next_msg = db_time.tv_sec + BL_RECHECK;
+				dcc_error_msg("fgets(%s): %s",
+					      DB_NM2PATH_ERR(BL_NM),
+					      ERROR_STR());
+			}
+			break;
+		}
+		/* reject lines that are too long */
+		i = strlen(buf);
+		if (buf[i-1] != '\n') {
+			next_msg = db_time.tv_sec + BL_RECHECK;
+			dcc_error_msg("syntax error%s",
+				      fnm_lno(&fnm_buf,
+					      DB_NM2PATH_ERR(BL_NM), lno));
+			break;
+		}
+
+		/* ignore leading blanks, comments, and blank lines */
+		p = strchr(buf, '#');
+		if (p)
+			*p = '\0';
+		p = buf;
+		p += strspn(p, DCC_WHITESPACE);
+		if (*p == '\0')
+			continue;
+
+		flags = IP_BL_ADDR;
+		for (;;) {
+			i = strspn(p, " \t");
+			if (i != 0) {
+				p += i;
+				continue;
+			}
+			if (!CLITCMP(p, "trace")) {
+				p += LITZ("trace");
+				if (*p == ',')
+					++p;
+				flags |= IP_BL_TRACE;
+				continue;
+			}
+			if (!CLITCMP(p, "ok")) {
+				p += LITZ("ok");
+				if (*p == ',')
+					++p;
+				flags &= ~IP_BL_ADDR;
+				continue;
+			}
+			if (!CLITCMP(p, "bad")) {
+				p += LITZ("bad");
+				if (*p == ',')
+					++p;
+				flags |= IP_BL_ADDR;
+				continue;
+			}
+			break;
+		}
+
+		i = dcc_str2cidr(dcc_emsg, &addr, &mask, 0, p, BL_NM, lno);
+		if (i <= 0) {
+			if (i < 0)
+				dcc_error_msg("%s", dcc_emsg);
+			else
+				dcc_error_msg("syntax error%s",
+					      fnm_lno(&fnm_buf,
+						      DB_NM2PATH_ERR(BL_NM),
+						      lno));
+			next_msg = db_time.tv_sec + BL_RECHECK;
+			continue;
+		}
+
+		bl = dcc_malloc(sizeof(*bl));
+		bl->addr = addr;
+		bl->mask = mask;
+		bl->flags = flags;
+		bl->fwd = ip_bl;
+		ip_bl = bl;
+		if (++entries > 100) {
+			dcc_error_msg("too many entries in %s",
+				      DB_NM2PATH_ERR(BL_NM));
+			next_msg = db_time.tv_sec + BL_RECHECK;
+			break;
+		}
+	}
+	fclose(f);
+
+	if (entries)
+		dcc_trace_msg("read %d entries from %s",
+			      entries, DB_NM2PATH_ERR(BL_NM));
+
+#undef BL_NM
+#undef BL_RECHECK
+}
+
+
+
+static inline RL **
+rl_hash_fnc(DCC_CLNT_ID	clnt_id, const struct in6_addr *addr)
+{
+	u_int32_t sum;
+
+	sum = clnt_id;
+	sum += addr->s6_addr32[0];
+	sum += addr->s6_addr32[1];
+	sum += addr->s6_addr32[2];
+	sum += addr->s6_addr32[3];
+	return &rl_hash[mhash(sum, rl_hash_len)];
+}
+
+
+
+/* link a rate limit block into its hash bin */
+static inline void
+rl_hash_link(RL *rl,			/* new block */
+	     RL **bin)			/* its bin */
+{
+	RL *rl2;
+
+	rl->bin = bin;
+	rl2 = *bin;
+	rl->hfwd = rl2;
+	if (rl2)
+		rl2->hbak = rl;
+	*bin = rl;
+}
+
+
+
+static int
+set_rl_min_max(void)
+{
+	int i;
+
+	if (rl_min_max == 0) {
+		/* allow RL_MIN_MAX_DEF for 512 MByte
+		 *	RL_MIN_MAX_DEF*2 for 1 GByte
+		 *	...
+		 *	RL_MIN_MAX_DEF*16 for 8 GByte
+		 */
+		i = (db_max_rss + 512*1024*1024-1) / (512*1024*1024);
+		if (i <= 0)
+			i = 1;
+		rl_min_max = RL_MIN_MAX_DEF * i;
+	}
+	if (rl_min_max > RL_MIN_MAX_MAX)
+		rl_min_max = RL_MIN_MAX_MAX;
+
+	return rl_min_max;
+}
+
+
+
+static void
+rl_expand(int new_len)
+{
+	RL *rl;
+	int j;
+
+	/* create a bunch of rate limit blocks */
+	if (new_len < 64)
+		new_len = 64;
+
+	if (rl_hash_len+new_len > set_rl_min_max()) {
+		if (rl_hash_len > rl_min_max)
+			dcc_logbad(EX_SOFTWARE, "impossible # of RL blocks");
+		new_len = rl_min_max - rl_hash_len;
+	}
+
+	if (rl_hash_len != 0)
+		dcc_trace_msg("increase from %d to %d RL blocks",
+			      rl_hash_len, rl_hash_len+new_len);
+
+	rl = dcc_malloc(new_len*sizeof(*rl));
+	if (!rl)
+		dcc_logbad(EX_OSERR, "malloc(%d RL's) failed", new_len);
+	memset(rl, 0, new_len*sizeof(*rl));
+	j = 0;
+	if (!rl_oldest) {
+		rl_oldest = rl;
+		rl_newest = rl;
+		++rl;
+		++j;
+	}
+	while (j < new_len) {	/* make the new blocks oldest */
+		rl_oldest->older = rl;
+		rl->newer = rl_oldest;
+		rl_oldest = rl;
+		 ++rl;
+		 ++j;
+	}
+
+	/* Rebuild and expand the hash table.
+	 *	The hash table is an array of pointers to rate limit blocks. */
+	if (rl_hash)
+		dcc_free(rl_hash);
+	rl_hash_len += new_len;
+	rl_hash = dcc_malloc(rl_hash_len*sizeof(*rl_hash));
+	memset(rl_hash, 0, rl_hash_len*sizeof(RL *));
+
+	/* copy the active blocks to the new hash table */
+	for (rl = rl_newest; rl; rl = rl->older) {
+		if (!rl->bin)
+			continue;
+		rl->hbak = 0;
+		rl_hash_link(rl,
+			     rl_hash_fnc(rl->d.clnt_id, &rl->d.clnt_addr));
+	}
+}
+
+
+
+/* age a rate limit */
+static void
+rl_age(RL *rl, const RL_RATE *credits)
+{
+	time_t secs;
+
+	secs = (db_time.tv_sec - rl->d.last_used);
+
+	/* only prevent overflow if no time has passed */
+	if (secs <= 0) {
+		if (rl->d.request_credits < credits->lo)
+			rl->d.request_credits = credits->lo;
+		if (rl->d.bug_credits < rl_bugs_rate.lo)
+			rl->d.bug_credits = rl_bugs_rate.lo;
+	}
+
+	rl->d.last_used = db_time.tv_sec;
+
+	/* reset idle counters */
+	if (secs >= RL_AVG_SECS || secs < 0) {
+		rl->d.bug_credits = rl_bugs_rate.hi;
+		rl->d.request_credits = credits->hi;
+		return;
+	}
+
+	rl->d.request_credits += secs * credits->per_sec;
+	if (rl->d.request_credits > credits->hi)
+		rl->d.request_credits = credits->hi;
+
+	rl->d.bug_credits += secs * rl_bugs_rate.per_sec;
+	if (rl->d.bug_credits > rl_bugs_rate.hi)
+		rl->d.bug_credits = rl_bugs_rate.hi;
+}
+
+
+
+/* age and increment a rate limit */
+void
+rl_inc(RL *rl, const RL_RATE *credits)
+{
+	rl_age(rl, credits);
+	++rl->d.requests;		/* increase total count of requests */
+	rl->d.request_credits -= RL_SCALE;  /* charge for this request */
+}
+
+
+
+/* update the request rate */
+static void
+rl_avg_requests_age(RL *rl, u_char force)
+{
+	time_t secs;
+	double trim;
+
+	if (rl->d.requests_avg_aged > db_time.tv_sec) {
+		/* clear things if time has jumped backwards */
+		rl->d.requests_avg_start = 0;
+
+	} else if (rl->d.requests_avg_aged + RL_AVG_UPDATE > db_time.tv_sec
+		   && !force) {
+		/* do nothing if it is not yet time and we are not forced */
+		return;
+	}
+
+	secs = db_time.tv_sec - rl->d.requests_avg_start;
+	if (secs > RL_AVG_TERM*2) {
+		/* clear if too long since the average was updated */
+		rl->d.requests_old = rl->d.requests;
+		rl->d.requests_avg_total = 0;
+		rl->d.nops_old = rl->d.nops;
+		rl->d.nops_avg_total = 0;
+		rl->d.requests_avg_start = db_time.tv_sec;
+		trim = 1.0;
+
+	} else if (secs < 24*60*60) {
+		/* we have no average for the first day */
+		trim = 1.0;
+
+	} else if (secs <= RL_AVG_TERM) {
+		trim = (60*60*24 * 1.0) / secs;
+
+	} else {
+		/* trim old counts if we have been averaging long enough */
+		trim = secs - RL_AVG_TERM;
+		trim /= (RL_AVG_TERM*2);
+		rl->d.requests_avg_total -= (rl->d.requests_avg_total * trim);
+		rl->d.nops_avg_total -= (rl->d.nops_avg_total * trim);
+		rl->d.requests_avg_start = db_time.tv_sec - RL_AVG_TERM;
+		trim = (60*60*24 * 1.0) / RL_AVG_TERM;
+	}
+
+	rl->d.requests_avg_total += rl->d.requests - rl->d.requests_old;
+	rl->d.requests_old = rl->d.requests;
+	rl->d.nops_avg_total += rl->d.nops - rl->d.nops_old;
+	rl->d.nops_old = rl->d.nops;
+
+	rl->d.requests_avg = rl->d.requests_avg_total * trim;
+	rl->d.nops_avg = rl->d.nops_avg_total * trim;
+
+	rl->d.requests_avg_aged = db_time.tv_sec;
+}
+
+
+
+static void
+rl_unref(RL *rl)
+{
+	if (rl->newer) {
+		rl->newer->older = rl->older;
+	} else if (rl_newest == rl) {
+		rl_newest = rl->older;
+	}
+
+	if (rl->older) {
+		rl->older->newer = rl->newer;
+	} else if (rl_oldest == rl) {
+		rl_oldest = rl->newer;
+	}
+
+	rl->newer = 0;
+	rl->older = 0;
+}
+
+
+
+static void
+rl_ref(RL *rl)
+{
+	RL **bin, *bin_head;
+
+	if (rl_newest == rl)
+		return;
+
+	/* make it the most recently used block */
+	rl_unref(rl);
+	rl->older = rl_newest;
+	rl_newest->newer = rl;
+	rl_newest = rl;
+
+	/* move it to the head of its hash bin if it is not already there */
+	bin = rl->bin;
+	bin_head = *bin;
+	if (bin_head == rl)
+		return;
+	*bin = rl;
+	if (rl->hfwd)
+		rl->hfwd->hbak = rl->hbak;
+	rl->hbak->hfwd = rl->hfwd;	/* we know there is a predecessor */
+	rl->hbak = 0;
+	rl->hfwd = bin_head;
+	bin_head->hbak = rl;
+}
+
+
+
+/* get a free rate limit block, recycling the oldest block if necessary */
+static RL *				/* block to use or 0 to make more */
+rl_get_free(void)
+{
+	RL *rl, *rl2;
+	time_t stale;
+
+	for (rl = rl_oldest; rl != 0; rl = rl->newer) {
+		if (rl->ref_cnt)
+			continue;
+
+		/* Found oldest free block */
+		if (rl->d.last_used != 0) {
+			/* oldest free block has been used and so we
+			 * must recycle something */
+			stale = db_time.tv_sec;
+			/* keep many blocks until we get enough to
+			 * worry about a denial of service attack */
+			if (rl_hash_len < set_rl_min_max())
+				stale -= CLIENTS_SAVE_AGE;
+			else
+				stale -= RL_LIFE_SECS;
+			/* make more if the oldest is new
+			 * and we don't have too many */
+			if (rl->d.last_used >= stale
+			    && rl_hash_len < rl_min_max)
+				return 0;
+
+			/* We cannot make more because we have too many blocks.
+			 *
+			 * Notice if we are about to recycle a block that is
+			 * not obsolete.
+			 * Try to find an old, little used block, so that we
+			 * do not recycle a block used by a busy client that
+			 * has only paused */
+			if (rl->d.last_used > clients_cleared) {
+				rl_too_many = 1;
+				stale = db_time.tv_sec - CLIENTS_AGE/2;
+				for (rl2 = rl;
+				     rl2 && rl2->d.last_used <= stale;
+				     rl2 = rl2->newer) {
+					/* avoid forgeting bad guys */
+					if (rl->d.flags & RL_FG_BLS)
+					    continue;
+					/* take the first tiny client found */
+					if (RL_REQUESTS_AVG(&rl2->d) < 100) {
+					    rl = rl2;
+					    break;
+					}
+				}
+			}
+		}
+
+		/* Recycle a block by first removing it from its hash chain */
+		if (rl->hfwd)
+			rl->hfwd->hbak = rl->hbak;
+		if (rl->hbak)
+			rl->hbak->hfwd = rl->hfwd;
+		else if (rl->bin)	/* It will not be on a hash chain */
+			*rl->bin = rl->hfwd;	/* if it has never been used */
+		rl_unref(rl);
+		memset(rl, 0, sizeof(*rl));
+		rl_avg_requests_age(rl, 1);
+		return rl;
+	}
+
+	/* There are no free blocks that are old enough to recycle,
+	 * so tell the caller to make more */
+	return 0;
+}
+
+
+
+/* get a rate limit block based on the IP address of the sender */
+static u_char				/* 1=existing block */
+rl_get(RL **rlp, DCC_CLNT_ID clnt_id, const struct in6_addr *cap)
+{
+	RL *rl, **bin;
+
+	if (!rl_hash_len)
+		rl_expand(queue_max);
+
+	bin = rl_hash_fnc(clnt_id, cap);
+	for (rl = *bin; rl; rl = rl->hfwd) {
+		if (rl->d.clnt_id != clnt_id
+		    || memcmp(&rl->d.clnt_addr, cap, sizeof(rl->d.clnt_addr)))
+			continue;
+		rl_ref(rl);		/* found it, so make it newest */
+		rl_avg_requests_age(rl, 0);
+		*rlp = rl;
+		return 1;
+	}
+
+	rl = rl_get_free();
+	if (!rl) {
+		/* when we are out of rate limiting blocks, make more */
+		rl_expand(queue_max);
+		/* which makes a new rate limit hash table which makes our
+		 * pointer into the old hash table bogus */
+		rl = rl_get_free();
+		if (!rl)
+			dcc_logbad(EX_SOFTWARE, "no available RL blocks");
+		bin = rl_hash_fnc(clnt_id, cap);
+	}
+	rl_hash_link(rl, bin);
+	rl_ref(rl);
+	rl->d.clnt_addr = *cap;
+	rl->d.clnt_id = clnt_id;
+	*rlp = rl;
+	return 0;
+}
+
+
+
+/* get a rate limit block for a job */
+static RL *
+rl_get_q(QUEUE *q, DCC_CLNT_ID id)
+{
+	struct in6_addr clnt_addr;
+	const struct in6_addr *cap;
+	RL *rl;
+
+	if (q->clnt_su.sa.sa_family == AF_INET6) {
+		cap = &q->clnt_su.ipv6.sin6_addr;
+	} else {
+		dcc_ipv4toipv6(&clnt_addr, q->clnt_su.ipv4.sin_addr);
+		cap = &clnt_addr;
+	}
+	rl_get(&q->rl, id, cap);
+	rl = q->rl;
+	++rl->ref_cnt;
+
+	return rl;
+}
+
+
+
+static u_char
+clients_write(FILE *f, const void *buf, int buf_len)
+{
+	if (1 == fwrite(buf, buf_len, 1, f))
+		return 1;
+	dcc_error_msg("fwrite(%s): %s",
+		      DB_NM2PATH_ERR(CLIENTS_NM()), ERROR_STR());
+	fclose(f);
+	return 0;
+}
+
+
+
+/* dump the rate limit blocks into a file */
+void
+clients_save(void)
+{
+	int fd;
+	FILE *f;
+	CLIENTS_HEADER header;
+	RL *rl;
+
+	need_clients_save = db_time.tv_sec + CLIENTS_SAVE_SECS;
+
+	/* prevent evil games with symbolic links */
+	unlink(CLIENTS_NM());
+	fd = open(CLIENTS_NM(), O_WRONLY|O_CREAT|O_EXCL, 0644);
+	if (fd < 0) {
+		dcc_error_msg("open(%s): %s",
+			      DB_NM2PATH_ERR(CLIENTS_NM()), ERROR_STR());
+		return;
+	}
+	f = fdopen(fd, "w");
+
+	memset(&header, 0, sizeof(header));
+	BUFCPY(header.magic, CLIENTS_MAGIC(grey_on));
+	header.now = db_time.tv_sec;
+	header.cleared = clients_cleared;
+	if (anon_off) {
+		header.anon_delay_us = DCC_ANON_DELAY_FOREVER;
+	} else {
+		header.anon_delay_us = anon_delay_us;
+		header.anon_delay_inflate = anon_delay_inflate;
+	}
+	if (clients_cleared > db_time.tv_sec)   /* fix time jump */
+		clients_cleared = db_time.tv_sec;
+
+	for (rl = rl_oldest; rl != 0; rl = rl->newer) {
+		if (rl->d.last_used == 0)
+			continue;
+		if (rl->d.last_used > db_time.tv_sec)   /* fix time jump */
+			rl->d.last_used = db_time.tv_sec;
+		++header.hash_len;
+	}
+	if (!clients_write(f, &header, sizeof(header)))
+		return;
+
+	for (rl = rl_oldest; rl != 0; rl = rl->newer) {
+		if (rl->d.last_used == 0)
+			continue;
+		if (!clients_write(f, &rl->d, sizeof(rl->d)))
+			return;
+	}
+	fclose(f);
+}
+
+
+
+static u_char
+clients_read(FILE *f, void *buf, int buf_len)
+{
+	int i;
+
+	i = fread(buf, buf_len, 1, f);
+	if (i == 1)
+		return 1;
+
+	if (feof(f))
+		return 0;
+
+	dcc_error_msg("fread(%s): %s",
+		      DB_NM2PATH_ERR(CLIENTS_NM()), ERROR_STR());
+	fclose(f);
+	return 0;
+}
+
+
+
+/* load the rate limit blocks from a previous instance */
+void
+clients_load(void)
+{
+	struct stat sb;
+	int fd;
+	FILE *f;
+	CLIENTS_HEADER header;
+	RL_DATA data;
+	RL *rl;
+	int rl_size, total, used, anon;
+#	define BAD_FILE() ((0 > rename(CLIENTS_NM(), BAD_CLIENTS_NM()))	    \
+			   ? unlink(CLIENTS_NM()) : 0)
+
+	clients_cleared = db_time.tv_sec;
+
+	fd = open(CLIENTS_NM(), O_RDONLY, 0);
+	if (fd < 0) {
+		if (errno != ENOENT)
+			dcc_error_msg("open(%s): %s",
+				      DB_NM2PATH_ERR(CLIENTS_NM()),
+				      ERROR_STR());
+		else if (db_debug)
+			dcc_trace_msg("open(%s): %s",
+				      DB_NM2PATH_ERR(CLIENTS_NM()),
+				      ERROR_STR());
+		BAD_FILE();
+		return;
+	}
+	f = fdopen(fd, "r");
+	if (0 > fstat(fd, &sb)) {
+		dcc_error_msg("stat(%s): %s",
+			      DB_NM2PATH_ERR(CLIENTS_NM()), ERROR_STR());
+		BAD_FILE();
+		fclose(f);
+		return;
+	}
+	if ((int)sb.st_size < ISZ(header)) {
+		dcc_error_msg("%s has invalid size %d",
+			      DB_NM2PATH_ERR(CLIENTS_NM()), (int)sb.st_size);
+		BAD_FILE();
+		fclose(f);
+		return;
+	}
+
+	if (!clients_read(f, &header, sizeof(header)))
+		return;
+
+	/* try to save strange files but quietly ignore old files */
+	if (strcmp(header.magic, CLIENTS_MAGIC(grey_on))) {
+		if (strncmp(header.magic, CLIENTS_MAGIC_BASE(grey_on),
+			    strlen(CLIENTS_MAGIC_BASE(grey_on)))) {
+			dcc_error_msg("unrecognized magic in %s",
+				      DB_NM2PATH_ERR(CLIENTS_NM()));
+			BAD_FILE();
+		}
+		fclose(f);
+		return;
+	}
+
+	if (((sb.st_size - ISZ(header)) % ISZ(RL_DATA)) != 0) {
+		dcc_error_msg("%s has invalid size %d",
+			      DB_NM2PATH_ERR(CLIENTS_NM()), (int)sb.st_size);
+		BAD_FILE();
+		fclose(f);
+		return;
+	}
+
+	if (header.hash_len > RL_MIN_MAX_MAX) {
+		dcc_trace_msg("unrecognized hash_len=%d in %s",
+			      header.hash_len, DB_NM2PATH_ERR(CLIENTS_NM()));
+		fclose(f);
+		return;
+	}
+	if (header.cleared > db_time.tv_sec) {
+		dcc_trace_msg("bad time %d in %s",
+			      (int)header.cleared,
+			      DB_NM2PATH_ERR(CLIENTS_NM()));
+		BAD_FILE();
+		fclose(f);
+		return;
+	}
+	clients_cleared = header.cleared;
+	rl_size = header.hash_len;
+	rl_size += queue_max - (rl_size % queue_max);
+	rl_size = min(rl_size, set_rl_min_max());
+	rl_expand(rl_size);
+
+	for (total = 0, used = 0, anon = 0; ; ++total) {
+		if (!clients_read(f, &data, sizeof(data)))
+			break;
+		if (data.last_used > db_time.tv_sec) {
+			dcc_error_msg("badly timestamped entry in %s",
+				      DB_NM2PATH_ERR(CLIENTS_NM()));
+			break;
+		}
+
+		if (rl_get(&rl, data.clnt_id, &data.clnt_addr)) {
+			dcc_error_msg("duplicate entry in %s",
+				      DB_NM2PATH_ERR(CLIENTS_NM()));
+			break;
+		}
+
+		rl->d = data;
+		rl->d.flags &= ~RL_FG_CK_BL;	/* check current blacklist */
+		++used;
+		if ((rl->d.flags & RL_FG_ANON)
+		    && rl->d.last_used >= db_time.tv_sec-2*24*60*60)
+			++anon;
+	}
+	fclose(f);
+
+	if (used != total
+	    || total > 500
+	    || anon != 0
+	    || db_debug) {
+		if (anon != 0)
+			dcc_trace_msg("used %d of %d RL blocks,"
+				      " %d for recent anonymous clients"
+				      " in %s",
+				      used, total, anon,
+				      DB_NM2PATH_ERR(CLIENTS_NM()));
+		else
+			dcc_trace_msg("used %d of %d RL blocks in %s",
+				      used, total,
+				      DB_NM2PATH_ERR(CLIENTS_NM()));
+	}
+}
+
+
+
+/* pack up a 32 bit int as part of an answer to a `cdcc clients` request */
+static u_char *
+client_pack4(u_char *cp,
+	     u_int32_t v)
+{
+	while (v > 0x7f) {
+		*cp++ = v | 0x80;
+		v >>= 7;
+	}
+	*cp++ = v;
+	return cp;
+}
+
+
+
+static int
+client_pack(u_char *cp0,		/* pack into this byte first */
+	    u_char **flagsp,		/* pointer to packed flags */
+	    u_char flags,		/* DCC_ADMN_RESP_CLIENTS_* */
+	    DCC_CLNT_ID clnt_id,
+	    time_t last_used,		/* skip count if ..._CLIENTS_SKIP */
+	    int requests,
+	    int nops,
+	    u_char vers,
+	    const struct in6_addr *clnt_addr)
+{
+	u_char *cp;
+
+	cp = cp0;
+	*flagsp = cp0;			/* announce location of these flags */
+	if (vers != 0)
+		flags |= DCC_ADMN_RESP_CLIENTS_VERS;
+	*cp++ = flags;
+	if (flags & DCC_ADMN_RESP_CLIENTS_VERS)
+		*cp++ = vers;
+	*cp++ = last_used >> 24;
+	*cp++ = last_used >> 16;
+	*cp++ = last_used >> 8;
+	*cp++ = last_used;
+	if (clnt_id == DCC_ID_ANON) {
+		*cp0 |= DCC_ADMN_RESP_CLIENTS_ID1;
+	} else {
+		cp = client_pack4(cp, clnt_id);
+	}
+	cp = client_pack4(cp, requests);
+	cp = client_pack4(cp, nops);
+	if (!clnt_addr) {
+		memset(cp, 0, 4);
+		cp += 4;
+	} else if (DCC_IN6_ADDR_V4MAPPED(clnt_addr)) {
+		memcpy(cp, &clnt_addr->s6_addr32[3], 4);
+		cp += 4;
+	} else {
+		*cp0 |= DCC_ADMN_RESP_CLIENTS_IPV6;
+		memcpy(cp, clnt_addr->s6_addr32, 16);
+		cp += 16;
+	}
+	return cp-cp0;
+}
+
+
+
+static int
+client_pack_skip(u_char *cp, u_char **prev_flags, u_int skipped)
+{
+	return client_pack(cp, prev_flags, DCC_ADMN_RESP_CLIENTS_SKIP,
+			   DCC_ID_ANON, skipped, 0, 0, 0, 0);
+}
+
+
+
+/* get list of clients,
+ *	treating clients with the same ID as if they were all the same system */
+void
+clients_get_id(DCC_ADMN_RESP_VAL *val,
+	       int *lenp,		/* buffer length */
+	       u_int offset,		/* skip this many newer entries */
+	       int thold,		/* skip clients with fewer requests */
+	       u_char req_flags,
+	       const struct in6_addr *addr6,
+	       const struct in6_addr *mask6)
+{
+	RL *rl, *rl2;
+	u_char *skip;
+	int requests, nops;
+	u_char *prev_flags;		/* flags in previous record */
+	u_int skipped;
+	int len, len_lim;
+
+	if (offset == 0)
+		need_clients_save = 0;
+
+	prev_flags = 0;
+	len_lim = *lenp;
+	len_lim <<= DCC_ADMIN_RESP_CLIENTS_SHIFT;
+	if (len_lim > ISZ(*val))
+		len_lim = ISZ(*val);
+	skip = 0;
+	skipped = 0;
+	len = 0;
+	if (!thold)
+		thold = 1;
+	for (rl = rl_newest; rl != 0; rl = rl->older)
+		rl->d.flags &= ~RL_FG_MARKED;
+
+	for (rl = rl_newest; ; rl = rl->older) {
+		if (!rl) {
+			/* there are no more, so
+			 * mark the previous record as the last */
+			if (prev_flags)
+				*prev_flags |= DCC_ADMN_RESP_CLIENTS_LAST;
+			break;
+		}
+
+		if (rl->d.flags & RL_FG_MARKED)
+			continue;
+
+		if (addr6 && !DCC_IN_BLOCK(rl->d.clnt_addr, *addr6, *mask6)) {
+			rl->d.flags |= RL_FG_MARKED;
+			continue;
+		}
+
+		if (rl->d.last_used == 0) {
+			if (!(req_flags & DCC_AOP_CLIENTS_AVG))
+				continue;
+			/* report clients that have been quiet today if
+			 * they were active yesterday */
+			rl_avg_requests_age(rl, 0);
+			if (RL_REQUESTS_AVG(&rl->d) == 0
+			    && RL_NOPS_AVG(&rl->d) == 0)
+				continue;
+		}
+
+		requests = 0;
+		nops = 0;
+		for (rl2 = rl; rl2 != 0; rl2 = rl2->older) {
+			if (rl2->d.clnt_id != rl->d.clnt_id)
+				continue;
+			rl2->d.flags |= RL_FG_MARKED;
+			if (addr6
+			    && !DCC_IN_BLOCK(rl->d.clnt_addr, *addr6, *mask6))
+				continue;
+			if (req_flags & DCC_AOP_CLIENTS_AVG) {
+				rl_avg_requests_age(rl2, 0);
+				requests += RL_REQUESTS_AVG(&rl2->d);
+				nops += RL_NOPS_AVG(&rl2->d);
+			} else {
+				requests += rl2->d.requests;
+				nops += rl2->d.nops;
+			}
+		}
+
+		/* get the part of the list that cdcc wants */
+		if (offset != 0) {
+			--offset;
+			continue;
+		}
+
+		if (requests < thold) {
+			/* The threshold might be larger on the next request
+			 * from cdcc, so tell cdcc the number skipped for
+			 * the threshold this time.
+			 * Tell cdcc by insertint a fake entry. */
+			if (!skip) {
+				skip = &val->clients[len];
+				len += client_pack_skip(skip, &prev_flags, 0);
+			}
+			++skipped;
+			continue;
+		}
+
+		/* stop at end of buffer
+		 * check only after skipping boring records for the common
+		 * case of the last 10,000 records missing the threshold */
+		if (len+DCC_ADMN_RESP_CLIENTS_MAX_SIZE > len_lim)
+			break;
+
+		len += client_pack(&val->clients[len], &prev_flags, 0,
+				   rl->d.clnt_id, rl->d.last_used,
+				   requests, nops, 0, 0);
+	}
+	if (skipped)
+		client_pack_skip(skip, &prev_flags, skipped);
+	*lenp = len;
+}
+
+
+
+/* get a list of the most recent clients */
+int					/* +/- number of clients */
+clients_get(DCC_ADMN_RESP_VAL *val,
+	    int *lenp,			/* buffer length */
+	    u_int offset,		/* skip this many newer entries */
+	    int thold,			/* skip clients with fewer requests */
+	    u_char req_flags,
+	    const struct in6_addr *addr6,
+	    const struct in6_addr *mask6)
+{
+	RL *rl;
+	u_char *skip;
+	int requests, nops;
+	u_char prev_vers, vers;
+	u_char *prev_flags;		/* flags in previous record */
+	int skipped, total, len, len_lim;
+
+	if (offset == 0)
+		need_clients_save = 0;
+
+	prev_flags = 0;
+	if (!val || !lenp) {
+		len_lim = 0;
+	} else {
+		len_lim = *lenp;
+		len_lim <<= DCC_ADMIN_RESP_CLIENTS_SHIFT;
+		if (len_lim > ISZ(*val))
+			len_lim = ISZ(*val);
+	}
+	if (!thold)
+		thold = 1;
+	prev_vers = 0;
+	skip = 0;
+	skipped = 0;
+	total = 0;
+	len = 0;
+	for (rl = rl_newest; ; rl = rl->older) {
+		if (!rl) {
+			/* there are no more, so
+			 * mark the previous record as the last */
+			if (prev_flags)
+				*prev_flags |= DCC_ADMN_RESP_CLIENTS_LAST;
+			break;
+		}
+
+		if (rl->d.last_used == 0) {
+			if (!(req_flags & DCC_AOP_CLIENTS_AVG))
+				continue;
+			/* report clients that have been quiet today if
+			 * they were active yesterday
+			 * and we are reporting averages */
+			rl_avg_requests_age(rl, 0);
+			if (RL_REQUESTS_AVG(&rl->d) == 0
+			    && RL_NOPS_AVG(&rl->d) == 0)
+				continue;
+		}
+
+		if (rl->d.requests != 0)
+			++total;
+
+		/* compute only the total for `cdcc stats` if buffer is null */
+		if (len_lim == 0)
+			continue;
+
+		/* respect client IP address limitations */
+		if (addr6 && !DCC_IN_BLOCK(rl->d.clnt_addr, *addr6, *mask6))
+			continue;
+
+		/* always report blacklisted clients
+		 * report only (non-)anonymous clients if asked, error
+		 *	toward reporting clients that were treated as
+		 *	anonymous but used a real ID */
+		if (!(rl->d.flags & RL_FG_BLS)) {
+			if ((req_flags & DCC_AOP_CLIENTS_ANON)
+			    && !(rl->d.flags & RL_FG_ANON))
+				continue;
+			if ((req_flags & DCC_AOP_CLIENTS_NON_ANON)
+			    && rl->d.clnt_id == DCC_ID_ANON)
+				continue;
+		}
+
+		if (offset != 0) {
+			--offset;
+			continue;
+		}
+
+		if (req_flags & DCC_AOP_CLIENTS_AVG) {
+			rl_avg_requests_age(rl, 0);
+			requests = RL_REQUESTS_AVG(&rl->d);
+			nops = RL_NOPS_AVG(&rl->d);
+		} else {
+			requests = rl->d.requests;
+			nops = rl->d.nops;
+		}
+
+		/* skip uninteresting records and insert a fake
+		 * entry in the output to the client with the total
+		 * skipped in the entire response to the client */
+		if (requests < thold
+		    && (!(rl->d.flags & RL_FG_BLS)
+			|| rl->d.requests == 0)) {
+			if (!skip) {
+				/* start a new fake record */
+				skip = &val->clients[len];
+				len += client_pack_skip(skip, &prev_flags, 0);
+				prev_vers = 0;
+			}
+			++skipped;
+			continue;
+		}
+
+		/* stop at end of buffer
+		 * check only after skipping boring records for the common
+		 * case of ignoring the last 10,000 records */
+		if (len + DCC_ADMN_RESP_CLIENTS_MAX_SIZE*2 > len_lim)
+				break;
+
+		/* send the version number if it is wanted and differs
+		 * from the previous value */
+		if ((req_flags & DCC_AOP_CLIENTS_VERS)
+		    && rl->d.pkt_vers != prev_vers) {
+			vers = rl->d.pkt_vers;
+			prev_vers = vers;
+		} else {
+			vers = 0;
+		}
+
+		len += client_pack(&val->clients[len], &prev_flags,
+				   (rl->d.flags & (RL_FG_BL_ADDR | RL_FG_BL_ID))
+				   ? DCC_ADMN_RESP_CLIENTS_BL
+				   : (rl->d.flags & RL_FG_BL_BAD)
+				   ? DCC_ADMN_RESP_CLIENTS_BAD
+				   : 0,
+				   rl->d.clnt_id, rl->d.last_used,
+				   requests, nops, vers, &rl->d.clnt_addr);
+	}
+	/* put final total number of skipped records in the output */
+	if (skipped)
+		client_pack_skip(skip, &prev_flags, skipped);
+
+	if (lenp)
+		*lenp = len;
+
+	/* return negative total if the oldest block is not very old and
+	 * we have had to recycle a recent block */
+	if (rl_too_many)
+		return -total;
+	return total;
+}
+
+
+
+/* forget old clients */
+void
+clients_clear()
+{
+	RL *rl;
+
+	for (rl = rl_oldest; rl != 0; rl = rl->newer) {
+		rl_avg_requests_age(rl, 1);
+		rl->d.requests = 0;
+		rl->d.requests_old = 0;
+		rl->d.nops = 0;
+		rl->d.nops_old = 0;
+	}
+
+	clients_cleared = db_time.tv_sec;
+	rl_too_many = 0;
+}
+
+
+
+u_char					/* 0=bad passwd, 1=1st passwd, 2=2nd */
+ck_sign(const ID_TBL **tpp,		/* return ID table entry here */
+	DCC_PASSWD passwd,		/* return matching password here */
+	DCC_CLNT_ID id,
+	const void *buf, u_int buf_len)
+{
+	ID_TBL *tp;
+
+	tp = find_id_tbl(id);
+	if (tpp)
+		*tpp = tp;
+	if (!tp)
+		return 0;
+
+	if (tp->cur_passwd[0] != '\0'
+	    && dcc_ck_signature(tp->cur_passwd, sizeof(tp->cur_passwd),
+				buf, buf_len)) {
+		if (passwd)
+			memcpy(passwd, tp->cur_passwd, sizeof(DCC_PASSWD));
+		return 1;
+	}
+	if (tp->next_passwd[0] != '\0'
+	    && dcc_ck_signature(tp->next_passwd, sizeof(tp->next_passwd),
+				buf, buf_len)) {
+		if (passwd)
+			memcpy(passwd, tp->next_passwd, sizeof(DCC_PASSWD));
+		return 2;
+	}
+	return 0;
+}
+
+
+
+/* decide how long a request should be delayed */
+static u_char				/* 0=delay >= maximum */
+tp2delay(QUEUE *q, time_t delay_us, u_int delay_inflate)
+{
+	int inflate;
+
+	if (delay_us != 0) {
+		inflate = 1 + RL_REQUESTS_AVG(&q->rl->d)/delay_inflate;
+		if (inflate > DCC_ANON_DELAY_MAX / delay_us /* no overflow */
+		    || (delay_us *= inflate) >= DCC_ANON_DELAY_MAX) {
+			q->rl->d.flags |= RL_FG_BL_BAD;
+			q->delay_us = DCC_ANON_DELAY_MAX;
+			return 0;
+		}
+	}
+
+	/* do not answer anonymous clients while we are cleaning
+	 * the database */
+	if ((db_minimum_map || anon_off) && (q->rl->d.flags & RL_FG_ANON)) {
+		q->delay_us = DCC_ANON_DELAY_MAX;
+		return 0;
+	}
+
+	if (flods_off > 0
+	    || flods_st != FLODS_ST_ON
+	    || (iflods.open == 0 && (oflods.total != 0 || !grey_on))
+	    || !DB_IS_TIME(iflods_ok_timer, IFLODS_OK_SECS)) {
+		/* Increase the delay when flooding is off, broken,
+		 * or just starting after being off
+		 * Greylist servers without any flooding peers can be isolated
+		 * but not DCC servers */
+		delay_us += 400*1000;
+
+	} else if (dbclean_wfix_state == WFIX_QUIET
+		   || dbclean_wfix_state == WFIX_CHECK) {
+		/* increase the delay if we are testing to see if clients
+		 * have a alternative so we can do a quick window fix. */
+		delay_us += 1000*1000;
+	}
+
+	if (delay_us >= DCC_ANON_DELAY_MAX) {
+		q->delay_us = DCC_ANON_DELAY_MAX;
+		return 0;
+	}
+	q->delay_us = delay_us;
+	return 1;
+}
+
+
+
+/* check the message authentication code and rate limit requests */
+static u_char				/* 0=forget request, 1=go ahead */
+ck_id(QUEUE *q,
+      DCC_CLNT_ID id,			/* what the client claimed */
+      DCC_CLNT_ID min_id)		/* allowed */
+{
+#define RL_CNT2AVG(cur,lims) ((lims.hi - cur) / (RL_SCALE*RL_AVG_SECS*1.0))
+	RL_DATA_FG id_fgs = 0;
+	const ID_TBL *tp;
+	RL *rl;
+	int result;			/* 1=ok & no msg; 0=msg; -1=no msg */
+
+	q->clnt_id = id;
+	if (id == DCC_ID_ANON) {
+		tp = 0;
+		q->flags |= Q_FG_UNTRUSTED;
+		id_fgs = RL_FG_ANON;
+		result = 1;
+
+	} else if (id < min_id) {
+		tp = 0;
+		q->flags |= (Q_FG_UNTRUSTED | Q_FG_UKN_ID);
+		id_fgs = (RL_FG_UKN_ID | RL_FG_ANON);
+		result = 0;
+
+	} else if (!ck_sign(&tp, q->passwd, id, &q->pkt, q->pkt_len)) {
+		/* failed to authenticate the ID */
+		if (!tp) {
+			q->flags |= Q_FG_UKN_ID;
+			id_fgs = (RL_FG_UKN_ID | RL_FG_ANON);
+		} else {
+			if (tp->delay_us >= DCC_ANON_DELAY_US_BLACKLIST)
+				id_fgs |= RL_FG_BL_ID;
+			tp = 0;
+			q->flags |= Q_FG_BAD_PASSWD;
+			id_fgs = (RL_FG_PASSWD | RL_FG_ANON);
+		}
+		q->flags |= Q_FG_UNTRUSTED;
+		result = 0;
+
+	} else if (tp->delay_us >= DCC_ANON_DELAY_US_BLACKLIST) {
+		q->flags |= Q_FG_UNTRUSTED;
+		id_fgs = RL_FG_BL_ID;
+		result = -1;
+
+	} else {
+		id_fgs = 0;
+		result = 1;
+	}
+
+
+	rl = rl_get_q(q, id);
+
+	rl->d.pkt_vers = q->pkt.hdr.pkt_vers;
+
+	/* See if this client IP address is blacklisted,
+	 * if we have not checked it since (re)loading the blacklist */
+	rl->d.flags &= ~(RL_FG_BL_ID | RL_FG_BL_BAD
+			 | RL_FG_PASSWD | RL_FG_UKN_ID | RL_FG_ANON);
+	rl->d.flags |= id_fgs;
+	if (!(rl->d.flags & RL_FG_CK_BL))
+		ck_ip_bl(&rl, q->clnt_id, &rl->d.clnt_addr);
+	if (rl->d.flags & (RL_FG_BL_ADDR | RL_FG_BL_ID))
+		result = -1;
+
+	if (tp) {
+		if (!query_only || (tp->flags & ID_FLG_RPT_OK))
+			q->flags |= Q_FG_RPT_OK;
+
+		rl_inc(rl, &rl_sub_rate);
+
+		if (!tp2delay(q, tp->delay_us, tp->delay_inflate))
+			result = -1;
+
+		if (rl->d.request_credits <= 0 && result >= 0) {
+			clnt_msg(q, "%.1f requests/sec are too many%s",
+				 RL_CNT2AVG(rl->d.request_credits, rl_sub_rate),
+				 from_id_ip(q, 0));
+			++dccd_stats.rl;
+			rl->d.flags |= RL_FG_BL_BAD;
+			result = -1;
+		}
+
+	} else {
+		if (!query_only)
+			q->flags |= Q_FG_RPT_OK;
+
+		rl_inc(rl, &rl_anon_rate);
+		rl_inc(&rl_all_anon, &rl_all_anon_rate);
+
+		if (!tp2delay(q, anon_delay_us, anon_delay_inflate))
+			result = -1;
+
+		if (rl->d.request_credits <= 0 && result >= 0) {
+			anon_msg("%.1f requests/sec are too many%s",
+				 RL_CNT2AVG(rl->d.request_credits,
+					    rl_anon_rate),
+				 from_id_ip(q, 0));
+			rl->d.flags |= RL_FG_BL_BAD;
+			++dccd_stats.anon_rl;
+			result = -1;
+		} else if (rl_all_anon.d.request_credits <= 0 && result >= 0) {
+			anon_msg("%s contributed to %.1f"
+				 " anonymous requests/sec",
+				 Q_CIP(q),
+				 RL_CNT2AVG(rl_all_anon.d.request_credits,
+					    rl_all_anon_rate));
+			++dccd_stats.anon_rl;
+			result = -1;
+		}
+	}
+
+	if (q->flags & (Q_FG_BAD_PASSWD | Q_FG_UKN_ID))
+		++dccd_stats.bad_passwd;
+	else if (rl->d.flags & RL_FG_BL_BAD)
+		++dccd_stats.bad_op;
+	else if (rl->d.flags & RL_FG_BLS)
+		++dccd_stats.blist;
+
+	/* complain if tracing all blacklisted clients
+	 * or if tracing this client
+	 * or if this client is not blacklisted
+	 *	but uses a bad password or ID */
+	if (((rl->d.flags & RL_FG_BLS) && (DCC_TRACE_BL_BIT & dccd_tracemask))
+	    || (rl->d.flags & RL_FG_TRACE)
+	    || result == 0) {
+		char result_buf[30];
+		const char *result_str, *bl_str;
+
+		if (result >= 0) {
+			if (q->delay_us == 0) {
+				result_str = "";
+			} else {
+				snprintf(result_buf, sizeof(result_buf),
+					 "delay %.3f ",
+					 q->delay_us/(DCC_US*1.0));
+				result_str = result_buf;
+			}
+		} else {
+			result_str = "drop ";
+		}
+		bl_str = ((rl->d.flags & RL_FG_BL_ADDR) ? "blacklisted "
+			  : (rl->d.flags & RL_FG_BL_ID) ? "ID blacklisted "
+			  : "");
+		dcc_trace_msg("%s%s%s",
+			      result_str, bl_str, from_id_ip(q, 1));
+	}
+
+	return (result >= 0);
+}
+
+
+
+/* check the message authentication code for a client of our server-ID
+ * and rate limit its messages */
+u_char					/* 0=forget it, 1=go ahead */
+ck_clnt_srvr_id(QUEUE *q)
+{
+	/* require a client-ID, our server-ID, or the anonymous client-ID
+	 * to consider allowing an administrative request */
+	return ck_id(q, ntohl(q->pkt.hdr.sender), DCC_SRVR_ID_MIN);
+}
+
+
+
+/* check the message authentication code of a request,
+ * and rate limit the source */
+u_char					/* 0=forget it, 1=go ahead */
+ck_clnt_id(QUEUE *q)
+{
+	/* require a client-ID and not a server-ID to discourage server
+	 * operators from leaking server-ID's */
+	return ck_id(q, ntohl(q->pkt.hdr.sender), DCC_CLNT_ID_MIN);
+}
+
+
+
+const char *
+qop2str(const QUEUE *q)
+{
+	static int bufno;
+	static struct {
+	    char    str[DCC_OPBUF];
+	} bufs[4];
+	char *s;
+
+	s = bufs[bufno].str;
+	bufno = (bufno+1) % DIM(bufs);
+
+	return dcc_hdr_op2str(s, DCC_OPBUF, &q->pkt.hdr);
+}
+
+
+
+const char *
+from_id_ip(const QUEUE *q,
+	   u_char print_op)		/* 1=include operation */
+{
+	static char buf[DCC_OPBUF
+			+ISZ(" from ")+40+ISZ(" at ")+DCC_SU2STR_SIZE];
+	char ob[DCC_OPBUF];
+	const char *op;
+
+	if (print_op) {
+		op = dcc_hdr_op2str(ob, sizeof(ob), &q->pkt.hdr);
+	} else {
+		op = "";
+	}
+
+	if (q->clnt_id == DCC_ID_ANON)
+		snprintf(buf, sizeof(buf),
+			 "%s from anonymous at %s",
+			 op, dcc_su2str_err(&q->clnt_su));
+	else if (q->flags & Q_FG_UKN_ID)
+		snprintf(buf, sizeof(buf),
+			 "%s from unknown ID %d at %s",
+			 op, q->clnt_id, dcc_su2str_err(&q->clnt_su));
+	else if (q->flags & Q_FG_BAD_PASSWD)
+		snprintf(buf, sizeof(buf),
+			 "%s from ID %d at %s with bad password ",
+			 op, q->clnt_id, dcc_su2str_err(&q->clnt_su));
+	else if (q->flags & Q_FG_UNTRUSTED)
+		snprintf(buf, sizeof(buf),
+			 "%s from bad ID %d at %s",
+			 op, q->clnt_id, dcc_su2str_err(&q->clnt_su));
+	else
+		snprintf(buf, sizeof(buf),
+			 "%s from ID %d at %s",
+			 op, q->clnt_id, dcc_su2str_err(&q->clnt_su));
+
+	return buf;
+}
+
+
+
+const char *
+op_id_ip(const QUEUE *q)
+{
+	static char buf[3+14+4+DCC_SU2STR_SIZE];
+	char ob[DCC_OPBUF];
+
+	snprintf(buf, sizeof(buf), "%s from ID %d at %s",
+		 dcc_hdr_op2str(ob, sizeof(ob), &q->pkt.hdr),
+		 (DCC_CLNT_ID)ntohl(q->pkt.hdr.sender), Q_CIP(q));
+	return buf;
+}
+
+
+
+/* complain about an anonymous, non-paying client */
+void
+vanon_msg(const char *p, va_list args)
+{
+	rl_age(&rl_all_anon, &rl_all_anon_rate);
+	if ((DCC_TRACE_ANON_BIT & dccd_tracemask)
+	    && (rl_all_anon.d.bug_credits > 0
+		|| (DCC_TRACE_RLIM_BIT & dccd_tracemask))) {
+		rl_all_anon.d.bug_credits -= RL_SCALE;
+		dcc_vtrace_msg(p, args);
+	}
+}
+
+
+
+void
+anon_msg(const char *p, ...)
+{
+	va_list args;
+
+	va_start(args, p);
+	vanon_msg(p, args);
+	va_end(args);
+}
+
+
+
+/* complain about a client */
+void
+vclnt_msg(const QUEUE *q, const char *p, va_list args)
+{
+	if ((q->flags & Q_FG_UNTRUSTED) || !q->rl) {
+		vanon_msg(p, args);
+		return;
+	}
+
+	if (DCC_TRACE_CLNT_BIT & dccd_tracemask) {
+		rl_age(q->rl, &rl_sub_rate);
+		if (q->rl->d.bug_credits > 0
+		    || (DCC_TRACE_RLIM_BIT & dccd_tracemask)) {
+			q->rl->d.bug_credits -= RL_SCALE;
+			dcc_vtrace_msg(p, args);
+		}
+	}
+}
+
+
+
+/* complain about a client */
+void
+clnt_msg(const QUEUE *q, const char *p, ...)
+{
+	va_list args;
+
+	va_start(args, p);
+	vclnt_msg(q, p, args);
+	va_end(args);
+}
+
+
+
+void
+drop_msg(QUEUE *q, const char *p, ...)
+{
+	RL *rl;
+	char buf[80];
+	va_list args;
+
+	rl = rl_get_q(q, DCC_ID_ANON);
+	rl->d.flags |= RL_FG_BL_BAD;
+
+	rl_inc(rl, &rl_anon_rate);
+	rl_inc(&rl_all_anon, &rl_all_anon_rate);
+	if (!(rl->d.flags & RL_FG_BL_ADDR)) {
+		va_start(args, p);
+		vsnprintf(buf, sizeof(buf), p, args);
+		va_end(args);
+		anon_msg("drop %s from %s", buf, Q_CIP(q));
+	}
+}
diff -r 000000000000 -r c7f6b056b673 dccd/work.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccd/work.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,3079 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * work on a job in the server
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.287 $Revision$
+ */
+
+#include "dccd_defs.h"
+
+typedef struct {
+    time_t us;
+    u_int   ops;
+} Q_DELAY_SEC;
+static Q_DELAY_SEC q_delays[9];
+static Q_DELAY_SEC q_delays_sum;	/* sum of all but q_delays[0] */
+static time_t q_delays_start;		/* second for q_delayw[0] */
+
+DCCD_STATS dccd_stats;
+
+u_char query_only;			/* 1=treat reports as queries */
+
+u_char grey_weak_body;			/* 1=ignore bodies for greylisting */
+u_char grey_weak_ip;			/* 1=a good triple whitelists addr */
+
+static u_char ridc_get(QUEUE *);
+
+
+/* report cache used to detect duplicate or retransmitted reports */
+static RIDC *ridc_newest, *ridc_oldest;
+static RIDC **ridc_hash;
+static int ridc_hash_len;
+
+
+static inline RIDC **
+ridc_hash_fnc(DCC_HDR *hdr)
+{
+	u_int32_t sum;
+
+	/* The client's (ID,RID,HID,PID) should be unique and constant for
+	 * retransmissions of a single request.  It should make a reasonable
+	 * hash value.  We cannot trust it entirely, if only because of
+	 * anonymous clients */
+	sum = hdr->sender;
+	sum += hdr->op_nums.h;
+	sum += hdr->op_nums.p;
+	sum += hdr->op_nums.r;
+
+	return &ridc_hash[mhash(sum, ridc_hash_len)];
+}
+
+
+
+static void
+ridc_ref(RIDC *ridc)
+{
+	ridc->last_used = db_time.tv_sec;
+	if (!ridc->newer)
+		return;			/* it's already newest */
+
+	ridc->newer->older = ridc->older;
+	if (ridc->older)
+		ridc->older->newer = ridc->newer;
+	else
+		ridc_oldest = ridc->newer;
+	ridc->older = ridc_newest;
+	ridc->newer = 0;
+	ridc_newest->newer = ridc;
+	ridc_newest = ridc;
+}
+
+
+
+/* get a free report cache block */
+static RIDC *
+ridc_get_free(void)
+{
+	RIDC *ridc;
+	time_t stale = db_time.tv_sec - DCC_MAX_RETRANS_DELAY_SECS;
+
+	for (ridc = ridc_oldest; ridc != 0; ridc = ridc->newer) {
+		if (ridc->last_used < stale) {
+			/* found one, so recycle it */
+			if (ridc->fwd)
+				ridc->fwd->bak = ridc->bak;
+			if (ridc->bak)
+				ridc->bak->fwd = ridc->fwd;
+			else if (ridc->hash)
+				*ridc->hash = ridc->fwd;
+			ridc->bak = 0;
+			ridc_ref(ridc);
+			return ridc;
+		}
+	}
+
+	/* there are no free blocks that are old enough to recycle */
+	return 0;
+}
+
+
+
+/* make some (more) RID blocks and (re)build the hash table */
+static void
+ridc_make(void)
+{
+	int new_len, old_len, j;
+	RIDC *ridc, *ridc2, **ridch, **old_ridc_hash;
+
+	new_len = queue_max;
+	ridc = dcc_malloc(new_len*sizeof(*ridc));
+	if (!ridc)
+		dcc_logbad(EX_OSERR, "malloc(%d RIDC blocks) failed",
+			   new_len);
+	memset(ridc, 0, new_len*sizeof(*ridc));
+	for (j = 0; j < new_len; ++j, ++ridc) {	/* make the new blocks oldest */
+		if (!ridc_oldest) {
+			ridc_oldest = ridc_newest = ridc;
+		} else {
+			ridc_oldest->older = ridc;
+			ridc->newer = ridc_oldest;
+			ridc_oldest = ridc;
+		}
+	}
+
+	/* rebuild and expand the hash table */
+	old_len = ridc_hash_len;
+	ridc_hash_len += new_len;
+	old_ridc_hash = ridc_hash;
+	ridc_hash = dcc_malloc(ridc_hash_len*sizeof(*ridch));
+	if (!ridc_hash)
+		dcc_logbad(EX_OSERR, "malloc(%d RIDC hash table) failed",
+			   ridc_hash_len);
+	memset(ridc_hash, 0, ridc_hash_len*sizeof(*ridch));
+	if (old_len != 0) {
+		do {
+			for (ridc = old_ridc_hash[--old_len];
+			     ridc != 0;
+			     ridc = ridc2) {
+				ridch = ridc_hash_fnc(&ridc->hdr);
+				ridc2 = ridc->fwd;
+				ridc->bak = 0;
+				ridc->hash = ridch;
+				if ((ridc->fwd = *ridch) != 0)
+					ridc->fwd->bak = ridc;
+				*ridch = ridc;
+			}
+		} while (old_len != 0);
+		dcc_free(old_ridc_hash);
+	}
+}
+
+
+
+/* get the report cache block for an operation */
+static u_char				/* 0=new operation, 1=retransmission */
+ridc_get(QUEUE *q)
+{
+	RIDC *ridc, **ridch;
+
+	for (;;) {
+		if (ridc_hash) {
+			/* look for the existing report cache block */
+			ridch = ridc_hash_fnc(&q->pkt.hdr);
+			for (ridc = *ridch; ridc; ridc = ridc->fwd) {
+				/* Reports are relatively small, so we
+				 * can afford to not trust the client's
+				 * RID to be unique.  Compare all but the
+				 * client's transmission #. 
+				 * Also check client's UDP port # because
+				 * it should be unchanged regardless of
+				 * multi-homing. */
+				if (ridc->clnt_port == q->clnt_su.ipv4.sin_port
+				    && !memcmp(&ridc->hdr, &q->pkt.hdr,
+					       sizeof(ridc->hdr)
+					       - sizeof(ridc->hdr.op_nums.t))) {
+					/* found it, so make it newest */
+					ridc_ref(ridc);
+					q->ridc = ridc;
+					return 1;
+				}
+			}
+
+			/* the block does not already exist, so create it */
+			ridc = ridc_get_free();
+			if (ridc)
+				break;
+		}
+		/* we are out of report cache blocks, so make more */
+		ridc_make();
+
+		/* re-hash because our previous pointer is invalid */
+	}
+
+	memcpy(&ridc->hdr, &q->pkt.hdr, sizeof(ridc->hdr));
+	ridc->clnt_port = q->clnt_su.ipv4.sin_port;
+	ridc->op = DCC_OP_INVALID;
+	ridc->bad = 1;
+	ridc->len = 0;
+	ridc->hash = ridch;
+	ridc->fwd = *ridch;
+	if (ridc->fwd)
+		ridc->fwd->bak = ridc;
+	*ridch = ridc;
+
+	q->ridc = ridc;
+	return 0;
+}
+
+
+
+#define RIDC_BAD(q) {if ((q)->ridc) (q)->ridc->bad = 1;}
+
+
+/* update the average queue delay at the start of a new second */
+static void
+update_q_delay(void)
+{
+	time_t secs;
+	Q_DELAY_SEC *src, *tgt;
+
+	secs = db_time.tv_sec - q_delays_start;
+	if (secs == 0)
+		return;
+
+	/* At the start of a new second,
+	 * forget the delays for old seconds we no longer care about
+	 * and start accumulating delays for the new second
+	 * Slide accumulated delays and total operations previous seconds. */
+	q_delays_start = db_time.tv_sec;
+	q_delays_sum.us = 0;
+	q_delays_sum.ops = 0;
+	tgt = LAST(q_delays);
+	if (secs > 0 && secs < DIM(q_delays)) {
+		src = tgt - secs;
+		do {
+			q_delays_sum.us += (tgt->us = src->us);
+			q_delays_sum.ops += (tgt->ops = src->ops);
+			--tgt;
+		} while (src-- != &q_delays[0]);
+	}
+	memset(q_delays, 0, sizeof(q_delays[0]) * (tgt+1 - q_delays));
+}
+
+
+
+/* compute the average queue delay this client should see */
+static u_int
+avg_q_delay_ms(const QUEUE *q)
+{
+	u_int ops;
+	time_t us;
+
+	/* get the average service delay excluding per-client-ID delays */
+	update_q_delay();
+	ops = q_delays[0].ops + q_delays_sum.ops;
+	if (ops == 0)
+		us = 0;
+	else
+		us = (q_delays[0].us + q_delays_sum.us + ops/2) / ops;
+
+	/* add the per-client-ID penalty */
+	us += q->delay_us;
+	return (us + 500) / 1000;
+}
+
+
+
+/* get a unique timestamp */
+static void
+get_ts(DCC_TS *ts)			/* put it here */
+{
+	static struct timeval prev_time;
+	static int faked;
+
+	/* if we have generated a lot of fake timestamps
+	 * and our snapshot of the clock is old,
+	 * then check the clock in the hope it has ticked */
+	if (db_time.tv_usec <= prev_time.tv_usec
+	    && db_time.tv_sec == prev_time.tv_sec
+	    && faked > 100) {
+		faked = 0;
+		gettimeofday(&db_time, 0);
+	}
+
+	/* Try to make the next timestamp unique, but only as long
+	 * as time itself marches forward.  This must work many times
+	 * a second, or the resoltion of DCC timestaps.
+	 * Worse, the increment can exhaust values from future seconds.
+	 * Forget about it if the problem lasts for more than 5 minutes. */
+	if (db_time.tv_sec > prev_time.tv_sec
+	    || (db_time.tv_sec == prev_time.tv_sec
+		&& db_time.tv_usec > prev_time.tv_usec)
+	    || db_time.tv_sec < prev_time.tv_sec-5*60) {
+		/* either the current time is good enough or we must
+		 * give up and use it to make the timestamp */
+		prev_time = db_time;
+		faked = 0;
+
+	} else {
+		/* fudge the previous timestamp to make it good enough */
+		prev_time.tv_usec += DCC_TS_US_MULT;
+		if (prev_time.tv_usec >= DCC_US) {
+			prev_time.tv_usec -= DCC_US;
+			++prev_time.tv_sec;
+		}
+		++faked;
+	}
+
+	dcc_timeval2ts(ts, &prev_time, 0);
+}
+
+
+
+/* find database record for a server-ID
+ *	use only db_sts.hash and db_sts.rcd2
+ *	put the result in db_sts.rcd2 */
+int					/* -1=broken database 0=no record */
+find_srvr_rcd(const DCC_SUM sum, const char *str)
+{
+	DB_RCD_CK *found_ck;
+	DB_PTR prev;
+	int failsafe;
+
+	switch (db_lookup(dcc_emsg, DCC_CK_SRVR_ID, sum,
+			  0, MAX_HASH_ENTRIES,
+			  &db_sts.hash, &db_sts.rcd2, &found_ck)) {
+	case DB_FOUND_LATER:
+	case DB_FOUND_SYSERR:
+		DB_ERROR_MSG2(str, dcc_emsg);
+		return -1;
+	case DB_FOUND_IT:
+		/* look for a record that is neither obsolete nor deleted */
+		for (failsafe = 0; failsafe < 20; ++failsafe) {
+			if (!DB_CK_OBS(found_ck)
+			    && DB_TGTS_RCD(db_sts.rcd2.d.r) != 0)
+				return 1;
+			prev = DB_PTR_EX(found_ck->prev);
+			if (prev == DB_PTR_NULL)
+				return 0;
+			found_ck = db_map_rcd_ck(dcc_emsg, &db_sts.rcd2, prev,
+						 DCC_CK_SRVR_ID);
+			if (!found_ck) {
+				DB_ERROR_MSG2(str, dcc_emsg);
+				return -1;
+			}
+		}
+		break;
+	case DB_FOUND_EMPTY:
+	case DB_FOUND_CHAIN:
+	case DB_FOUND_INTRUDER:
+		break;
+	}
+	return 0;
+}
+
+
+
+/* find the database record of the type of a server
+ *	use only db_sts.hash and db_sts.rcd2
+ *	put the result in db_sts.rcd2 */
+int					/* -1=broken database 0=no record */
+find_srvr_rcd_type(DCC_SRVR_ID tgt_id)
+{
+	DCC_SUM srvr_id_sum;
+
+	if (db_failed_line)
+		return -1;
+	memset(srvr_id_sum, 0, sizeof(srvr_id_sum));
+	srvr_id_sum[0] = DCC_CK_SRVR_ID;
+	srvr_id_sum[1] = tgt_id >> 8;
+	srvr_id_sum[2] = tgt_id;
+	return find_srvr_rcd(srvr_id_sum, "checking server-ID state");
+}
+
+
+
+/* find the server type in the table of IDs */
+ID_TBL *
+find_srvr_type(DCC_SRVR_ID tgt_id)
+{
+	ID_TBL *tp;
+	DCC_SRVR_ID srvr_type;
+
+	tp = find_id_tbl(tgt_id);
+	if (!tp) {
+		/* check the database if it is not in the table */
+		if (0 >= find_srvr_rcd_type(tgt_id)) {
+			/* assume it is a simple server if there is
+			 * no declaration in the database */
+			srvr_type = DCC_ID_SRVR_SIMPLE;
+		} else {
+			srvr_type = DB_RCD_ID(db_sts.rcd2.d.r);
+			if (!DCC_ID_SRVR_TYPE(srvr_type))
+				srvr_type = DCC_ID_SRVR_SIMPLE;
+			/* the free code knows nothing about reputations */
+			if (srvr_type == DCC_ID_SRVR_REP_OK)
+				srvr_type = DCC_ID_SRVR_SIMPLE;
+		}
+		/* cache it in the table */
+		tp = add_id_tbl(tgt_id);
+		tp->srvr_type = srvr_type;
+	}
+	return tp;
+}
+
+
+
+/* refresh our claim to our server-ID or similar
+ *	use only db_sts.hash and db_sts.rcd2 */
+void
+refresh_srvr_rcd(const DCC_SUM sum, DCC_SRVR_ID val, const char *str)
+{
+	DCC_TS old;
+	DB_RCD rcd;
+	int i;
+
+	/* add a new record
+	 * only if no recent record of the right value exists */
+	i = find_srvr_rcd(sum, str);
+	if (i < 0)
+		return;			/* broken database */
+	if (i > 0
+	    && DB_RCD_ID(db_sts.rcd2.d.r) == val) {
+		dcc_timeval2ts(&old, &db_time, -DCC_SRVR_ID_SECS/2);
+		if (!dcc_ts_older_ts(&db_sts.rcd2.d.r->ts, &old))
+			return;
+	}
+
+	memset(&rcd, 0, sizeof(rcd));
+	get_ts(&rcd.ts);
+	rcd.srvr_id_auth = val;
+	DB_TGTS_RCD_SET(&rcd, 1);
+	rcd.fgs_num_cks = 1;
+	rcd.cks[0].type_fgs = DCC_CK_SRVR_ID;
+	memcpy(rcd.cks[0].sum, sum, sizeof(DCC_SUM));
+	if (!db_add_rcd(dcc_emsg, &rcd))
+		DB_ERROR_MSG2(str, dcc_emsg);
+}
+
+
+
+static void
+send_resp(const QUEUE *q,
+	  DCC_HDR *hdr,			/* length in host byte order */
+	  u_char no_msg)
+{
+	u_int save_len;
+	char ob[DCC_OPBUF];
+	int len, i;
+
+	len = hdr->len;
+	hdr->len = htons(len);
+	/* callers must have dealt with the variations due to versions */
+	if (q->pkt.hdr.pkt_vers < DCC_PKT_VERSION_MIN)
+		hdr->pkt_vers = DCC_PKT_VERSION_MIN;
+	else if (q->pkt.hdr.pkt_vers > DCC_PKT_VERSION_MAX)
+		hdr->pkt_vers = DCC_PKT_VERSION_MAX;
+	else
+		hdr->pkt_vers = q->pkt.hdr.pkt_vers;
+	hdr->sender = htonl(my_srvr_id);
+	hdr->op_nums = q->pkt.hdr.op_nums;
+	if (q->passwd[0] != '\0') {
+		/* sign with the password that authenticated the client */
+		dcc_sign(q->passwd, sizeof(q->passwd), hdr, len);
+#ifdef DCC_PKT_VERSION8
+	} else if (q->pkt.hdr.pkt_vers <= DCC_PKT_VERSION8) {
+		/* Sign old protocol responses with the client's transaction
+		 * numbers if we do not have a good password.
+		 * This happens with anonymous clients */
+		dcc_sign((char *)&q->pkt.hdr.op_nums,
+			 sizeof(q->pkt.hdr.op_nums),
+			 hdr, len);
+#endif
+	} else {
+		memset((char *)hdr + (len-sizeof(DCC_SIGNATURE)), 0,
+		       sizeof(DCC_SIGNATURE));
+	}
+
+	if (q->ridc) {
+		save_len = len-sizeof(*hdr)-sizeof(DCC_SIGNATURE);
+		if (save_len > ISZ(q->ridc->result)) {
+			if (hdr->op == DCC_OP_ERROR)
+				save_len = sizeof(q->ridc->result);
+			else
+				dcc_logbad(EX_SOFTWARE, "RIDC buffer overflow");
+		}
+		q->ridc->len = save_len;
+		memcpy(&q->ridc->result, hdr+1, save_len);
+		q->ridc->op = hdr->op;
+		q->ridc->bad = 0;
+	}
+
+	i = sendto(q->sp->udp, hdr, len, 0,
+		   &q->clnt_su.sa, DCC_SU_LEN(&q->clnt_su));
+	if (i < 0) {
+		clnt_msg(q, "sendto(%s, %s): %s",
+			 dcc_hdr_op2str(ob, sizeof(ob), hdr), Q_CIP(q),
+			 ERROR_STR());
+	} else if (len != i) {
+		clnt_msg(q, "sendto(%s, %s)=%d instead of %d",
+			 dcc_hdr_op2str(ob, sizeof(ob), hdr), Q_CIP(q),
+			 i, len);
+	} else if (!no_msg
+		   && (dccd_tracemask & ((hdr->op == DCC_OP_ANSWER
+					  || hdr->op == DCC_OP_NOP)
+					 ? DCC_TRACE_QUERY_BIT
+					 : DCC_TRACE_ADMN_BIT))) {
+		dcc_trace_msg("sent %s to %s for %s",
+			      dcc_hdr_op2str(ob, sizeof(ob), hdr),
+			      Q_CIP(q), qop2str(q));
+	}
+
+}
+
+
+
+/* do not send an error response to a client */
+static void PATTRIB(2,3)
+forget_error(const QUEUE *q, const char *p, ...)
+{
+	va_list args;
+
+	RIDC_BAD(q);
+
+	if ((!q->flags & Q_FG_BAD_PASSWD)
+	    && !(q->rl->d.flags & RL_FG_BLS)) {
+		q->rl->d.flags |= RL_FG_BL_BAD;
+		++dccd_stats.bad_op;
+	}
+
+	va_start(args, p);
+	vclnt_msg(q, p, args);
+	va_end(args);
+}
+
+
+
+/* send an error response to a client */
+static void
+send_error(const QUEUE *q, const char *p, ...)
+{
+	DCC_ERROR buf;
+	int slen;
+	va_list args;
+
+
+	/* build and log the message */
+	va_start(args, p);
+	slen = vsnprintf(buf.msg, sizeof(buf.msg), p, args);
+	if (slen > ISZ(buf.msg)-1)
+		slen = ISZ(buf.msg)-1;
+	va_end(args);
+	clnt_msg(q, "\"%s\" sent to %s", buf.msg, Q_CIP(q));
+
+	/* send it */
+	buf.hdr.len = sizeof(buf)-sizeof(buf.msg)+slen+1;
+	buf.hdr.op = DCC_OP_ERROR;
+	send_resp(q, &buf.hdr, 1);
+
+	++dccd_stats.send_error;
+}
+
+
+
+#define NORESP_EMSG(q) noresp_emsg(q, __LINE__)
+
+static void
+noresp_emsg(const QUEUE *q, int linenum)
+{
+	dcc_error_msg("error near line %d in "DCC_VERSION" "__FILE__, linenum);
+	RIDC_BAD(q);
+}
+
+
+
+/* tell client that a NOP or an administrative request was ok */
+static void
+send_ok(QUEUE *q)
+{
+	DCC_OK buf;
+	time_t us;
+
+	memset(&buf, 0, sizeof(buf));
+
+	buf.max_pkt_vers = max(min(q->pkt.hdr.pkt_vers, DCC_PKT_VERSION_MAX),
+			       DCC_PKT_VERSION_MIN);
+	us = (q->delay_us + 500) / 1000;
+	buf.qdelay_ms = htons(us);
+	strncpy(buf.brand, brand, sizeof(buf.brand));
+	buf.hdr.op = DCC_OP_OK;
+	buf.hdr.len = sizeof(buf);
+
+	send_resp(q, &buf.hdr, 0);
+}
+
+
+
+static void
+repeat_resp(QUEUE *q)
+{
+	struct {
+	    DCC_HDR hdr;
+	    u_char  b[sizeof(q->ridc->result)];
+	} buf;
+	char ob[DCC_OPBUF];
+
+	++dccd_stats.report_retrans;
+
+	if (q->ridc->bad) {
+		TMSG1(RIDC, "repeat drop of %s", from_id_ip(q, 1));
+		return;
+	}
+
+	memcpy(&buf.hdr+1, &q->ridc->result, q->ridc->len);
+	buf.hdr.op = q->ridc->op;
+	buf.hdr.len = htons(q->ridc->len
+			    + sizeof(buf.hdr) + sizeof(DCC_SIGNATURE));
+	TMSG2(RIDC, "repeat previous answer of %s for %s",
+	      dcc_hdr_op2str(ob, sizeof(ob), &buf.hdr),
+	      from_id_ip(q, 1));
+	buf.hdr.len = ntohs(buf.hdr.len);
+	send_resp(q, &buf.hdr, 0);
+}
+
+
+
+/* find a checksum in the database
+ *	use only db_sts.hash and db_sts.rcd2
+ *	put the result in db_sts.rcd2 */
+static u_char				/* 0=broken database */
+get_ck_tgts(DCC_TGTS *tgtsp,
+	    const DB_RCD_CK **pfound_ck,
+	    u_char must_have_it,	/* 1=database broken if cksum absent */
+	    DCC_CK_TYPES type,
+	    const DCC_SUM sum)
+{
+	DB_RCD_CK *found_ck;
+
+	switch (db_lookup(dcc_emsg, type, sum, 0, MAX_HASH_ENTRIES,
+			  &db_sts.hash, &db_sts.rcd2, &found_ck)) {
+	case DB_FOUND_LATER:
+	case DB_FOUND_SYSERR:
+		DB_ERROR_MSG(dcc_emsg);
+		return 0;
+	case DB_FOUND_IT:
+		if (pfound_ck)
+			*pfound_ck = found_ck;
+		if (tgtsp)
+			*tgtsp = DB_TGTS_CK(found_ck);
+		break;
+	case DB_FOUND_EMPTY:
+	case DB_FOUND_CHAIN:
+	case DB_FOUND_INTRUDER:
+		if (must_have_it) {
+			db_error_msg(__LINE__,__FILE__,
+				     "missing hash entry for %s %s ",
+				     DB_TYPE2STR(type),
+				     dcc_ck2str_err(type, sum, 0));
+			return 0;
+		}
+		if (pfound_ck)
+			*pfound_ck = 0;
+		if (tgtsp)
+			*tgtsp = 0;
+		break;
+	}
+	return 1;
+}
+
+
+
+/* see if a count just passed a multiple of a threshold and so is
+ *	worth flooding or summarizing */
+static u_char				/* 1=time to summarize this checksum */
+quick_sum_thold(DCC_CK_TYPES type,
+		DCC_TGTS rpt_tgts,	/* targets in this report */
+		DCC_TGTS ck_tgts)	/* grand total */
+{
+	static DCC_TGTS thold_mults[] = {
+	    1, 2, 3, 5, 10
+	};
+	DCC_TGTS thold;
+	DCC_TGTS mult, new_mult, old_mult;
+	int i;
+
+	thold = flod_tholds[type];
+	if (ck_tgts < thold
+	    || thold >= DCC_TGTS_TOO_MANY)
+		return 0;
+	if (thold == 0)
+		return 1;
+
+	new_mult = ck_tgts / thold;
+	old_mult = (ck_tgts - rpt_tgts) / thold;
+	for (i = 0; i < DIM(thold_mults); ++i) {
+		mult = thold_mults[i];
+		if (old_mult < mult)
+			return (new_mult >= mult);
+	}
+	return 0;
+}
+
+
+
+/* compute summarizable total for one checksum
+ *	use  db_sts.hash, db_sts.rcd2, and *rcd_st */
+static DCC_TGTS				/* DCC_TGTS_INVALID=broken database */
+sum_total(DCC_CK_TYPES type,		/* look for this */
+	  const DCC_SUM sum,
+	  u_char must_have_it,
+	  DB_STATE *rcd_st,		/* starting here */
+	  const DB_RCD_CK *found_ck,
+	  u_char *undelay_ok,		/* 0=cannot undelay by clearing bit */
+	  DB_PTR *sum_oldest)
+{
+	DB_PTR prev;
+	DCC_TGTS rcd_tgts, sub_total;
+	int limit;
+
+	if (!rcd_st) {
+		if (!get_ck_tgts(0, &found_ck, must_have_it, type, sum))
+			return DCC_TGTS_INVALID;
+		if (!found_ck)
+			return 0;
+		rcd_st = &db_sts.rcd2;
+	}
+
+	if (sum_oldest)
+		*sum_oldest = DB_PTR_MAX;
+	sub_total = 0;
+	for (limit = 10000; limit >= 0; --limit) {
+		/* stop adding reports at the first summary or
+		 * compressed record in the hash chain */
+		if (DB_RCD_SUMRY(rcd_st->d.r)
+		    || DB_RCD_ID(rcd_st->d.r) == DCC_ID_COMP)
+			break;
+
+		/* honor deletions */
+		rcd_tgts = DB_TGTS_RCD(rcd_st->d.r);
+		if (rcd_tgts == 0)
+			break;
+
+		/* We can only summarize our own delayed reports
+		 * to keep loops in the flooding topology from
+		 * inflating totals. */
+		if (DB_RCD_DELAY(rcd_st->d.r)
+		    && DB_RCD_ID(rcd_st->d.r) == my_srvr_id) {
+			if (sum_oldest)
+				*sum_oldest = rcd_st->s.rptr;
+			sub_total = db_sum_ck(sub_total, rcd_tgts, type);
+			/* if we summarize more than one record,
+			 * then we cannot simply convert the record */
+			if (undelay_ok
+			    && db_sts.sumrcd.s.rptr != rcd_st->s.rptr)
+				*undelay_ok = 0;
+		}
+		prev = DB_PTR_EX(found_ck->prev);
+		if (prev == DB_PTR_NULL)
+			break;
+		rcd_st = &db_sts.rcd2;
+		found_ck = db_map_rcd_ck(dcc_emsg, rcd_st, prev, type);
+		if (!found_ck) {
+			DB_ERROR_MSG(dcc_emsg);
+			return 0;
+		}
+	}
+
+	return sub_total;
+}
+
+
+
+/* generate a summary record of checksum counts
+ *	db_sts.sumrcd points to the record being summarize on entry
+ *	    On exit db_sts.sumrcd points to the same record or the original
+ *	    has been trashed and db_sts.sumrcd points to a moved copy.
+ *	Use db_sts.rcd, db_sts.hash, db_sts.rcd2, db_sts.free, db_sts.tmp */
+static u_char				/* 0=sick db, 1=ok, 2=moved rcd */
+summarize_rcd(u_char dly)		/* 1=working on delayed records */
+{
+	DB_RCD new;
+	DCC_TGTS rcd_tgts, ck_tgts, new_tgts, sub_total;
+	DCC_CK_TYPES type;
+	DB_RCD_CK *cur_ck, *new_ck;
+	int cur_num_cks;
+	u_char ck_needed;		/* 0=junk cksum, 2=needed in new rcd */
+	u_char rcd_needed;		/* 1=have created rcd to add */
+	u_char undelay_ok;		/* 1=ok to remove delay bit */
+	u_char move_ok;
+	DB_PTR sum_oldest;
+	DB_PTR rcd_pos;
+
+	if (db_lock() < 0)
+		return 0;
+
+	/* For each checksum whose flooding was delayed but is now needed,
+	 * generate a fake record that will be flooded */
+	cur_num_cks = DB_NUM_CKS(db_sts.sumrcd.d.r);
+	cur_ck = db_sts.sumrcd.d.r->cks;
+	new_tgts = 0;
+	undelay_ok = (FLODS_OK()
+		      && (DB_RCD_ID(db_sts.sumrcd.d.r) == my_srvr_id));
+	move_ok = 1;
+	rcd_needed = 0;
+	new_ck = new.cks;
+	do {
+		/* Sum counts of all delayed reports for this checksum */
+		type = DB_CK_TYPE(cur_ck);
+		if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+			continue;
+
+		ck_needed = DB_CK_OBS(cur_ck) ? 0 : 1;
+
+		/* skip trudging through the hash table to find the
+		 * most recent instance of the checksum if we
+		 * are dealing with a new record and so already
+		 * have the most recent instance. */
+		sub_total = sum_total(type, cur_ck->sum, 1,
+				      !dly ? &db_sts.sumrcd : 0,
+				      cur_ck, &undelay_ok, &sum_oldest);
+		if (sub_total == DCC_TGTS_INVALID)
+			return 0;
+
+		/* Deletions and summaries between our record and the start
+		 * of the hash chain remove the need to flood this checkusm */
+		if (sub_total == 0) {
+			/* skipping a checksum in the original
+			 * record makes it impossible to move it */
+			move_ok = 0;
+			continue;
+		}
+
+		if (ck_needed == 1) {
+			ck_tgts = DB_TGTS_CK(cur_ck);
+			if (dly) {
+				/* Flood only 1 summary per delay period */
+				if ((flod_mmaps == 0
+				     || sum_oldest <= flod_mmaps->delay_pos)
+				    && ck_tgts >= flod_tholds[type])
+					ck_needed = 2;
+			} else {
+				/* We are considering the need for a summary
+				 * based on a report just received from a client
+				 * or by flooding */
+				if (quick_sum_thold(type, sub_total, ck_tgts))
+					ck_needed = 2;
+			}
+		}
+
+		if (new_ck != new.cks) {
+			/* We have already begun a summary record. */
+
+			if (sub_total == new_tgts) {
+				/* extend it with this checksum even if we do
+				 * not really need to flood this checksum */
+				new_ck->type_fgs = type;
+				memcpy(new_ck->sum, cur_ck->sum,
+				       sizeof(new_ck->sum));
+				++new.fgs_num_cks;
+				++new_ck;
+				if (ck_needed == 2)
+					rcd_needed = 1;
+				continue;
+			}
+			/* We cannot extend the current summary record. */
+
+			/* If we don't really need the checksum,
+			 * then forget the checksum. */
+			if (ck_needed != 2) {
+				/* skipping a checksum in the original
+				 * record makes it impossible to move */
+				move_ok = 0;
+				continue;
+			}
+
+			/* Add the current summary record to the database if
+			 * it is needed. */
+			if (rcd_needed) {
+				if (!db_add_rcd(dcc_emsg, &new)) {
+					DB_ERROR_MSG(dcc_emsg);
+					return 0;
+				}
+			}
+			/* start a new summary with this checksum. */
+			rcd_needed = 0;
+			/* having added one summary record,
+			 * we cannot undelay or move the original record */
+			undelay_ok = 0;
+		}
+
+		/* start a new summary record */
+		new.srvr_id_auth = my_srvr_id;
+		get_ts(&new.ts);
+		new_tgts = sub_total;
+		DB_TGTS_RCD_SET(&new, new_tgts);
+		new.fgs_num_cks = DB_RCD_FG_SUMRY+1;
+		new_ck = new.cks;
+		new_ck->type_fgs = type;
+		memcpy(new_ck->sum, cur_ck->sum, sizeof(new_ck->sum));
+		++new_ck;
+		if (ck_needed == 2)
+			rcd_needed = 1;
+	} while (++cur_ck, --cur_num_cks > 0);
+
+	/* finished if nothing more to summarize */
+	if (!rcd_needed) {
+		return 1;
+	}
+
+	/* Add the last summary record */
+	if (undelay_ok) {
+		/* If possible, instead of adding a new record,
+		 * change the preceding record to not be delayed
+		 * That is possible if the preceding record has
+		 * not yet been passed by the flooding */
+		if (db_sts.sumrcd.s.rptr >= oflods_max_cur_pos
+		    && oflods_max_cur_pos != 0) {
+			db_sts.sumrcd.d.r->fgs_num_cks &= ~DB_RCD_FG_DELAY;
+			SET_FLUSH_RCD_HDR(&db_sts.sumrcd, 1);
+			return 1;
+		}
+
+		/* failing that, try to move the record by making a new copy
+		 * and deleting the original */
+		if (move_ok) {
+			/* make the new record */
+			memcpy(&new, db_sts.sumrcd.d.r, DB_RCD_LEN(&new));
+			new.fgs_num_cks &= ~DB_RCD_FG_DELAY;
+
+			/* delete the old record */
+			DB_TGTS_RCD_SET(db_sts.sumrcd.d.r, 0);
+
+			/* adjust the totals in the old record so
+			 * that the totals in the new record will be right */
+			rcd_tgts = DB_TGTS_RCD(&new);
+			cur_num_cks = DB_NUM_CKS(db_sts.sumrcd.d.r);
+			cur_ck = db_sts.sumrcd.d.r->cks;
+			do {
+				new_tgts = DB_TGTS_CK(cur_ck);
+				if (new_tgts >= DCC_TGTS_TOO_MANY)
+					    continue;
+				if (new_tgts != 0)
+					new_tgts -= rcd_tgts;
+				DB_TGTS_CK_SET(cur_ck, new_tgts);
+			}  while (++cur_ck, --cur_num_cks > 0);
+			SET_FLUSH_RCD_HDR(&db_sts.sumrcd, 1);
+
+			rcd_pos = db_add_rcd(dcc_emsg, &new);
+			if (rcd_pos == DB_PTR_NULL) {
+				DB_ERROR_MSG(dcc_emsg);
+				return 0;
+			}
+			if (!db_map_rcd(dcc_emsg, &db_sts.sumrcd, rcd_pos, 0)) {
+				DB_ERROR_MSG(dcc_emsg);
+				return 0;
+			}
+			return 1;
+		}
+	}
+
+	if (!db_add_rcd(dcc_emsg, &new)) {
+		DB_ERROR_MSG(dcc_emsg);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+/* generate a delayed summary for checksums in a record if necessary
+ *	The target record is specified by db_sts.sumrcd.  It might be changed
+ *	Use db_sts.hash and db_sts.rcd2 */
+u_char
+summarize_dly(void)
+{
+	DCC_CK_TYPES type;
+	const DB_RCD_CK *cur_ck;
+	int cur_num_cks;
+	DCC_TGTS ck_tgts;
+
+	/* look for a checksum that could be summarized */
+	cur_num_cks = DB_NUM_CKS(db_sts.sumrcd.d.r);
+	cur_ck = db_sts.sumrcd.d.r->cks;
+	do {
+		type = DB_CK_TYPE(cur_ck);
+		if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+			continue;
+
+		if (!get_ck_tgts(&ck_tgts, 0, 1, type, cur_ck->sum))
+			return 0;
+
+		/* nothing to do if the checksum has already been summarized */
+		if (DB_RCD_SUMRY(db_sts.rcd2.d.r))
+			continue;
+
+		/* spam reports are ignored or not delayed */
+		if (ck_tgts == DCC_TGTS_TOO_MANY)
+			continue;
+
+		/* Generate a summary for a bulk checksum
+		 * Records that are marked "delayed" are not flooded.
+		 * If a summary record is not synthesized and if the delay
+		 * marking not removed (instead of synthesizing a summary),
+		 * then the counts for a checksum will not be flooded. */
+
+		if (ck_tgts >= flod_tholds[type])
+			return summarize_rcd(1);
+	}  while (++cur_ck, --cur_num_cks > 0);
+
+	return 1;
+}
+
+
+
+/* See if passing on a flooded report would be worthwhile.  It is worthwhile
+ *	to pass on reports of spam that have not been flooded recently
+ *	and of checksums that not yet or just barely reached spam.
+ *
+ *	db_sts.sumrcd points to the new record */
+static u_char				/* 0=sick database */
+flod_worth(u_char *pflod,		/* set =1 if report should be flooded */
+	   const DB_RCD_CK *ck,
+	   DCC_CK_TYPES type)
+{
+	DCC_TS past;
+	DCC_TGTS total;
+	int limit;
+	DB_PTR prev;
+
+	/* if the total with the new report is small,
+	 * then we should flood it */
+	total = DB_TGTS_CK(ck);
+	if (total < REFLOOD_THRESHOLD) {
+		/* but only if it is not trivial.
+		 * our neighbors should not send trivial reports,
+		 * but bugs happen */
+		if (total >= BULK_THRESHOLD/2)
+			*pflod = 1;
+		return 1;
+	}
+
+	/* Look for a recent report for this checksum that has been
+	 * or will be flooded.  If we find one, and if the total
+	 * including it is large enough, we may not need to flood
+	 * the incoming report.  If the total is too small, we
+	 * must flood the report. */
+	dcc_timeval2ts(&past, &db_time, -summarize_delay_secs);
+	for (limit = 20; limit >= 0; --limit) {
+		prev = DB_PTR_EX(ck->prev);
+		if (prev == DB_PTR_NULL)
+			break;
+		ck = db_map_rcd_ck(dcc_emsg, &db_sts.rcd2, prev, type);
+		if (!ck) {
+			DB_ERROR_MSG(dcc_emsg);
+			return 0;
+		}
+
+		/* if the previous total was small,
+		 * then we must flood the new report */
+		total = DB_TGTS_CK(ck);
+		if (total < REFLOOD_THRESHOLD*4) {
+			*pflod = 1;
+			return 1;
+		}
+
+		/* The old total is large.
+		 * If this found old report is not very old and good,
+		 * we will flood it and so the newest needed not be flooded
+		 * and can be marked obsolete. */
+		if (!DB_CK_OBS(ck)
+		    && dcc_ts_newer_ts(&db_sts.rcd2.d.r->ts, &past))
+			return 1;
+	}
+
+	/* flood this one if we can't find a recent preceding report */
+	*pflod = 1;
+	return 1;
+}
+
+
+
+/* Add a record and deal with delaying its flooding.
+ *	We will delay flooding it if its totals are not interesting.
+ *	db_sts.sumrcd points to the new record on exit
+ *	Use db_sts.rcd, db_sts.hash, db_sts.rcd2, db_sts.free, db_sts.tmp
+ *	the database must be locked */
+u_char					/* 1=ok, delayed or not, 0=failure */
+add_dly_rcd(DB_RCD *new_rcd, u_char flod_in)
+{
+	DB_PTR rcd_pos;
+	int num_cks;
+	DB_RCD_CK *new_ck;
+	DCC_CK_TYPES type;
+	DCC_TGTS rpt_tgts, ck_tgts;
+	u_char flod_out;	/* 0=flooded in but not worth flooding out */
+	u_char useful = 0;		/* 1=worth delaying */
+	u_char summarize = 0;
+
+	/* put the record in the database */
+	rcd_pos = db_add_rcd(dcc_emsg, new_rcd);
+	if (rcd_pos == DB_PTR_NULL) {
+		DB_ERROR_MSG(dcc_emsg);
+		return 0;
+	}
+	if (!db_map_rcd(dcc_emsg, &db_sts.sumrcd, rcd_pos, 0)) {
+		DB_ERROR_MSG(dcc_emsg);
+		return 0;
+	}
+
+	/* delete requests should not be delayed */
+	rpt_tgts = DB_TGTS_RCD_RAW(db_sts.sumrcd.d.r);
+	if (rpt_tgts == DCC_TGTS_DEL)
+		return 1;
+
+	/* we always consider flooding our own reports
+	 * and the greylist thresholds are zilch */
+	flod_out = !flod_in || grey_on;
+
+	for (num_cks = DB_NUM_CKS(db_sts.sumrcd.d.r),
+	     new_ck = db_sts.sumrcd.d.r->cks;
+	     num_cks > 0;
+	     ++new_ck, --num_cks) {
+		/* ingore already obsolete reports of spam */
+		if (DB_CK_OBS(new_ck))
+			continue;
+		/* ignore checksums we won't keep and so won't be flooded */
+		type = DB_CK_TYPE(new_ck);
+		if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+			continue;
+
+		/* Server-ID declarations cannot be summarized and should
+		 * not be delayed. */
+		if (type == DCC_CK_SRVR_ID) {
+			flod_out = 1;
+			break;
+		}
+
+		ck_tgts = DB_TGTS_CK(new_ck);
+		if (ck_tgts == DCC_TGTS_TOO_MANY) {
+			/* This checksum has a total of TOO_MANY and so
+			 * either the report has a target count of TOO_MANY
+			 * or is a report of a checksum already known to
+			 * be spam.  Since this report of this checksum
+			 * was not marked obsolete as it was linked into the
+			 * database, it should not be delayed.  */
+			if (rpt_tgts == DCC_TGTS_TOO_MANY) {
+				/* if the report is of spam, then all of its
+				 * individual checksum totals will be
+				 * DCC_TGTS_TOO_MANY.  The checksums will be
+				 * obsolete, not kept, or the same as this.
+				 * There will be no reputation checksums. */
+				return 1;
+			}
+			/* it is worth sending on even if was not ours */
+			flod_out = 1;
+			continue;
+		}
+
+		/* This report has some potential value and should be delayed
+		 * instead of forgotten */
+		useful = 1;
+
+		/* Summarize our records for the checksums in this record
+		 * if we just passed the threshold for one checksum. */
+		if (!summarize
+		    && quick_sum_thold(type, rpt_tgts, ck_tgts))
+			summarize = 1;
+
+		/* If this is an incoming flooded checksum,
+		 * then pass it on if it is novel (has a low total)
+		 * or if we have not passed it on recently. */
+		if (!flod_out
+		    && !flod_worth(&flod_out, new_ck, type))
+			return 0;	/* broken database */
+	}
+
+	/* Reports that are reports of spam or "trimmed" or "obsolete"
+	 * noise should not be summarized or marked to be delayed.
+	 * They will be flooded or skipped by the flooder */
+	if (!useful)
+		return 1;
+
+	if (!flod_in) {
+		/* Delay and sooner or later summarize our own
+		 * reports of non-spam */
+		db_sts.sumrcd.d.r->fgs_num_cks |= DB_RCD_FG_DELAY;
+
+	} else if (!flod_out) {
+		/* We are dealing with a report flooded in from another
+		 * server that is not (yet?) worth flooding out.
+		 * We can't delay it, because we can't delay reports from
+		 * other servers, because we cannot summarize them.
+		 * Summarizing other servers' reports would allow
+		 * loops in the flooding topology to inflate the totals.
+		 * So mark it to be expired but not delayed. */
+		for (num_cks = DB_NUM_CKS(db_sts.sumrcd.d.r),
+		     new_ck = db_sts.sumrcd.d.r->cks;
+		     num_cks > 0;
+		     ++new_ck, --num_cks) {
+			new_ck->type_fgs |= DB_CK_FG_OBS;
+		}
+	}
+
+	/* If this record pushed us past a threshold for at least one
+	 * checksum, then try to generate a summary of our own previously
+	 * delayed reports even if this record was not our own. */
+	if (summarize
+	    && !summarize_rcd(0))
+		return 0;
+
+	return 1;
+}
+
+
+
+/* the database must be locked */
+static u_char
+add_del(const DCC_CK *del_ck)
+{
+	DB_RCD del_rcd;
+
+	memset(&del_rcd, 0, sizeof(del_rcd));
+	get_ts(&del_rcd.ts);
+	DB_TGTS_RCD_SET(&del_rcd, DCC_TGTS_DEL);
+	del_rcd.srvr_id_auth = my_srvr_id;
+	del_rcd.fgs_num_cks = 1;
+	del_rcd.cks[0].type_fgs = del_ck->type;
+	memcpy(del_rcd.cks[0].sum, del_ck->sum, sizeof(del_rcd.cks[0].sum));
+	if (!db_add_rcd(dcc_emsg, &del_rcd)) {
+		DB_ERROR_MSG2("add delete", dcc_emsg);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+static const DCC_CK *
+start_work(QUEUE *q)
+{
+	const DCC_CK *ck, *ck_lim;
+	DCC_CK_TYPES type, prev_type;
+	int num_cks;
+
+	num_cks = q->pkt_len - (sizeof(q->pkt.r) - sizeof(q->pkt.r.cks));
+	if (num_cks < 0
+	    || num_cks > ISZ(q->pkt.r.cks)
+	    || num_cks % sizeof(DCC_CK) != 0) {
+		forget_error(q, "packet length %d wrong for %s",
+			     q->pkt_len, from_id_ip(q, 1));
+		return 0;
+	}
+	num_cks /= sizeof(DCC_CK);
+
+	/* send previous answer if this is a retransmission */
+	if (ridc_get(q)) {
+		repeat_resp(q);
+		return 0;
+	}
+
+	if (db_failed_line)		/* be silent while database bad */
+		return 0;
+
+	ck = q->pkt.r.cks;
+	ck_lim = &q->pkt.r.cks[num_cks];
+
+	/* check each checksum */
+	for (prev_type = DCC_CK_INVALID; ck < ck_lim; ++ck, prev_type = type) {
+		if (ck->len != sizeof(*ck)) {
+			forget_error(q, "unknown checksum length %d%s",
+				     ck->len, from_id_ip(q, 0));
+			return 0;
+		}
+		/* requiring that the checksums be ordered makes it easy
+		 * to check for duplicates and for bogus long packets */
+		type = ck->type;
+		if (!DCC_CK_OK_DCC_CLNT(grey_on, type)) {
+			forget_error(q, "unknown checksum %s%s",
+				     DB_TYPE2STR(type), from_id_ip(q, 0));
+			return 0;
+		}
+		if (prev_type >= type) {
+			forget_error(q, "out of order %s checksum%s",
+				     DB_TYPE2STR(ck->type), from_id_ip(q, 0));
+			return 0;
+		}
+	}
+
+	if (db_lock() < 0) {
+		NORESP_EMSG(q);
+		return 0;
+	}
+
+	return ck_lim;
+}
+
+
+
+/* send the response and release q */
+static void
+fin_work(const QUEUE *q, DCC_HDR *answer)
+{
+	int delay_us;
+
+	/* send the response */
+	answer->op = DCC_OP_ANSWER;
+	send_resp(q, answer, 0);
+
+	/* update the average queue delay, unless it is crazy */
+	gettimeofday(&db_time, 0);
+	delay_us = tv_diff2us(&db_time, &q->answer);
+	if (delay_us < 0)
+		return;
+
+	update_q_delay();
+	q_delays[0].us += delay_us;
+	++q_delays[0].ops;
+}
+
+
+
+/* use only db_sts.hash and db_sts.rcd2
+ * release q on failure */
+static u_char
+make_answer(QUEUE *q,
+	    const DCC_CK *ck_lim,
+	    u_char have_rcd,		/* db_sts.sumrcd.d.r is new record */
+	    DCC_ANSWER *answer,
+	    DCC_TGTS gross_tgts,	/* total for this report, maybe MANY */
+	    DCC_TGTS* max_tgts)		/* statistics */
+{
+	const DCC_CK *ck;
+	DCC_TGTS c_tgts;		/* current count with this report */
+	DCC_TGTS p_tgts;		/* count before this report */
+	DCC_ANSWER_BODY_CKS *b;
+	DCC_CK_TYPES type;
+	const DB_RCD_CK *rcd_ck, *prev_rcd_ck;
+	int num_rcd_cks;
+	DB_PTR prev;
+	*max_tgts = 0;
+
+	if (have_rcd) {
+		rcd_ck = db_sts.sumrcd.d.r->cks;
+		num_rcd_cks = DB_NUM_CKS(db_sts.sumrcd.d.r);
+	} else {
+		num_rcd_cks = 0;
+		rcd_ck = 0;
+	}
+	b = answer->b;
+	for (ck = q->pkt.r.cks; ck < ck_lim; ++ck) {
+		type = ck->type;
+		if (num_rcd_cks > 0
+		    && type == DB_CK_TYPE(rcd_ck)) {
+			/* try to copy answer from report's new record */
+			c_tgts = DB_TGTS_CK(rcd_ck);
+			if (c_tgts < DCC_TGTS_TOO_MANY) {
+				p_tgts = c_tgts - gross_tgts;
+			} else if (prev = DB_PTR_EX(rcd_ck->prev),
+				   prev == DB_PTR_NULL) {
+				p_tgts = 0;
+			} else {
+				prev_rcd_ck = db_map_rcd_ck(dcc_emsg,
+							&db_sts.rcd2,
+							prev, type);
+				if (!prev_rcd_ck) {
+					DB_ERROR_MSG(dcc_emsg);
+					RIDC_BAD(q);
+					return 0;
+				}
+				p_tgts = DB_TGTS_CK(prev_rcd_ck);
+			}
+			--num_rcd_cks;
+			++rcd_ck;
+
+		} else {
+			if (!get_ck_tgts(&p_tgts, 0, 0, type, ck->sum)) {
+				NORESP_EMSG(q);
+				return 0;
+			}
+			if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type)) {
+				/* uninteresting checksums have no value
+				 * unless they are whitelisted */
+				c_tgts = p_tgts;
+				if (p_tgts == 0)
+					p_tgts = DCC_TGTS_INVALID;
+			} else {
+				c_tgts = db_sum_ck(p_tgts, gross_tgts, type);
+			}
+		}
+
+		b->c = htonl(c_tgts);
+		b->p = htonl(p_tgts);
+#ifdef DCC_PKT_VERSION5
+		if (q->pkt.hdr.pkt_vers <= DCC_PKT_VERSION5)
+			b = (DCC_ANSWER_BODY_CKS *)&b->p;
+		else
+#endif
+		++b;
+
+		if (*max_tgts < c_tgts
+		    && c_tgts <= DCC_TGTS_OK2) {
+			*max_tgts = c_tgts;
+			/* Complain about failures to whitelist by
+			 * trusted clients.  The main use of this is
+			 * to detect whitelisting failures of IP addresses
+			 * such as 127.0.0.1 for reputations, and those
+			 * matter only for known clients.  */
+			if ((p_tgts >= DCC_TGTS_OK)
+			    && !(q->flags & Q_FG_UNTRUSTED))
+				TMSG4(WLIST, "%s whitelisted %s %s%s",
+				      qop2str(q),
+				      DB_TYPE2STR(type),
+				      dcc_ck2str_err(type, ck->sum, 0),
+				      from_id_ip(q, 0));
+		}
+	}
+	answer->hdr.len = (sizeof(*answer) - sizeof(answer->b)
+			   + ((char *)b - (char *)answer->b));
+	return 1;
+}
+
+
+
+/* release q on failure
+ *	the database must be locked */
+static u_char
+do_report(QUEUE *q,
+	  DCC_TGTS tgts0, const DCC_CK *ck_lim,
+	  DCC_ANSWER *answer,
+	  DCC_TGTS *max_tgts)
+{
+	const DCC_CK *ck;
+	DCC_TGTS tgts;
+	DCC_TGTS gross_tgts;		/* DCC_TGTS_TOO_MANY if spam */
+	DB_PTR rcd_pos;
+	DB_RCD new;
+	DB_RCD_CK *new_ck;
+	DCC_CK_TYPES type;
+	char tgts_buf[DCC_XHDR_MAX_TGTS_LEN];
+
+	tgts = tgts0;
+	if (tgts & (DCC_TGTS_SPAM | DCC_TGTS_REP_SPAM)) {
+		tgts &= DCC_TGTS_MASK;
+		if (tgts == 0)
+			tgts = 1;
+		gross_tgts = DCC_TGTS_TOO_MANY;
+	} else if (tgts == DCC_TGTS_TOO_MANY) {
+		tgts = 1;
+		gross_tgts = DCC_TGTS_TOO_MANY;
+	} else if (tgts > DCC_TGTS_RPT_MAX) {
+		forget_error(q, "bogus target count %s%s",
+			     dcc_tgts2str(tgts_buf, sizeof(tgts_buf),
+					  tgts, grey_on),
+			     from_id_ip(q, 0));
+		return 0;
+	} else {
+		gross_tgts = tgts;
+	}
+
+	if (gross_tgts < 10) {
+		;
+	} else if (gross_tgts == DCC_TGTS_TOO_MANY) {
+		++dccd_stats.reportmany;
+	} else if (gross_tgts > 1000) {
+		++dccd_stats.report1000;
+	} else if (gross_tgts > 100) {
+		++dccd_stats.report100;
+	} else if (gross_tgts > 10) {
+		++dccd_stats.report10;
+	}
+
+	/* Get ready to add the report to the database,
+	 * and as a side effect, find the data to answer the query.
+	 * Start by creating the record to add to the database. */
+	get_ts(&new.ts);
+	new.srvr_id_auth = my_srvr_id;
+	DB_TGTS_RCD_SET(&new, gross_tgts);
+
+	/* copy checksums to the new record */
+	new.fgs_num_cks = 0;
+	new_ck = new.cks;
+	for (ck = q->pkt.r.cks; ck < ck_lim; ++ck) {
+		type = ck->type;
+		if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type))
+			continue;
+		memcpy(new_ck->sum, ck->sum, sizeof(new_ck->sum));
+		new_ck->type_fgs = type;
+		++new_ck;
+		++new.fgs_num_cks;
+	}
+
+	if (!(q->flags & Q_FG_RPT_OK)) {
+		/* finished if this is a query */
+		return make_answer(q, ck_lim, 0, answer, gross_tgts,
+				   max_tgts);
+	}
+
+	if (new.fgs_num_cks == 0) {
+		rcd_pos = DB_PTR_NULL;
+	} else {
+		/* Add the record to the database.
+		 * That will update the totals for each checksum */
+		if (!add_dly_rcd(&new, 0)) {
+			NORESP_EMSG(q);
+			return 0;
+		}
+		rcd_pos = db_sts.sumrcd.s.rptr;
+	}
+
+	/* generate the response, perhaps from the new record */
+	return make_answer(q, ck_lim, rcd_pos!=DB_PTR_NULL, answer, gross_tgts,
+			   max_tgts);
+}
+
+
+
+/* process a single real request */
+void
+do_work(QUEUE *q)
+{
+	const DCC_CK *ck_lim;
+	DCC_ANSWER answer;
+	DCC_TGTS max_tgts, tgts;
+
+	ck_lim = start_work(q);
+	if (!ck_lim)
+		return;
+
+	tgts = 0;
+	switch (q->pkt.hdr.op) {
+	case DCC_OP_QUERY:
+		++dccd_stats.queries;
+		q->flags &= ~Q_FG_RPT_OK;
+		break;
+
+	case DCC_OP_REPORT:
+		if (!(q->flags & Q_FG_RPT_OK)) {
+			++dccd_stats.report_reject;
+			clnt_msg(q, "treat %s as query", from_id_ip(q, 1));
+			++dccd_stats.queries;
+		} else {
+			tgts = ntohl(q->pkt.r.tgts);
+			++dccd_stats.reports;
+		}
+		break;
+
+	case DCC_OP_INVALID:
+	case DCC_OP_NOP:
+	case DCC_OP_ANSWER:
+	case DCC_OP_ADMN:
+	case DCC_OP_OK:
+	case DCC_OP_ERROR:
+	case DCC_OP_DELETE:
+	case DCC_OP_GREY_REPORT:
+	case DCC_OP_GREY_QUERY:
+	case DCC_OP_GREY_SPAM:
+	case DCC_OP_GREY_WHITE:
+		dcc_logbad(EX_SOFTWARE, "impossible queued operation");
+		break;
+	}
+
+	if (!do_report(q, tgts, ck_lim, &answer,
+		       &max_tgts)) {
+		/* ensure that the clock ticks so rate limits don't stick */
+		gettimeofday(&db_time, 0);
+	} else {
+		/* notice the size of our answer */
+		if (max_tgts == DCC_TGTS_OK || max_tgts == DCC_TGTS_OK2) {
+			++dccd_stats.respwhite;
+		} else if (max_tgts == DCC_TGTS_TOO_MANY) {
+			++dccd_stats.respmany;
+		} else if (max_tgts > 1000) {
+			++dccd_stats.resp1000;
+		} else if (max_tgts > 100) {
+			++dccd_stats.resp100;
+		} else if (max_tgts > 10) {
+			++dccd_stats.resp10;
+		}
+
+		fin_work(q, &answer.hdr);
+	}
+}
+
+
+
+/* return 0 for a new embargo,
+ *	embargo count	for an existing embargo,
+ *	DCC_TGTS_TOO_MANY   no embargo
+ *	DCC_TGTS_OK	    a newly expired embargo
+ *	DCC_TGTS_INVALID    broken database */
+static DCC_TGTS
+search_grey(const DCC_CK *req_ck3,	/* triple checksum */
+	    const DCC_CK *req_ckb,	/* body seen with it */
+	    u_char body_known)
+{
+	DB_RCD_CK *ck3, *ckb;
+	DB_PTR prev3;
+	DCC_TS old_ts;
+	DCC_TGTS result_tgts;
+	int i;
+
+	/* look for the triple checksum */
+	switch (db_lookup(dcc_emsg, DCC_CK_GREY3, req_ck3->sum,
+			  0, MAX_HASH_ENTRIES,
+			  &db_sts.hash, &db_sts.rcd, &ck3)) {
+	case DB_FOUND_EMPTY:
+	case DB_FOUND_CHAIN:
+	case DB_FOUND_INTRUDER:
+		return 0;
+
+	case DB_FOUND_IT:
+		/* We found the triple checksum.
+		 * If it is marked ok (MANY) or deleted,
+		 * then we have our answer */
+		result_tgts = DB_TGTS_CK(ck3);
+		if (result_tgts == DCC_TGTS_TOO_MANY || result_tgts == 0)
+			return result_tgts;
+
+		/* Otherwise look for a report of the triple with
+		 * the right body checksum that is old enough. */
+		result_tgts = 0;
+		dcc_timeval2ts(&old_ts, &db_time, -grey_embargo);
+		for (;;) {
+			ckb = db_sts.rcd.d.r->cks;
+			for (i = DB_NUM_CKS(db_sts.rcd.d.r);
+			     i > 0;
+			     --i, ++ckb) {
+				/* try the next report in the database
+				 * if it has the wrong body checksum
+				 *
+				 * If we are weak on bodies,
+				 * act as if all reports of the triple checksums
+				 * are with the right body checksum. */
+				if (!grey_weak_body && req_ckb) {
+					if (DB_CK_TYPE(ckb) != DCC_CK_BODY)
+					    continue;
+					if (memcmp(req_ckb->sum, ckb->sum,
+						   sizeof(DCC_SUM)))
+					    break;
+				}
+
+				/* We found the right body checksum in
+				 * chain of the triple checksum
+				 * or we don't care.
+				 *
+				 * If the report is old enough, then
+				 * the embargo is over. */
+				if (dcc_ts_newer_ts(&old_ts,
+						    &db_sts.rcd.d.r->ts))
+					return DCC_TGTS_OK;
+
+				/* If it is not old enough,
+				 * then we know this is not a new embargo for
+				 * this body (i.e. the reported target count
+				 * will be >0) and we must keep looking for an
+				 * old enough report with the body checksum. */
+				++result_tgts;
+				break;
+			}
+
+			/* If we know the body checksum is not in the database,
+			 * then there is no profit in looking at other reports
+			 * of the triple checksum to try to find an old enough
+			 * report that is with the right body checksum.
+			 * We know this is a new embargo. */
+			if (!body_known)
+				return 0;
+
+			/* If we reach the end of the chain of the
+			 * triple checksum without finding an old
+			 * enough report for the right body,
+			 * then the embargo is not over. */
+			prev3 = DB_PTR_EX(ck3->prev);
+			if (prev3 == DB_PTR_NULL)
+				return result_tgts;
+
+			/* examine the timestamp of the preceding report
+			 * of the triple */
+			ck3 = db_map_rcd_ck(dcc_emsg, &db_sts.rcd,
+					    prev3, DCC_CK_GREY3);
+			if (!ck3)
+				return DCC_TGTS_INVALID;
+		}
+		break;
+
+	case DB_FOUND_LATER:
+	case DB_FOUND_SYSERR:
+		DB_ERROR_MSG(dcc_emsg);
+		return DCC_TGTS_INVALID;
+	}
+	return DCC_TGTS_INVALID;
+}
+
+
+
+void
+do_grey(QUEUE *q)
+{
+	DCC_OPS op;
+	DB_RCD new;
+	const DCC_CK *req, *req_lim;
+	const DCC_CK *req_ck_ip, *req_ck_triple, *req_ck_msg, *req_ck_body;
+	u_char body_known;
+	DB_RCD_CK *new_ck, *found_ck;
+	DCC_GREY_ANSWER resp;
+	DCC_TGTS tgts;
+	DCC_TGTS ip_tgts;		/* existing count for DCC_CK_IP */
+	DCC_TGTS triple_tgts;		/*   "   count for GREY_TRIPLE */
+	DCC_TGTS msg_tgts;		/*   "   count for GREY_MSG */
+	DCC_TGTS eff_msg_tgts;		/* effective value: 0=reported to DCC */
+	DCC_TGTS new_msg_tgts;		/* value after this */
+	DCC_TGTS result_tgts;		/* no embargo, ending, whitelist or # */
+
+	TMSG1(QUERY, "received %s", op_id_ip(q));
+	if (!ck_clnt_id(q))
+		return;
+	if (q->flags & Q_FG_UNTRUSTED) {
+		anon_msg("drop %s", from_id_ip(q, 1));
+		return;
+	}
+
+	/* an embargo of 0 seconds means we should only collect names */
+	op = q->pkt.hdr.op;
+	if (op == DCC_OP_GREY_REPORT && grey_embargo == 0)
+		op = DCC_OP_GREY_WHITE;
+
+	req_lim = start_work(q);
+	if (!req_lim)
+		return;
+
+	/* Require
+	 *	the body checksum,
+	 *	the checksum of the (body,sender,target),
+	 *	and the checksum of the (source,sender,target) triple.
+	 * Allow other checksums for whitelisting. */
+	ip_tgts = 0;
+	body_known = grey_weak_body;
+	req_ck_ip = 0;
+	req_ck_body = 0;
+	req_ck_triple = 0;
+	req_ck_msg = 0;
+	msg_tgts = eff_msg_tgts = 0;
+	for (req = q->pkt.r.cks; req < req_lim; ++req) {
+		/* Note our main checksums of the greylist triple and
+		 * the message body.  Search the database for it later */
+		if (DCC_CK_IS_GREY_TRIPLE(1, req->type)) {
+			req_ck_triple = req;
+			continue;
+		}
+
+		if (!DCC_CK_OK_GREY_CLNT(req->type))
+			continue;	/* ignore unknown checksums */
+		switch (req->type) {
+		case DCC_CK_IP:
+			req_ck_ip = req;
+			break;
+		case DCC_CK_BODY:
+			req_ck_body = req;
+			break;
+		case DCC_CK_GREY_MSG:
+			req_ck_msg = req;
+			break;
+		}
+		/* check for whitelisting and whether this is a new embargo */
+		switch (db_lookup(dcc_emsg, req->type, req->sum,
+				  0, MAX_HASH_ENTRIES,
+				  &db_sts.hash, &db_sts.rcd, &found_ck)) {
+		case DB_FOUND_LATER:
+		case DB_FOUND_SYSERR:
+			DB_ERROR_MSG(dcc_emsg);
+			RIDC_BAD(q);
+			return;
+		case DB_FOUND_IT:
+			/* ignore deleted checksums */
+			tgts = DB_TGTS_CK(found_ck);
+			if (tgts == 0)
+				continue;
+
+			/* honor whitelisting */
+			if (tgts == DCC_TGTS_GREY_WHITE
+			    && op != DCC_OP_GREY_WHITE) {
+				op = DCC_OP_GREY_WHITE;
+				++dccd_stats.respwhite;
+			}
+
+			switch (req->type) {
+			case DCC_CK_BODY:
+				/* notice if the target body exists at all */
+				body_known = 1;
+				break;
+			case DCC_CK_GREY_MSG:
+				msg_tgts = tgts;
+				if (msg_tgts != DCC_TGTS_TOO_MANY) {
+					/* this is an old embargo that has
+					 * already been reported by the client
+					 * to a normal DCC server */
+					eff_msg_tgts = 1;
+				}
+				break;
+			case DCC_CK_IP:
+				ip_tgts = tgts;
+				break;
+			default:
+				break;
+			}
+			break;
+		case DB_FOUND_EMPTY:
+		case DB_FOUND_CHAIN:
+		case DB_FOUND_INTRUDER:
+			break;
+		}
+	}
+	if (!req_ck_triple) {
+		send_error(q, "missing %s checksum for %s",
+			   DB_TYPE2STR(DCC_CK_GREY3), qop2str(q));
+		return;
+	}
+	if (op == DCC_OP_GREY_REPORT && !grey_weak_body) {
+		if (!req_ck_body) {
+			send_error(q, "missing body checksum for %s",
+				   qop2str(q));
+			return;
+		}
+		if (!req_ck_msg) {
+			send_error(q, "missing %s checksum for %s",
+				   DB_TYPE2STR(DCC_CK_GREY_MSG), qop2str(q));
+			return;
+		}
+	}
+
+	/* decide if the embargo should end */
+	triple_tgts = search_grey(req_ck_triple, req_ck_body, body_known);
+	if (triple_tgts == DCC_TGTS_INVALID) {
+		NORESP_EMSG(q);		/* broken database */
+		return;
+	}
+	/* End existing embargo on a newly whitelisted sender so its
+	 *	messages are logged.
+	 * Quietly prevent future embargos of whitelisted senders that have
+	 *	not been greylisted.
+	 * Honor grey_weak_ip whitelisting even after it is turned off */
+	if (triple_tgts >= DCC_TGTS_TOO_MANY) {
+		result_tgts = triple_tgts;
+	} else if (op == DCC_OP_GREY_WHITE) {
+		result_tgts = eff_msg_tgts ? DCC_TGTS_OK : DCC_TGTS_TOO_MANY;
+	} else if (ip_tgts == DCC_TGTS_TOO_MANY) {
+		result_tgts = DCC_TGTS_TOO_MANY;
+	} else {
+		result_tgts = triple_tgts;
+	}
+
+	if (op == DCC_OP_GREY_QUERY) {
+		++dccd_stats.queries;
+
+	} else if (!(q->flags & Q_FG_RPT_OK)) {
+		++dccd_stats.report_reject;
+		clnt_msg(q, "treat %s as query", from_id_ip(q, 1));
+		++dccd_stats.queries;
+
+	} else {
+		/* add a report for this message */
+		++dccd_stats.reports;
+		new.srvr_id_auth = my_srvr_id;
+		new_ck = new.cks;
+		new.fgs_num_cks = 0;
+		if (result_tgts < DCC_TGTS_TOO_MANY) {
+			if (req_ck_body) {
+				new_ck->type_fgs = DCC_CK_BODY;
+				memcpy(new_ck->sum, req_ck_body->sum,
+				       sizeof(new_ck->sum));
+				++new.fgs_num_cks;
+				++new_ck;
+			}
+			new_msg_tgts = 1;
+			DB_TGTS_RCD_SET(&new, 1);
+		} else {
+			/* embargo now ending (DCC_TGTS_TOO_OK)
+			 * or no embargo (DCC_TGTS_TOO_MANY) */
+			if (grey_weak_ip && req_ck_ip) {
+				new_ck->type_fgs = DCC_CK_IP;
+				memcpy(new_ck->sum, req_ck_ip->sum,
+				       sizeof(new_ck->sum));
+				++new.fgs_num_cks;
+				++new_ck;
+			}
+			new_msg_tgts = 0;
+			DB_TGTS_RCD_SET(&new, DCC_TGTS_TOO_MANY);
+		}
+
+		/* Include the GREY_MSG checksum in the database
+		 * record for a new embargo.
+		 * The message checksum lets an SMTP server report an
+		 * embargoed message to the DCC before the embargo is over,
+		 * but not report it more than once even if more than one
+		 * SMTP client retransmits the message.
+		 *
+		 * If the GREY_MSG checksum does not exist in the
+		 * database, then tell the DCC client the message is new
+		 * and should be reported to the DCC server.  We must put the
+		 * the _GREY_MSG into the database so we will recognize
+		 * the message as not new when it is retransmitted.
+		 *
+		 * If the GREY_MSG checksum exists and is not MANY,
+		 * then we may have a retransmission of the message
+		 * from another IP address.
+		 * We need to tell the DCC client to not report to the
+		 * DCC server. The new value for the CK_GREY_MSG checksum
+		 * should be whatever we are using for the triple checksum.
+		 *
+		 * If the existing count for the GREY_MSG checksum is
+		 * MANY, and the new value for triple checksum is not MANY,
+		 * then we have a new copy of the message and a new embargo.
+		 * We have a spammer with multiple senders instead of a
+		 * legitimate multihomed SMTP client.  We need to tell the
+		 * DCC client to report to the DCC server.  To remember
+		 * that we told the DCC client to report to the DCC server,
+		 * we must first delete the existing MANY report of the
+		 * GREY_MSG checksum. */
+		if (eff_msg_tgts != new_msg_tgts
+		    && req_ck_msg) {
+			if (msg_tgts == DCC_TGTS_TOO_MANY
+			    && !add_del(req_ck_msg)) {
+				NORESP_EMSG(q);
+				return;
+			}
+			new_ck->type_fgs = DCC_CK_GREY_MSG;
+			memcpy(new_ck->sum, req_ck_msg->sum,
+			       sizeof(new_ck->sum));
+			++new.fgs_num_cks;
+			++new_ck;
+		}
+
+		/* Add the triple checksum if we are not whitelisting
+		 *	by the IP address
+		 * or triple checksum is not new.
+		 * We do not want to leave any dangling triples in the
+		 * database */
+		if (!(grey_weak_ip && req_ck_ip)
+		    || result_tgts != DCC_TGTS_TOO_MANY) {
+			new_ck->type_fgs = DCC_CK_GREY3;
+			memcpy(new_ck->sum, req_ck_triple->sum,
+			       sizeof(new_ck->sum));
+			++new.fgs_num_cks;
+		}
+
+		get_ts(&new.ts);
+		if (!db_add_rcd(dcc_emsg, &new)) {
+			DB_ERROR_MSG(dcc_emsg);
+			RIDC_BAD(q);
+			return;
+		}
+	}
+
+	/* In the result sent to the DCC client,
+	 * the triple checksum is preceeded by the message checksum
+	 * with a count of 0 if this is a new embargo.
+	 * Targets of messages of new embargos should be counted among
+	 * total targets in reports sent to DCC servers.  After they
+	 * have been included in such an early report to a DCC server,
+	 * they should never be included again, except for bad reputations. */
+	resp.msg = htonl(eff_msg_tgts);
+
+	/* Answer SMTP DATA command greylist operations with the target
+	 * count of the triple checksum:
+	 *	DCC_TGTS_OK if the embargo is just now being removed
+	 *	DCC_TGTS_TOO_MANY if there is no current embargo
+	 *	DCC_TGTS_GREY_WHITE if whitelisted.
+	 *	embargo # otherwise */
+	resp.triple = htonl(result_tgts);
+	resp.hdr.len = sizeof(resp);
+
+	fin_work(q, &resp.hdr);
+}
+
+
+
+static time_t
+picky_time(const QUEUE *q)
+{
+	time_t ts, delta;
+
+	/* If the request arrived while we were asleep, then the client's
+	 * timestamp ought to be smaller than when select() finished and
+	 * we think the request arrived. */
+	ts = ntohl(q->pkt.d.date);
+	delta = ts - q->answer.tv_sec;
+	if (delta <= 0)
+		return delta;
+
+	/* If the request arrived while we were handling some other request,
+	 * then its timestamp can be larger than the select() wake-up time
+	 * but should not be in the future. */
+	delta = ts - db_time.tv_sec;
+	if (delta < 0)
+		delta = 0;
+	return delta;
+}
+
+
+
+static u_char				/* 0=refuse the bad guy, 1=continue */
+picky_admn(const QUEUE *q, u_char any_id, u_char any_time)
+{
+	time_t delta;
+
+	if ((q->flags & Q_FG_UNTRUSTED)
+	    || (q->clnt_id != my_srvr_id && !any_id)) {
+		forget_error(q, "drop %s", from_id_ip(q, 1));
+		return 0;
+	}
+
+	if (any_id && any_time)
+		return 1;
+
+	/* Demand a current timestamp to guard against replay attacks.
+	 * This requires that administrators have clocks close to servers',
+	 * and that network and server delays be reasonable. */
+	delta = picky_time(q);
+	if (delta < -MAX_CMD_CLOCK_SKEW || delta > MAX_CMD_CLOCK_SKEW) {
+		send_error(q, "drop %s; timestamp off by %d seconds",
+			   qop2str(q), (int)delta);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+/* the database must be locked */
+static u_char				/* 1=ok, 0=error sent to client */
+delete_sub(QUEUE *q, DCC_CK *del_ck,
+	   u_char grey_spam)
+{
+	DB_RCD_CK *rcd_ck;
+	char buf[80];
+	DB_PTR prev;
+	DCC_TGTS tgts;
+
+	buf[0] = '\0';
+	switch (db_lookup(dcc_emsg, del_ck->type, del_ck->sum,
+			  0, MAX_HASH_ENTRIES,
+			  &db_sts.hash, &db_sts.rcd, &rcd_ck)) {
+	case DB_FOUND_EMPTY:
+	case DB_FOUND_CHAIN:
+	case DB_FOUND_INTRUDER:
+		/* finished if we have not greylisted the spammer */
+		if (grey_spam)
+			return 1;
+
+		/* ordinary deletions need a delete request added
+		 * to the database and flooded */
+		snprintf(buf, sizeof(buf), "\"%s %s\" not found to delete",
+			 DB_TYPE2STR(del_ck->type),
+			 dcc_ck2str_err(del_ck->type, del_ck->sum, 0));
+
+		if (del_ck->type == DCC_CK_SRVR_ID) {
+			send_error(q, "%s", buf);
+			return 0;
+		}
+		break;
+
+	case DB_FOUND_IT:
+		tgts = DB_TGTS_CK(rcd_ck);
+		/* handle an ordinary delete request */
+		if (!grey_spam) {
+			if (tgts == 0)
+				snprintf(buf, sizeof(buf),
+					 "%s %s already deleted",
+					 DB_TYPE2STR(del_ck->type),
+					 dcc_ck2str_err(del_ck->type,
+							del_ck->sum, 0));
+			break;
+		}
+		/* We are deleting a greylist checksum.
+		 * If we are deleting very new greylist records,
+		 * we can cheat and avoid adding to the database
+		 * by scribbling over the records.
+		 * If there is an older record that might have been flooded,
+		 * we must add a delete request to the database
+		 * that will itself be flooded. */
+		for (;;) {
+			/* finished if the target has already been deleted */
+			if (tgts == 0)
+				return 1;
+			if (db_sts.rcd.s.rptr < oflods_max_cur_pos
+			    || oflods_max_cur_pos == 0) {
+				/* We need to add a delete request, because
+				 * the record might have been flooded */
+				break;
+			}
+			prev = DB_PTR_EX(rcd_ck->prev);
+			/* try to delete the entire greylist entry
+			 * starting with the target triple checksum */
+			do {
+				/* only if the embargo is not over */
+				if (DB_TGTS_CK(rcd_ck) >= DCC_TGTS_TOO_MANY)
+					goto need_rcd;
+				DB_TGTS_CK_SET(rcd_ck, 0);
+			} while (--rcd_ck >= db_sts.rcd.d.r->cks);
+			DB_TGTS_RCD_SET(db_sts.rcd.d.r, 0);
+			SET_FLUSH_RCD_HDR(&db_sts.rcd, 1);
+
+			/* stop after the last record */
+			if (prev == DB_PTR_NULL)
+				return 1;
+
+			rcd_ck = db_map_rcd_ck(dcc_emsg, &db_sts.rcd,
+					       prev, del_ck->type);
+			if (!rcd_ck) {
+				NORESP_EMSG(q);
+				return 0;
+			}
+			tgts = DB_TGTS_CK(rcd_ck);
+		}
+need_rcd:;
+		break;
+
+	case DB_FOUND_LATER:
+	case DB_FOUND_SYSERR:
+		DB_ERROR_MSG(dcc_emsg);
+		RIDC_BAD(q);
+		return 0;
+	}
+
+	/* Add the delete request to the database even if the
+	 * checksum seems deleted or absent so that we will
+	 * flood the delete request.  This is required to ensure that
+	 * records get deleted when they are created at one DCC server
+	 * and deleted at another. */
+	if (!add_del(del_ck))
+		BUFCPY(buf, dcc_emsg);
+
+	if (buf[0] != '\0') {
+		send_error(q, "%s", buf);
+		return 0;
+	}
+
+	TMSG3(ADMN, "deleted %s %s%s",
+	      DB_TYPE2STR(del_ck->type),
+	      dcc_ck2str_err(del_ck->type, del_ck->sum, 0),
+	      from_id_ip(q, 0));
+	return 1;
+}
+
+
+
+void
+do_delete(QUEUE *q)
+{
+	if (!ck_clnt_srvr_id(q))
+		return;
+	if (!picky_admn(q, 0, 0))
+		return;
+	/* if we've already answered, then just repeat ourselves */
+	if (ridc_get(q)) {
+		repeat_resp(q);
+		return;
+	}
+
+	dcc_error_msg("received %s", op_id_ip(q));
+	++dccd_stats.admin;
+
+	if (q->pkt_len != sizeof(q->pkt.d)) {
+		send_error(q, "wrong packet length %d for %s",
+			   q->pkt_len, qop2str(q));
+		return;
+	}
+	if (q->pkt.d.ck.len != sizeof(q->pkt.d.ck)) {
+		send_error(q, "unknown checksum length %d", q->pkt.d.ck.len);
+		return;
+	}
+	if (!DCC_CK_OK_DB(grey_on, q->pkt.d.ck.type)) {
+		send_error(q, "unknown checkksum type %d", q->pkt.d.ck.type);
+		return;
+	}
+
+	if (db_lock() < 0) {
+		NORESP_EMSG(q);
+		return;
+	}
+	if (delete_sub(q, &q->pkt.d.ck, 0)) {
+		/* We need to clean the database after a deletion
+		 * to correct the totals of other checksums.
+		 * Don't bother for reputations or server-ID declarations. */
+		if (!DCC_CK_IS_REP_CMN(grey_on, q->pkt.d.ck.type)
+		    && q->pkt.d.ck.type != DCC_CK_SRVR_ID)
+			need_del_dbclean = "checksum deleted";
+
+		send_ok(q);
+	}
+}
+
+
+
+/* restore the embargo against a sender of spam */
+void
+do_grey_spam(QUEUE *q)
+{
+	TMSG1(QUERY, "received %s", op_id_ip(q));
+	if (!ck_clnt_id(q))
+		return;
+	if (q->flags & Q_FG_UNTRUSTED) {
+		anon_msg("drop %s", from_id_ip(q, 1));
+		return;
+	}
+
+	/* require the checksum of the (source,sender,target) triple */
+	if (q->pkt_len != sizeof(q->pkt.gs)) {
+		send_error(q, "wrong packet length %d for %s",
+			   q->pkt_len, qop2str(q));
+		return;
+	}
+	if (q->pkt.gs.triple.type != DCC_CK_GREY3) {
+		send_error(q, "%s instead of %s for %s",
+			   DB_TYPE2STR(q->pkt.gs.msg.type),
+			   DB_TYPE2STR(DCC_CK_GREY3),
+			   qop2str(q));
+		return;
+	}
+	if (q->pkt.gs.triple.len != sizeof(q->pkt.gs.triple)) {
+		send_error(q, "unknown triple checksum length %d",
+			   q->pkt.gs.ip.len);
+		return;
+	}
+	if (q->pkt.gs.msg.type != DCC_CK_GREY_MSG) {
+		send_error(q, "%s instead of %s for %s",
+			   DB_TYPE2STR(q->pkt.gs.msg.type),
+			   DB_TYPE2STR(DCC_CK_GREY_MSG),
+			   qop2str(q));
+		return;
+	}
+	if (q->pkt.gs.msg.len != sizeof(q->pkt.gs.msg)) {
+		send_error(q, "unknown msg checksum length %d",
+			   q->pkt.gs.ip.len);
+		return;
+	}
+	if (q->pkt.gs.ip.type != DCC_CK_IP) {
+		send_error(q, "%s instead of %s for %s",
+			   DB_TYPE2STR(q->pkt.gs.msg.type),
+			   DB_TYPE2STR(DCC_CK_IP),
+			   qop2str(q));
+		return;
+	}
+	if (q->pkt.gs.ip.len != sizeof(q->pkt.gs.ip)) {
+		send_error(q, "unknown IP checksum length %d",
+			   q->pkt.gs.ip.len);
+		return;
+	}
+
+	if (db_lock() < 0) {
+		NORESP_EMSG(q);
+		return;
+	}
+	if (delete_sub(q, &q->pkt.gs.ip, 1)
+	    && delete_sub(q, &q->pkt.gs.triple, 1)
+	    && delete_sub(q, &q->pkt.gs.msg, 1))
+		send_ok(q);
+}
+
+
+
+static void
+do_flod(QUEUE *q)
+{
+	DCC_ADMN_RESP check;
+	int print_len;
+	u_int32_t val, arg;
+	DCC_AOP_FLODS fop;
+	FLOD_MMAP *mp;
+	OFLOD_INFO *ofp;
+	u_char loaded, found_it;
+
+	val = ntohl(q->pkt.ad.val1);
+	fop = val % 256;
+	arg = val / 256;
+
+	if (fop != DCC_AOP_FLOD_LIST) {
+	    if (!picky_admn(q, fop == DCC_AOP_FLOD_STATS, 0))
+		    return;
+	}
+
+	switch (fop) {
+	case DCC_AOP_FLOD_CHECK:
+		/* `cdcc "flood check"` forces occasional defenses of
+		 * our server-ID */
+		if (host_id_next > db_time.tv_sec + 60)
+			host_id_next = db_time.tv_sec;
+
+		next_flods_ck = 0;
+		if (0 >= check_load_ids(0)) {
+			dcc_error_msg("%s", dcc_emsg);
+			send_error(q, "%s", dcc_emsg);
+			return;
+		}
+		flod_stats_printf(check.val.string, sizeof(check.val.string),
+				  (!FLODS_OK() || flods_st == FLODS_ST_OFF)
+				  ? 0
+				  : (flods_st != FLODS_ST_ON) ? 1
+				  : 2,
+				  oflods.total, oflods.open, iflods.open);
+		check.hdr.len = (strlen(check.val.string)
+				 + sizeof(check)-sizeof(check.val));
+		check.hdr.op = DCC_OP_ADMN;
+		send_resp(q, &check.hdr, 0);
+		flods_ck(1);
+		check_blacklist_file();
+		return;
+
+	case DCC_AOP_FLOD_SHUTDOWN:
+		if (ridc_get(q)) {
+			repeat_resp(q);
+			return;
+		}
+		++flods_off;
+		flods_stop("shutdown flooding", 0);
+		send_ok(q);
+		return;
+
+	case DCC_AOP_FLOD_HALT:
+		if (ridc_get(q)) {
+			repeat_resp(q);
+			return;
+		}
+		++flods_off;
+		flods_stop("stop flooding", 1);
+		send_ok(q);
+		return;
+
+	case DCC_AOP_FLOD_RESUME:
+		if (ridc_get(q)) {
+			repeat_resp(q);
+			return;
+		}
+		if (0 >= check_load_ids(0)) {
+			dcc_error_msg("%s", dcc_emsg);
+			send_error(q, "%s", dcc_emsg);
+			return;
+		}
+		if (flods_off) {
+			flods_off = 0;
+			flods_restart("resume flooding", 0);
+		}
+		send_ok(q);
+		flods_ck(0);
+		return;
+
+	case DCC_AOP_FLOD_REWIND:
+		if (ridc_get(q)) {
+			repeat_resp(q);
+			return;
+		}
+		if (flod_mmaps) {
+			loaded = 0;
+		} else if (!load_flod(0)) {
+			send_error(q, "too busy to rewind floods");
+			return;
+		} else {
+			loaded = 1;
+		}
+		found_it = (arg == DCC_ID_INVALID);
+		for (mp = flod_mmaps->mmaps;
+		     mp <= LAST(flod_mmaps->mmaps);
+		     ++mp) {
+			if (arg == DCC_ID_INVALID
+			    || mp->rem_id == arg) {
+				mp->flags |= FLODMAP_FG_NEED_REWIND;
+				mp->flags &= ~FLODMAP_FG_FFWD_IN;
+				dcc_trace_msg("rewind flood from server-ID %d",
+					      arg);
+				found_it = 1;
+			}
+		}
+		if (!found_it) {
+			send_error(q, "unknown server-ID %d for %s",
+				   arg, qop2str(q));
+		} else {
+			send_ok(q);
+			flods_ck(0);
+		}
+		if (loaded)
+			oflods_clear();
+		return;
+
+	case DCC_AOP_FLOD_LIST:
+		loaded = !flod_mmaps && load_flod(0);
+		if (flod_mmaps) {
+			print_len = flods_list(check.val.string,
+					       sizeof(check.val.string),
+					       (q->flags & Q_FG_UNTRUSTED)!=0);
+		} else {
+			/* it is not an error if map is locked, because
+			 * dbclean uses this operation to see if we are
+			 * listening */
+			print_len = snprintf(check.val.string,
+					     ISZ(check.val.string),
+					     "too busy to list floods");
+			if (print_len > ISZ(check.val.string))
+				print_len = ISZ(check.val.string);
+		}
+		check.hdr.len = (print_len
+				 + sizeof(check)-sizeof(check.val));
+		check.hdr.op = DCC_OP_ADMN;
+		send_resp(q, &check.hdr, 0);
+		if (loaded)
+			oflods_clear();
+		return;
+
+	case DCC_AOP_FLOD_STATS:
+	case DCC_AOP_FLOD_STATS_CLEAR:
+		print_len = flod_stats(check.val.string,
+				       sizeof(check.val.string),
+				       arg,
+				       fop == DCC_AOP_FLOD_STATS_CLEAR);
+		if (print_len < 0) {
+			send_error(q, "too busy to find flood stats");
+			return;
+		}
+		check.hdr.len = print_len + sizeof(check)-sizeof(check.val);
+		check.hdr.op = DCC_OP_ADMN;
+		send_resp(q, &check.hdr, 0);
+		flods_ck(0);
+		return;
+
+	case DCC_AOP_FLOD_FFWD_IN:
+	case DCC_AOP_FLOD_FFWD_OUT:
+		if (ridc_get(q)) {
+			repeat_resp(q);
+			return;
+		}
+		if (flod_mmaps) {
+			loaded = 0;
+		} else if (!load_flod(0)) {
+			send_error(q, "too busy to fast-forward floods");
+			return;
+		} else {
+			loaded = 1;
+		}
+		ofp = oflods.infos;
+		for (;;) {
+			mp = ofp->mp;
+			if (mp->rem_id == arg) {
+				/* found the target */
+				if (fop == DCC_AOP_FLOD_FFWD_OUT) {
+					ofp->cur_pos = db_csize;
+					if (ofp->soc < 0)
+					    mp->confirm_pos = db_csize;
+					dcc_trace_msg("fast forward flood to"
+						      " server-ID %d",
+						      arg);
+				} else {
+					mp->flags |= FLODMAP_FG_FFWD_IN;
+					mp->flags &= ~FLODMAP_FG_NEED_REWIND;
+				}
+				send_ok(q);
+				if (!loaded)
+					flods_ck(0);
+				break;
+			}
+			if (++ofp > LAST(oflods.infos)) {
+				send_error(q, "unknown server-ID %d for %s",
+					   arg, qop2str(q));
+				break;
+			}
+		}
+		if (loaded)
+			oflods_clear();
+		return;
+	}
+
+	send_error(q, "unrecognized %s value %d", qop2str(q), fop);
+}
+
+
+
+void
+stats_clear(void)
+{
+	OFLOD_INFO *ofp;
+
+	memset(&dccd_stats, 0, sizeof(dccd_stats));
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		if (ofp->rem_hostname[0] == '\0')
+			continue;
+
+		/* The counts reported to `cdcc stats` are sums
+		 * of the dccd_stats and ofp->cnts values.  Bias
+		 * the dccd_stats values by the current ofp->cnts values
+		 * so the reported counts will be zero.  When the flooding
+		 * connection is closed, the ofp->cnts values will be added
+		 * to the dccd_stats values. */
+		dccd_stats.iflod_total -= ofp->cnts.total;
+		dccd_stats.iflod_accepted -= ofp->cnts.accepted;
+		dccd_stats.iflod_stale -= ofp->lc.stale.cur;
+		dccd_stats.iflod_dup -= ofp->lc.dup.cur;
+		dccd_stats.iflod_wlist -= ofp->lc.wlist.cur;
+		dccd_stats.iflod_not_deleted -= ofp->lc.not_deleted.cur;
+	}
+
+	q_delays_start = 0;
+
+	memset(&db_stats, 0, sizeof(db_stats));
+	dccd_stats.reset = db_time;
+}
+
+
+
+static u_char				/* 1=sent 0=something wrong */
+stats_send(QUEUE *q)
+{
+	DCC_ADMN_RESP stats;
+	char tbuf[80];
+	OFLOD_INFO *ofp;
+	IFLOD_INFO *ifp;
+	int oflods_connecting, iflods_connecting;
+	SCNTR iflod_total, iflod_accepted, iflod_stale;
+	SCNTR iflod_dup, iflod_wlist, iflod_not_deleted;
+	char flod_buf[60];
+	char clients_reset[40], reset_buf[36], now_buf[20];
+	int clients;
+	int age;
+	const char *client_ovf;
+	int blen, plen, len;
+
+	tbuf[0] = '\0';
+	if (dccd_tracemask & DCC_TRACE_ADMN_BIT)
+		strcat(tbuf, "ADMN ");
+	if (dccd_tracemask & DCC_TRACE_ANON_BIT)
+		strcat(tbuf, "ANON ");
+	if (dccd_tracemask & DCC_TRACE_CLNT_BIT)
+		strcat(tbuf, "CLNT ");
+	if (dccd_tracemask & DCC_TRACE_RLIM_BIT)
+		strcat(tbuf, "RLIM ");
+	if (dccd_tracemask & DCC_TRACE_QUERY_BIT)
+		strcat(tbuf, "QUERY ");
+	if (dccd_tracemask & DCC_TRACE_RIDC_BIT)
+		strcat(tbuf, "RIDC ");
+	if (dccd_tracemask & DCC_TRACE_FLOD_BIT)
+		strcat(tbuf, "FLOOD ");
+	if (dccd_tracemask & DCC_TRACE_FLOD2_BIT)
+		strcat(tbuf, "FLOOD2 ");
+	if (dccd_tracemask & DCC_TRACE_IDS_BIT)
+		strcat(tbuf, "IDS ");
+	if (dccd_tracemask & DCC_TRACE_BL_BIT)
+		strcat(tbuf, "BL ");
+	if (dccd_tracemask & DCC_TRACE_DB_BIT)
+		strcat(tbuf, "DB ");
+	if (dccd_tracemask & DCC_TRACE_WLIST_BIT)
+		strcat(tbuf, "WLIST ");
+
+	clients = clients_get(0, 0, 0, 0, 0, 0, 0);
+	if (clients >= 0) {
+		client_ovf = "";
+	} else {
+		client_ovf = ">";
+		clients = -clients;
+	}
+	age = db_time.tv_sec - clients_cleared;
+	if (age <= 24*60*60) {
+		dcc_time2str(clients_reset, sizeof(clients_reset),
+			     "since %X", clients_cleared);
+	} else if (age <= 3*24*60*60) {
+		snprintf(clients_reset, sizeof(clients_reset),
+			 "in %d hours", (age + 60*60/2) / (60*60));
+	} else {
+		snprintf(clients_reset, sizeof(clients_reset),
+			 "in %d days", (age + 24*60*60/2) / (24*60*60));
+	}
+
+	oflods_connecting = 0;
+	iflod_total = dccd_stats.iflod_total;
+	iflod_accepted = dccd_stats.iflod_accepted;
+	iflod_stale = dccd_stats.iflod_stale;
+	iflod_dup = dccd_stats.iflod_dup;
+	iflod_wlist = dccd_stats.iflod_wlist;
+	iflod_not_deleted = dccd_stats.iflod_not_deleted;
+	for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) {
+		if (ofp->soc >= 0 && !(ofp->flags & OFLOD_FG_CONNECTED))
+			++oflods_connecting;
+		iflod_total += ofp->cnts.total;
+		iflod_accepted += ofp->cnts.accepted;
+		iflod_stale += ofp->lc.stale.cur;
+		iflod_dup += ofp->lc.dup.cur;
+		iflod_wlist += ofp->lc.wlist.cur;
+		iflod_not_deleted += ofp->lc.not_deleted.cur;
+	}
+	iflods_connecting = 0;
+	for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) {
+		if (ifp->soc >= 0 && !(ifp->flags & IFLOD_FG_VERS_CK))
+			++iflods_connecting;
+	}
+	dcc_time2str(reset_buf, sizeof(reset_buf),"%b %d %X",
+		     dccd_stats.reset.tv_sec);
+	dcc_time2str(now_buf, sizeof(now_buf), "%b %d %X %Z",
+		     db_time.tv_sec);
+
+	blen = min(sizeof(stats.val.string), ntohl(q->pkt.ad.val1));
+	plen = snprintf(stats.val.string, blen,
+	    "    version "DCC_VERSION"  %s%s%stracing %s\n"
+	    "%7d hash entries %6d used "L_DWPAT(9)" DB bytes\n"
+	    "%5d ms delay  "L_DPAT" NOPs  "L_DPAT""
+	    " ADMN  "L_DPAT" query  %s%d clients %s\n",
+
+	    db_minimum_map ? "DB UNLOCKED  " : "",
+	    query_only ? "Q-mode  " : "",
+	    grey_on ? "greylist  " : "",
+	    tbuf[0] ? tbuf : "nothing",
+
+	    HADDR2LEN(db_hash_len), HADDR2LEN(db_hash_used), db_csize,
+
+	    avg_q_delay_ms(q),
+
+	    dccd_stats.nops, dccd_stats.admin, dccd_stats.queries,
+	    client_ovf, clients, clients_reset);
+	if (plen >= blen)
+		plen = blen-1;
+	blen -= plen;
+
+	if (grey_on) {
+		len = snprintf(&stats.val.string[plen], blen,
+	    L_DWPAT(7)" reports "L_DWPAT(2)" whitelisted\n",
+
+	    dccd_stats.reports,
+	    dccd_stats.respwhite);
+
+	} else {
+		len = snprintf(&stats.val.string[plen], blen,
+			       L_DWPAT(8)" reports "
+			       L_DWPAT(7)">10 "
+			       L_DWPAT(7)">100 "
+			       L_DWPAT(7)">1000 "
+			       L_DWPAT(7)" many\n"
+			       "         answers "L_DWPAT(7)">10 "
+			       L_DWPAT(7)">100 "
+			       L_DWPAT(7)">1000 "
+			       L_DWPAT(7)" many\n",
+
+	    dccd_stats.reports,
+	    (dccd_stats.report10 + dccd_stats.report100
+	     + dccd_stats.report1000 + dccd_stats.reportmany),
+	    (dccd_stats.report100 + dccd_stats.report1000
+	     + dccd_stats.reportmany),
+	    dccd_stats.report1000 + dccd_stats.reportmany,
+	    dccd_stats.reportmany,
+
+	    (dccd_stats.resp10 + dccd_stats.resp100
+	     + dccd_stats.resp1000 + dccd_stats.respmany),
+	    dccd_stats.resp100 + dccd_stats.resp1000 + dccd_stats.respmany,
+	    dccd_stats.resp1000 + dccd_stats.respmany,
+	    dccd_stats.respmany);
+	}
+	if (len >= blen)
+		len = blen-1;
+	blen -= len;
+	plen += len;
+
+	len = snprintf(&stats.val.string[plen], blen,
+	    L_DWPAT(8)" bad op "
+	    L_DWPAT(4)" passwd "
+	    L_DWPAT(6)" blist "
+	    L_DWPAT(4)" reject "
+	    L_DWPAT(6)" retrans\n",
+	    dccd_stats.bad_op, dccd_stats.bad_passwd, dccd_stats.blist,
+	    dccd_stats.send_error,  dccd_stats.report_retrans);
+	if (len >= blen)
+		len = blen-1;
+	blen -= len;
+	plen += len;
+
+	if (!grey_on) {
+		len = snprintf(&stats.val.string[plen], blen,
+	    L_DWPAT(8)" answers rate-limited "
+	    L_DWPAT(4)" anon "
+	    L_DWPAT(5)" reports rejected\n",
+	    dccd_stats.rl, dccd_stats.anon_rl,  dccd_stats.report_reject);
+		if (len >= blen)
+			len = blen-1;
+		blen -= len;
+		plen += len;
+	}
+
+	len = snprintf(&stats.val.string[plen], blen,
+	    "    %s "
+	    L_DWPAT(8)" total flooded in\n"
+	    L_DWPAT(8)" accepted "
+	    L_DWPAT(6)" stale "
+	    L_DWPAT(8)" dup  "
+	    L_DWPAT(5)" white    "
+	    L_DPAT" delete\n"
+	    L_DWPAT(8)" reports added between %s and %s",
+	    flod_stats_printf(flod_buf, sizeof(flod_buf),
+			      (db_minimum_map || flods_st == FLODS_ST_OFF) ? 0
+			      : (flods_st != FLODS_ST_ON) ? 1
+			      : 2,
+			      oflods.total,
+			      oflods.open - oflods_connecting,
+			      iflods.open - iflods_connecting),
+	    iflod_total,
+	    iflod_accepted, iflod_stale, iflod_dup,
+	    iflod_wlist, iflod_not_deleted,
+
+	    dccd_stats.adds+db_stats.adds, reset_buf, now_buf);
+	if (len >= blen)
+		len = blen-1;
+	blen -= len;
+	plen += len;
+
+	stats.hdr.len = plen + sizeof(stats)-sizeof(stats.val);
+	stats.hdr.op = DCC_OP_ADMN;
+	send_resp(q, &stats.hdr, 0);
+	return 1;
+}
+
+
+
+void
+timestamp_send(const QUEUE *q)
+{
+	time_t delta;
+	DCC_ADMN_RESP msg;
+	int blen, plen;
+
+	delta = picky_time(q);
+
+	blen = min(sizeof(msg.val.string), ntohl(q->pkt.ad.val1));
+	if (delta < -MAX_CMD_CLOCK_SKEW || delta > MAX_CMD_CLOCK_SKEW) {
+		if (delta < -MAX_FLOD_CLOCK_SKEW
+		    || delta > MAX_FLOD_CLOCK_SKEW) {
+			plen = snprintf(msg.val.string, blen,
+					"    clocks differ by about %d seconds"
+					"\n    which is more than the"
+					" maximum allowed for flooding, %d",
+					(int)delta, MAX_FLOD_CLOCK_SKEW);
+		} else {
+			plen = snprintf(msg.val.string, blen,
+					"    clocks differ by about %d seconds"
+					"\n    which is more than the"
+					" maximum allowed for commands, %d",
+					(int)delta, MAX_CMD_CLOCK_SKEW);
+		}
+	} else {
+		plen = snprintf(msg.val.string, blen,
+				"    clocks differ by about %d seconds",
+				(int)delta);
+	}
+
+	msg.hdr.len = plen + sizeof(msg)-sizeof(msg.val);
+	msg.hdr.op = DCC_OP_ADMN;
+	send_resp(q, &msg.hdr, 0);
+}
+
+
+
+void
+do_nop(QUEUE *q)
+{
+	/* respond immediately to even anonymous NOPs so that clients
+	 * that are confused about passwords and whether they are anonymous
+	 * do not retransmit unnecessarily */
+	TMSG1(ADMN, "received %s", op_id_ip(q));
+	++dccd_stats.nops;
+
+	if (!ck_clnt_srvr_id(q)) {
+		++q->rl->d.nops;
+		return;
+	}
+
+	++q->rl->d.nops;
+	send_ok(q);
+}
+
+
+
+/* deal with an adminstative request */
+void
+do_admn(QUEUE *q)
+{
+	u_int32_t val1;
+	DCC_ADMN_RESP resp;
+	int len, offset;
+	u_int32_t adelay_ms;
+	struct in6_addr addr6, mask6;
+	const struct in6_addr *addr6p, *mask6p;
+
+	val1 = ntohl(q->pkt.ad.val1);
+	TMSG3(ADMN, "received val2=%#x val3=%#x in %s",
+	      q->pkt.ad.val2, q->pkt.ad.val3, op_id_ip(q));
+	++dccd_stats.admin;
+
+	if (!ck_clnt_srvr_id(q))
+		return;
+
+	if (q->pkt_len != DCC_ADMN_REQ_MIN_SIZE
+	    && (q->pkt_len != (DCC_ADMN_REQ_MIN_SIZE
+			       + sizeof(DCC_AOP_CLIENTS_CIDR))
+		|| (q->pkt.ad.aop != DCC_AOP_CLIENTS
+		    && q->pkt.ad.aop != DCC_AOP_CLIENTS_ID))) {
+		send_error(q, "%s size = %d", qop2str(q), q->pkt_len);
+		return;
+	}
+
+	switch ((DCC_AOPS)q->pkt.ad.aop) {
+	case DCC_AOP_STOP:		/* stop gracefully */
+		if (!picky_admn(q, 0, 0))
+			return;
+		if (ridc_get(q)) {
+			repeat_resp(q);
+			return;
+		}
+		if (!stopint) {
+			stopint = -1;
+			next_flods_ck = 0;
+		}
+		send_ok(q);
+		/* fsync() or let the database be wrong	if asked */
+		if (val1 != 0)
+			stop_mode = val1;
+		return;
+
+	case DCC_AOP_DB_UNLOAD:
+		if (!picky_admn(q, 0, 0))
+			return;
+		/* repeat previous answer to repeated question */
+		if (ridc_get(q)) {
+			repeat_resp(q);
+			return;
+		}
+		/* unlike dbclean, dblist starts looking at the data
+		 * immediately, so we cannot answer before flushing */
+		if (val1 == 0) {
+			dcc_trace_msg("database flush started");
+			rel_db_states();
+			db_minimum_map = 1;
+			db_unload(0, 0);
+			dcc_trace_msg("database flushed; buffering off");
+		} else {
+			db_minimum_map = 0;
+			dcc_trace_msg("database buffering on");
+		}
+		send_ok(q);
+		return;
+
+	case DCC_AOP_FLOD:		/* control flooding */
+		do_flod(q);
+		return;
+
+	case DCC_AOP_DB_CLEAN:		/* start switch to new database */
+		if (!picky_admn(q, 0, 0))
+			return;
+		/* repeat previous answer to repeated question */
+		if (ridc_get(q)) {
+			repeat_resp(q);
+			return;
+		}
+		if (!flods_off || oflods.total != 0) {
+			send_error(q, "flooding not stopped before %s",
+				   qop2str(q));
+			return;
+		}
+		send_ok(q);		/* asnwer now before we stall */
+		dcc_trace_msg("database cleaning begun");
+		next_flods_ck = 0;
+		/* don't start our own cleaning */
+		del_dbclean_next = db_time.tv_sec + DEL_DBCLEAN_SECS;
+		dbclean_limit = db_time.tv_sec + dbclean_limit_secs;
+		/* Dbclean expects us to remove its separate hold on flooding
+		 * so that it will not need to talk to us after telling us
+		 * to close the old database.  This because we might stall
+		 * on some systems with lame mmap() support including BSD/OS,
+		 * for minutes in close().
+		 * It might be nice to be able to turn off flooding before
+		 * dbclean is run and have it remain off when dbclean
+		 * finishes.  However, the need for that that is very rare
+		 * and there are mysterious cases where flooding gets
+		 * turned off by dbclean and never restored. */
+		flods_off = 0;
+		/* release and unmap buffers, possibly stalling */
+		db_minimum_map = 1;
+		rel_db_states();
+		db_unload(0, 0);
+		return;
+
+	case DCC_AOP_DB_NEW:		/* finish switch to new database */
+		if (!picky_admn(q, 0, 0))
+			return;
+		if (ridc_get(q)) {
+			repeat_resp(q);
+			return;
+		}
+		if (!db_minimum_map) {
+			send_error(q, "%s received before %s",
+				   qop2str(q),
+				   dcc_aop2str(0, 0, DCC_AOP_DB_CLEAN, 0));
+			return;
+		}
+		/* send "ok" now because we may stall waiting to reopen */
+		send_ok(q);
+		db_close(1);
+		dccd_stats.adds += db_stats.adds;
+		if (!dccd_db_open(DB_OPEN_LOCK_WAIT))
+			dcc_logbad(dcc_ex_code,
+				   "could not restart database %s: %s",
+				   db_nm, dcc_emsg);
+		dcc_trace_msg(DCC_VERSION" database %s reopened with %s",
+			      db_nm, db_window_size_str);
+		flods_off = 0;
+		flods_restart("database reopened", 0);
+		next_flods_ck = 0;	/* possibly reap dbclean child */
+		if (0 >= check_load_ids(2))
+			dcc_error_msg("%s", dcc_emsg);
+		return;
+
+	case DCC_AOP_STATS:		/* return counters */
+		/* we cannot just repeat ourselves for retransmissions,
+		 * because the answer is too big to save */
+		stats_send(q);
+		return;
+
+	case DCC_AOP_STATS_CLEAR:	/* return and then zero counters */
+		if (!picky_admn(q, 0, 0))
+			return;
+		/* we cannot just repeat ourselves for retransmissions,
+		 * because the answer is too big to save */
+		if (stats_send(q)) {
+			clients_clear();
+			stats_clear();
+		}
+		return;
+
+	case DCC_AOP_TRACE_ON:
+	case DCC_AOP_TRACE_OFF:
+		if (!picky_admn(q, 0, 0))
+			return;
+		/* it is idempotent, but suppress duplicate trace messages */
+		if (ridc_get(q)) {
+			repeat_resp(q);
+			return;
+		}
+		/* log trace changes even when tracing is off */
+		if (!(DCC_TRACE_ADMN_BIT & dccd_tracemask))
+			dcc_trace_msg("received %s", op_id_ip(q));
+		if ((val1 & ~DCC_TRACE_BITS) != 0 || val1 == 0) {
+			send_error(q, "invalid trace bits %#x", val1);
+			return;
+		}
+		if (q->pkt.ad.aop == DCC_AOP_TRACE_OFF) {
+			dccd_tracemask &= ~val1;
+		} else {
+			dccd_tracemask |= val1;
+			/* do not suppress the next duplicated flood message */
+			if (val1 & DCC_TRACE_FLOD_BIT)
+				flod_trace_gen = db_time.tv_sec;
+		}
+		send_ok(q);
+		return;
+
+	case DCC_AOP_CLIENTS:
+	case DCC_AOP_CLIENTS_ID:
+		if (!picky_admn(q, 1, 1))
+			return;
+		/* we cannot just repeat ourselves for retransmissions,
+		 * because the answer is too big to save */
+		offset = (val1 >> 16) + (((u_int)q->pkt.ad.val4) << 16);
+		val1 &= 0xffff;
+		len = q->pkt.ad.val2;
+		if (q->pkt_len == (DCC_ADMN_REQ_MIN_SIZE
+				   + sizeof(DCC_AOP_CLIENTS_CIDR))) {
+			memcpy(&addr6, &q->pkt.ad.val5[0], sizeof(addr6));
+			dcc_bits2mask(&mask6, q->pkt.ad.val5[sizeof(addr6)]);
+			addr6p = &addr6;
+			mask6p = &mask6;
+		} else {
+			mask6p = 0;
+			addr6p = 0;
+		}
+		if (q->pkt.ad.aop == DCC_AOP_CLIENTS)
+			clients_get(&resp.val, &len, offset,
+				    val1, q->pkt.ad.val3, addr6p, mask6p);
+		else
+			clients_get_id(&resp.val, &len, offset,
+				       val1, q->pkt.ad.val3, addr6p, mask6p);
+		resp.hdr.len = len + sizeof(resp)-sizeof(resp.val);
+		resp.hdr.op = DCC_OP_ADMN;
+		send_resp(q, &resp.hdr, 0);
+		return;
+
+	case DCC_AOP_ANON_DELAY:
+		/* get and set the anonymous client delay
+		 *
+		 * repeat answer to identical question */
+		if (ridc_get(q)) {
+			repeat_resp(q);
+			return;
+		}
+		if (anon_off)
+			adelay_ms = DCC_ANON_DELAY_FOREVER;
+		else
+			adelay_ms = anon_delay_us/1000;
+		resp.val.anon_delay.delay[0] = adelay_ms>>8;
+		resp.val.anon_delay.delay[1] = adelay_ms;
+		if (anon_delay_inflate == DCC_ANON_INFLATE_OFF) {
+			resp.val.anon_delay.inflate[0] = 0;
+			resp.val.anon_delay.inflate[1] = 0;
+			resp.val.anon_delay.inflate[2] = 0;
+			resp.val.anon_delay.inflate[3] = 0;
+		} else {
+			resp.val.anon_delay.inflate[0] = anon_delay_inflate>>24;
+			resp.val.anon_delay.inflate[1] = anon_delay_inflate>>16;
+			resp.val.anon_delay.inflate[2] = anon_delay_inflate>>8;
+			resp.val.anon_delay.inflate[3] = anon_delay_inflate;
+		}
+		adelay_ms = (q->pkt.ad.val2<<8) + q->pkt.ad.val3;
+		if (adelay_ms != DCC_NO_ANON_DELAY
+		    && picky_admn(q, 0, 0)) {
+			if (adelay_ms == DCC_ANON_DELAY_FOREVER) {
+				anon_off = 1;
+			} else {
+				anon_off = 0;
+				if (adelay_ms > DCC_ANON_DELAY_MAX/1000)
+					adelay_ms = DCC_ANON_DELAY_MAX/1000;
+				anon_delay_us = adelay_ms*1000;
+				if (val1 == 0)
+					val1 = DCC_ANON_INFLATE_OFF;
+				anon_delay_inflate = val1;
+			}
+		}
+		resp.hdr.len = (sizeof(resp)-sizeof(resp.val)
+				+ sizeof(resp.val.anon_delay));
+		resp.hdr.op = DCC_OP_ADMN;
+		send_resp(q, &resp.hdr, 0);
+		return;
+
+	case DCC_AOP_CLOCK_CHECK:
+		timestamp_send(q);
+		return;
+
+	case DCC_AOP_OK:
+	case DCC_AOP_unused1:
+	default:
+		break;
+	}
+
+	send_error(q, "invalid %s", qop2str(q));
+}
diff -r 000000000000 -r c7f6b056b673 dccifd.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccifd.0	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,677 @@
+dccifd(8)             Distributed Checksum Clearinghouse             dccifd(8)
+
+NNAAMMEE
+     ddcccciiffdd -- Distributed Checksum Clearinghouse Interface Daemon
+
+SSYYNNOOPPSSIISS
+     ddcccciiffdd [--VVddbbxxAANNQQ] [--GG _o_n | _o_f_f | _n_o_I_P | _I_P_m_a_s_k_/_x_x] [--hh _h_o_m_e_d_i_r] [--II _u_s_e_r]
+            [--pp _/_s_o_c_k | _h_o_s_t_,_p_o_r_t_,_r_h_o_s_t_/_b_i_t_s] [--oo _/_s_o_c_k | _h_o_s_t_,_p_o_r_t]
+            [--DD _l_o_c_a_l_-_d_o_m_a_i_n] [--mm _m_a_p] [--ww _w_h_i_t_e_c_l_n_t] [--UU _u_s_e_r_d_i_r_s]
+            [--aa _I_G_N_O_R_E | _R_E_J_E_C_T | _D_I_S_C_A_R_D] [--tt _t_y_p_e_,[_l_o_g_-_t_h_o_l_d_,]_r_e_j_-_t_h_o_l_d]
+            [--gg [_n_o_t_-]_t_y_p_e] [--SS _h_e_a_d_e_r] [--ll _l_o_g_d_i_r] [--RR _r_u_n_d_i_r]
+            [--rr _r_e_j_e_c_t_i_o_n_-_m_s_g] [--TT _t_m_p_d_i_r] [--jj _m_a_x_j_o_b_s]
+            [--BB _d_n_s_b_l_-_o_p_t_i_o_n] [--LL _l_t_y_p_e_,_f_a_c_i_l_i_t_y_._l_e_v_e_l]
+
+DDEESSCCRRIIPPTTIIOONN
+     ddcccciiffdd is a daemon intended to connect spam filters such as SpamAssasin
+     and mail transfer agents (MTAs) other than sendmail to DCC servers.  The
+     MTA or filter ddcccciiffdd which in turn reports related checksums to the near-
+     est DCC server and adds an _X_-_D_C_C SMTP header line to the message.  The
+     MTA is told to reject the message if it is unsolicited bulk.
+
+     DDcccciiffdd is similar to the DCC sendmail milter interface, dccm(8) and the
+     DCC Procmail interface, dccproc(8).  DDcccciiffdd is more efficient than
+     dccproc(8) but not restricted to use with sendmail like dccm(8).  All
+     three send reports of checksums related to mail received by DCC clients
+     and queries about the total number of reports of particular checksums.
+
+     MTA programs use a simple ASCII protocol a subset of SMTP to send a mail
+     message including its SMTP envelope to the daemon.  DDcccciiffdd responds with
+     an indication of whether the message is unsolicited bulk and an optional
+     copy of the message with an _X_-_D_C_C header added.  The ASCII protocol is
+     described below and in the _i_n_c_l_u_d_e_/_d_c_c_i_f_._h file in the DCC source.  There
+     is a sample C interface routine in the _d_c_c_l_i_b_/_d_c_c_i_f_._c file in the DCC
+     source and the _d_c_c_l_i_b_._a library generated from the source.  A _P_e_r_l ver-
+     sion of the interface routine is in _d_c_c_i_f_d_/_d_c_c_i_f_._p_l.  Test or demonstra-
+     tion programs in the style of dccproc(8) that use those interface rou-
+     tines are in _d_c_c_i_f_d_/_d_c_c_i_f_-_t_e_s_t.
+
+     A subset of ESMTP can be used instead of the ASCII protocol to connect
+     ddcccciiffdd to postfix as a "Before-Queue Content Filter."  See the --oo flag.
+
+     Since the checksums of messages that are whitelisted locally by the --ww
+     _w_h_i_t_e_c_l_n_t file are not reported to the DCC server, ddcccciiffdd knows nothing
+     about the total recipient counts for their checksums and so cannot add
+     _X_-_D_C_C header lines to such messages.
+
+     Enable the daemon and put its parameters in the _d_c_c___c_o_n_f file and start
+     the daemon with the _s_t_a_r_t_-_d_c_c_i_f_d script.
+
+     The list of servers that ddcccciiffdd contacts is in the memory mapped file _m_a_p
+     shared by local DCC clients.  The file is  maintained with cdcc(8).
+
+   OOPPTTIIOONNSS
+     The following options are available:
+
+     --VV   displays the version of ddcccciiffdd.
+
+     --dd   enables debugging output from the DCC client software.  Additional
+          --dd options increase the number of messages.  A single --dd
+           aborted SMTP transactions including those from some "dictionary
+          attacks."
+
+     --bb   causes the daemon to not detach itself from the controlling tty and
+          put itself into the background.
+
+     --xx   causes the daemon to try "extra hard" to contact a DCC server.
+          Since it is usually more important to deliver mail than to report
+          its checksums, ddcccciiffdd normally does not delay too long while trying
+          to contact a DCC server.  It will not try again for several seconds
+          after a failure.  With --xx, it will always try to contact the DCC
+          server and it will tell the MTA to answer the DATA command with a
+          4yz temporary failure.
+
+     --AA   adds to existing X-DCC headers in the message instead of replacing
+          existing headers of the brand of the current server.
+
+     --NN   neither adds, deletes, nor replaces existing X-DCC headers in the
+          message.  Each message is logged, rejected, and otherwise handled
+          the same.
+
+     --QQ   only queries the DCC server about the checksums of messages instead
+          of reporting and querying.  This is useful when ddcccciiffdd is used to
+          filter mail that has already been reported to a DCC server by
+          another DCC client.  No single mail message should be reported to a
+          DCC server more than once per recipient, because each report will
+          increase the apparent "bulkness" of the message.
+
+          It is better to use _M_X_D_C_C lines in the global _w_h_i_t_e_c_l_n_t file for
+          your MX mail servers that use DCC than --QQ.
+
+     --GG _o_n | _o_f_f | _n_o_I_P | _I_P_m_a_s_k_/_x_x
+          controls _g_r_e_y_l_i_s_t_i_n_g.  At least one working greylist server must be
+          listed in the _m_a_p file in the DCC home directory.  If more than one
+          is named, they must "flood" or change checksums and they must use
+          the same --GG parameters.  See dccd(8).  Usually all dccm or dccifd
+          DCC client processes use the same --GG parameters.
+
+          _I_P_m_a_s_k_/_x_x and _n_o_I_P remove part or all of the IP address from the
+          greylist triple.  The CIDR block size, _x_x, must be between 1 and
+          128.  96 is added to block sizes smaller than 33 to make them appro-
+          priate for the IPv6 addresses used by the DCC.  _I_P_m_a_s_k_/_9_6 differs
+          from _n_o_I_P for IPv4 addresses, because the former retains the IPv4 to
+          IPv6 mapping prefix.
+
+     --hh _h_o_m_e_d_i_r
+          overrides the default DCC home directory, _/_v_a_r_/_d_c_c.
+
+     --II _u_s_e_r
+          specifies the UID and GID of the process.
+
+     --pp _/_s_o_c_k_/_n_a_m_e | _h_o_s_t_,_p_o_r_t_,_r_h_o_s_t_/_b_i_t_s
+          overrides the default address at which programs contact ddcccciiffdd.  The
+          default is a UNIX domain socket named dccifd in the DCC home direc-
+          tory.
+
+          The second form specifies a local host name or IP address, a local
+          TCP port number, and the host names or IP addresses of computers
+          that can use ddcccciiffdd.  127.0.0.1 or _l_o_c_a_l_h_o_s_t are common choices for
+          _h_o_s_t.  The string _@ specifies IN_ADDRANY or all local IP addresses.
+          127.0.0.0/8 is a common choice for _r_h_o_s_t_/_b_i_t_s.
+
+     --oo _/_s_o_c_k | _h_o_s_t_,_p_o_r_t
+          enables SMTP proxy mode instead of the ASCII protocol and specifies
+          the output connection when ddcccciiffdd acts as an SMTP proxy.  It is the
+          address of the SMTP server for which ddcccciiffdd acts as SMTP client.
+          When _/_s_o_c_k is _/_d_e_v_/_n_u_l_l, ddcccciiffdd acts as if there were downstream
+          SMTP server that always answers "250 ok".  The string _@ specifies
+          the same IP address as the incoming TCP connection.
+
+          The input to ddcccciiffdd in SMTP proxy mode is specified with ----pp.  For
+          example, --pp _1_2_7_._0_._0_._1_,_1_0_0_2_5_,_1_2_7_._0_._0_._1_/_3_2 --oo _1_2_7_._0_._0_._1_,_1_0_0_2_6 could be
+          used to connect ddcccciiffdd with Postfix as described in the documenta-
+          tion in version 2.2.1 Postfix documentation.
+
+          See below concerning the subset of ESMTP used in this mode.
+
+     --mm _m_a_p
+          specifies a name or path of the memory mapped parameter file instead
+          of the default _m_a_p file in the DCC home directory.  It should be
+          created with the cdcc(8) command.
+
+     --ww _w_h_i_t_e_c_l_n_t
+          specifies an optional file containing filtering parameters as well
+          as SMTP client IP addresses, SMTP envelope values, and header values
+          of mail that is spam or is not spam and does not need a _X_-_D_C_C
+          header, and whose checksums should not be reported to the DCC
+          server.
+
+          If the pathname _w_h_i_t_e_c_l_n_t is not absolute, it is relative to the DCC
+          home directory.
+
+          The format of the ddcccciiffdd whiteclnt file is the same as the _w_h_i_t_e_l_i_s_t
+          files used by dbclean(8) and the _w_h_i_t_e_c_l_n_t file used by dccproc(8).
+          See dcc(8) for a description of DCC white and blacklists.  Because
+          the contents of the _w_h_i_t_e_c_l_n_t file are used frequently, a companion
+          file is automatically created and maintained.  It has the same path-
+          name but with an added suffix of _._d_c_c_w and contains a memory mapped
+          hash table of the main file.
+
+          A whitelist entry ("OK") or two or more semi-whitelistings ("OK2")
+          for one of the message's checksums prevents all of the message's
+          checksums from being reported to the DCC server and the addition of
+          a _X_-_D_C_C header line by ddcccciiffdd A whitelist entry for a checksum also
+          prevents rejecting or discarding the message based on DCC recipient
+          counts as specified by --aa and --tt.  Otherwise, one or more checksums
+          with blacklisting entries ("MANY") cause all of the message's check-
+          sums to be reported to the server with an addressee count of "MANY".
+
+          If the message has a single recipient, an _e_n_v___T_o _w_h_i_t_e_c_l_n_t entry of
+          "OK" for the checksum of its recipient address acts like any other
+          _w_h_i_t_e_c_l_n_t entry of "OK."  When the SMTP message has more than one
+          recipient, the effects can be complicated.  When a message has sev-
+          eral recipients with some but not all listed in the _w_h_i_t_e_c_l_n_t file,
+          ddcccciiffdd tries comply with the wishes of the users who want filtering
+          as well as those who don't by silently not delivering the message to
+          those who want filtering (i.e. are not whitelisted) and delivering
+          the message to don't want filtering.
+
+     --UU _u_s_e_r_d_i_r_s
+          enables per-user _w_h_i_t_e_c_l_n_t files and log directories.  Each target
+          of a message can have a directory of log files named
+          _u_s_e_r_d_i_r_s_/_a_d_d_r_/_l_o_g where _a_d_d_r is the local user or mailbox name com-
+          puted by the MTA.  The name of each user's log directory must be
+          _l_o_g.  If it is not absolute, _u_s_e_r_d_i_r_s is relative to the DCC home
+          directory.  The directory containing the log files must be named _l_o_g
+          and it must be writable by the ddcccciiffdd process.  Each log directory
+          must exist or logging for the corresponding is silently disabled.
+          The files created in the log directory are owned by the UID of the
+          ddcccciiffdd process, but they have _g_r_o_u_p and _o_t_h_e_r read and write permis-
+          sions copied from the corresponding _l_o_g directory.  To ensure the
+          privacy of mail, it may be good to make the directories readable
+          only by _o_w_n_e_r and _g_r_o_u_p, and to use a cron script that changes the
+          owner of each file to match the grandparent _a_d_d_r directory.
+
+          There can also be a per -user whitelist file named
+          _u_s_e_r_d_i_r_s_/_a_d_d_r_/_w_h_i_t_e_c_l_n_t for each address _a_d_d_r_. Any checksum that is
+          not white- or blacklisted by an individual addressee's _w_h_i_t_e_c_l_n_t
+          file  is checked in the main --ww --wwhhiitteeccllnntt file.  A missing per-
+          addressee _w_h_i_t_e_c_l_n_t file is the same as an empty file.  Relative
+          paths for files included in per-addressee files are resolved in the
+          DCC home directory.  The _w_h_i_t_e_c_l_n_t files and the _a_d_d_r directories
+          containing them must be writable by the ddcccciiffdd process.
+
+          _O_p_t_i_o_n lines in per-user whiteclnt files can be used to modify many
+          aspects of ddcccciiffdd filtering, as described in the main dcc man page.
+          For example, an _o_p_t_i_o_n _d_c_c_-_o_f_f line turns off DCC filtering for
+          individual mailboxes.
+
+     --aa _I_G_N_O_R_E | _R_E_J_E_C_T | _D_I_S_C_A_R_D
+          specifies the action taken when ddcccciiffdd is in proxy mode with --oo and
+          DCC server counts or --tt thresholds say that a message is unsolicited
+          and bulk.  _I_G_N_O_R_E causes the message to be unaffected except for
+          adding the _X_-_D_C_C header line to the message.  This turns off DCC
+          filtering.
+
+          Spam can also be _R_E_J_E_C_Ted or (when in proxy mode with --oo) accepted
+          and silently _D_I_S_C_A_R_Ded without being delivered to local mailboxes.
+          The default is _R_E_J_E_C_T.
+
+          Mail forwarded via IP addresses marked _M_X or _M_X_D_C_C in the main
+          _w_h_i_t_e_c_l_n_t file is treated as if --aa _D_I_S_C_A_R_D were specified.  This
+          prevents "bouncing" spam.
+
+          The effects of the --ww _w_h_i_t_e_c_l_n_t are not affected by --aa.
+
+     --tt _t_y_p_e_,[_l_o_g_-_t_h_o_l_d_,]_r_e_j_-_t_h_o_l_d
+          sets logging and "spam" thresholds for checksum _t_y_p_e.  The checksum
+          types are _I_P, _e_n_v___F_r_o_m, _F_r_o_m, _M_e_s_s_a_g_e_-_I_D, _s_u_b_s_t_i_t_u_t_e, _R_e_c_e_i_v_e_d,
+          _B_o_d_y, _F_u_z_1, _F_u_z_2, _r_e_p_-_t_o_t_a_l, and _r_e_p.  The first six, _I_P through
+          _s_u_b_s_t_i_t_u_t_e, have no effect except when a local DCC server configured
+          with --KK is used.  The _s_u_b_s_t_i_t_u_t_e thresholds apply to the first sub-
+          stitute heading encountered in the mail message.  The string _A_L_L
+          sets thresholds for all types, but is unlikely to be useful except
+          for setting logging thresholds.  The string _C_M_N specifies the com-
+          monly used checksums _B_o_d_y, _F_u_z_1, and _F_u_z_2.  _R_e_j_-_t_h_o_l_d and _l_o_g_-_t_h_o_l_d
+          must be numbers, the string _N_E_V_E_R, or the string _M_A_N_Y indicating
+          millions of targets.  Counts from the DCC server as large as the
+          threshold for any single type are taken as sufficient evidence that
+          the message should be logged or rejected.
+
+          _L_o_g_-_t_h_o_l_d is the threshold at which messages are logged.  It can be
+          handy to log messages at a lower threshold to find solicited bulk
+          mail sources such as mailing lists.  If no logging threshold is set,
+          only rejected mail and messages with complicated combinations of
+          white and blacklisting are logged.  Messages that reach at least one
+          of their rejection thresholds are logged regardless of logging
+          thresholds.
+
+          _R_e_j_-_t_h_o_l_d is the threshold at which messages are considered "bulk,"
+          and so should be rejected or discarded if not whitelisted.
+
+          DCC Reputation thresholds in the commercial version of the DCC are
+          controlled by thresholds on checksum types _r_e_p and _r_e_p_-_t_o_t_a_l.  Mes-
+          sages from an IP address that the DCC database says has sent more
+          than --tt _r_e_p_-_t_o_t_a_l_,_l_o_g_-_t_h_o_l_d messages are logged.  A DCC Reputation
+          is computed for messages received from IP addresses that have sent
+          more than --tt _r_e_p_-_t_o_t_a_l_,_l_o_g_-_t_h_o_l_d messages.  The DCC Reputation of an
+          IP address is the percentage of its messages that have been detected
+          as bulk or having at least 10 recipients.  The defaults are equiva-
+          lent to --tt _r_e_p_,_n_e_v_e_r and --tt _r_e_p_-_t_o_t_a_l_,_n_e_v_e_r_,_2_0.
+
+          Bad DCC Reputations do not reject mail unless enabled by an _o_p_t_i_o_n
+          _D_C_C_-_r_e_p_-_o_n line in a _w_h_i_t_e_c_l_n_t file.
+
+          The checksums of locally whitelisted messages are not checked with
+          the DCC server and so only the number of targets of the current copy
+          of a whitelisted message are compared against the thresholds.
+
+          The default is _A_L_L_,_N_E_V_E_R, so that nothing is discarded, rejected, or
+          logged.  A common choice is _C_M_N_,_2_5_,_5_0 to reject or discard mail with
+          common bodies except as overridden by the whitelist of the DCC
+          server, the sendmail _$_{_d_c_c___i_s_s_p_a_m_} and _$_{_d_c_c___n_o_t_s_p_a_m_} macros, and
+          --gg, and --ww.
+
+     --gg [_n_o_t_-]_t_y_p_e
+          indicates that whitelisted, _O_K or _O_K_2, counts from the DCC server
+          for a type of checksum are to be believed.  They should be ignored
+          if prefixed with _n_o_t_-.  _T_y_p_e is one of the same set of strings as
+          for --tt.  Only _I_P, _e_n_v___F_r_o_m, and _F_r_o_m are likely choices.  By default
+          all three are honored, and hence the need for _n_o_t_-.
+
+     --SS _h_d_r
+          adds to the list of substitute or locally chosen headers that are
+          checked with the --ww _w_h_i_t_e_c_l_n_t file and sent to the DCC server.  The
+          checksum of the last header of type _h_d_r found in the message is
+          checked.  _H_d_r can be _H_E_L_O to specify the SMTP envelope HELO value.
+          _H_d_r can also be _m_a_i_l___h_o_s_t to specify the host name from the
+          Mail_from value in the SMTP envelope.  As many as six different sub-
+          stitute headers can be specified, but only the checksum of the first
+          of the six will be sent to the DCC server.
+
+     --ll _l_o_g_d_i_r
+          specifies a directory in which files containing copies of messages
+          processed by ddcccciiffdd are kept.  They can be copied to per-user direc-
+          tories specified with --UU.  Information about other recipients of a
+          message is deleted from the per-user copies.
+
+          See the FILES section below concerning the contents of the files.
+          See also the _o_p_t_i_o_n _l_o_g_-_s_u_b_d_i_r_e_c_t_o_r_y_-_{_d_a_y_,_h_o_u_r_,_m_i_n_u_t_e_} lines in
+          _w_h_i_t_e_c_l_n_t files described in dcc(8).
+
+          The directory is relative to the DCC home directory if it is not
+          absolute
+
+     --RR _r_u_n_d_i_r
+          specifies the "run" directory where the file containing the daemon's
+          process ID is stored.  The default value is _/_v_a_r_/_r_u_n_/_d_c_c.
+
+     --TT _t_m_p_d_i_r
+          changes the default directory for temporary files from the default.
+          The default is the directory specified with --ll or the system default
+          if --ll is not used.  The system default is often _/_t_m_p.
+
+     --DD _l_o_c_a_l_-_d_o_m_a_i_n
+          specifies a host or domain name by which the system is known.  There
+          can be several --DD settings.
+
+          To find the per-user log directory and whitelist for each mail
+          recipient, ddcccciiffdd must know each recipient's user name.  The ASCII
+          protocol used between and the MTA includes an optional user name
+          with each SMTP recipient address.  When the user name is absent when
+          the ASCII protocol is used or when the subset of ESMTP enabled with
+          --oo is used, and when the SMTP recipient address includes an _a_t _s_i_g_n
+          (@) each mail address is checked against the list of _l_o_c_a_l_-_d_o_m_a_i_ns.
+          The part of the recipient address remaining after longest matching
+          _l_o_c_a_l_-_d_o_m_a_i_n (if any) is taken as the user name.  The match is
+          anchored at the right or the end of the recipient address.  It must
+          start at a period (.) or _a_t _s_i_g_n (@) in the domain name part of the
+          address.
+
+          If _l_o_c_a_l_-_d_o_m_a_i_n starts with an asterisk (*) indicating a wildcard,
+          preceding sub-domain names are discarded to compute the user name.
+          Otherwise, the computed user name will include any unmatched sub-
+          domain names.
+
+          The default value of _l_o_c_a_l_-_d_o_m_a_i_n when there are no --DD settings is
+          the host name of the system.
+
+     --rr _r_e_j_e_c_t_i_o_n_-_m_s_g
+          specifies the rejection message in --oo proxy mode for unsolicited
+          bulk mail or for mail temporarily blocked by _g_r_e_y_l_i_s_t_i_n_g when --GG is
+          specified.  The first --rr _r_e_j_e_c_t_i_o_n_-_m_s_g replaces the default bulk
+          mail rejection message, "5.7.1 550 mail %ID from %CIP rejected by
+          DCC".  The second replaces "4.2.1 452 mail %ID from %CIP temporary
+          greylist embargoed".  The third --rr _r_e_j_e_c_t_i_o_n_-_m_s_g replaces the
+          default SMTP rejection message "5.7.1 550 %ID bad reputation; see
+          http://commercial-dcc.rhyolite.com/cgi-bin/reps.cgi?tgt=%CIP" for
+          mail with bad DCC Reputations.  If _r_e_j_e_c_t_i_o_n_-_m_s_g is the zero-length
+          string, the --rr setting is counted but the corresponding message is
+          not changed.
+
+          _R_e_j_e_c_t_i_o_n_-_m_s_g can contain specific information about the mail mes-
+          sage.  The following strings starting with % are replaced with the
+          corresponding values:
+              %ID       message ID such as the unique part of log file name or
+                        sendmail queue ID
+              %CIP      SMTP client IP address
+              %BTYPE    type of DNS blacklist hit, such as "SMTP client",
+                        "mail_host", or "URL NS"
+              %BTGT     IP address or name declared bad by DNS blacklist
+              %BPROBE   domain name found in DNS blacklist such as
+                        4.3.2.10.example.com
+              %BRESULT  value of the %BPROBE domain name found in DNS black-
+                        list
+
+          A common alternate for the bulk mail rejection message is "4.7.1 451
+          Access denied by DCC" to tell the sending mail system to continue
+          trying.  Use a 4yz response with caution, because it is likely to
+          delay for days a delivery failure message for false positives.  If
+          the rejection message does not start with an RFC 1893 status code
+          and RFC 2821 reply code, 5.7.1 and 550 or 4.2.1 and 452 are used.
+
+          See also --BB _s_e_t_:_r_e_j_-_m_s_g_=_r_e_j_e_c_t_i_o_n_-_m_s_g to set the status message for
+          mail rejected by DNS blacklists.
+
+     --jj _m_a_x_j_o_b_s
+          limits the number of simultaneous requests that will be processed.
+          The default value is the maximum number that seems to be possible
+          given system limits on open files, select() bit masks, and so forth.
+          Start ddcccciiffdd with --dd and see the starting message in the system log
+          to see the limit.
+
+     --BB _d_n_s_b_l_-_o_p_t_i_o_n
+          enables DNS blacklist checks of the SMTP client IP address, SMTP
+          envelope Mail_From sender domain name, and of host names in URLs in
+          the message body.  Body URL blacklisting has too many false posi-
+          tives to use on abuse mailboxes.  It is less effective than
+          greylisting with dccm(8) or dccifd(8) but can be useful in situa-
+          tions where greylisting cannot be used.
+
+          _D_n_s_b_l_-_o_p_t_i_o_n is either one of the --BB _s_e_t_:_o_p_t_i_o_n forms or
+              --BB _d_o_m_a_i_n[_,_I_P_a_d_d_r[_/_x_x[_,_b_l_t_y_p_e]]]
+          _D_o_m_a_i_n is a DNS blacklist domain such as example.com that will be
+          searched.  _I_P_a_d_d_r[_/_x_x_x] is the string "any" an IP address in the DNS
+          blacklist that indicates that the mail message should be rejected,
+          or a CIDR block covering results from the DNS blacklist.
+          "127.0.0.2" is assumed if _I_P_a_d_d_r is absent.  IPv6 addresses can be
+          specified with the usual colon (:) notation.  Names can be used
+          instead of numeric addresses.  The type of DNS blacklist is speci-
+          fied by _b_l_t_y_p_e as _n_a_m_e, _I_P_v_4, or _I_P_v_6.  Given an envelope sender
+          domain name or a domain name in a URL of spam.domain.org and a
+          blacklist of type _n_a_m_e, spam.domain.org.example.com will be tried.
+          Blacklist types of _I_P_v_4 and _I_P_v_6 require that the domain name in a
+          URL sender address be resolved into an IPv4 or IPv6 address.  The
+          address is then written as a reversed string of decimal octets to
+          check the DNS blacklist, as in 2.0.0.127.example.com,
+
+          More than one blacklist can be specified and blacklists can be
+          grouped.  All searching within a group is stopped at the first posi-
+          tive result.
+
+          Positive results are ignored after being logged unless an
+          _o_p_t_i_o_n _D_N_S_B_L_-_o_n line appears in the global or per-user _w_h_i_t_e_c_l_n_t
+          file.
+
+          --BB _s_e_t_:_n_o_-_c_l_i_e_n_t
+               says that SMTP client IP addresses and reverse DNS domain names
+               should not be checked in the following blacklists.
+               --BB _s_e_t_:_c_l_i_e_n_t restores the default for the following black-
+               lists.
+
+          --BB _s_e_t_:_n_o_-_m_a_i_l___h_o_s_t
+               says that SMTP envelope Mail_From sender domain names should
+               not be checked in the following blacklists.  --BB _s_e_t_:_m_a_i_l___h_o_s_t
+               restores the default.
+
+          --BB _s_e_t_:_n_o_-_U_R_L
+               says that URLs in the message body should not be checked in the
+               in the following blacklists.  --BB _s_e_t_:_U_R_L restores the default.
+
+          --BB _s_e_t_:_n_o_-_M_X
+               says MX servers of sender Mail_From domain names and host names
+               in URLs should not be checked in the following blacklists.
+               --BB _s_e_t_:_M_X restores the default.
+
+          --BB _s_e_t_:_n_o_-_N_S
+               says DNS servers of sender Mail_From domain names and host
+               names in URLs should not be checked in the following black-
+               lists.  --BB _s_e_t_:_N_S restores the default.
+
+          --BB _s_e_t_:_d_e_f_a_u_l_t_s
+               is equivalent to all of --BB _s_e_t_:_n_o_-_t_e_m_p_-_f_a_i_l --BB _s_e_t_:_c_l_i_e_n_t
+               --BB _s_e_t_:_m_a_i_l___h_o_s_t --BB _s_e_t_:_U_R_L --BB _s_e_t_:_M_X and --BB _s_e_t_:_N_S
+
+          --BB _s_e_t_:_g_r_o_u_p_=_X
+               adds later DNS blacklists specified with
+                   --BB _d_o_m_a_i_n[_,_I_P_a_d_d_r[_/_x_x[_,_b_l_t_y_p_e]]]
+               to group 1, 2, or 3.
+
+          --BB _s_e_t_:_d_e_b_u_g_=_X
+               sets the DNS blacklist logging level
+
+          --BB _s_e_t_:_m_s_g_-_s_e_c_s_=_S
+               limits ddcccciiffdd to _S seconds total for checking all DNS black-
+               lists.  The default is 25.
+
+          --BB _s_e_t_:_U_R_L_-_s_e_c_s_=_S
+               limits ddcccciiffdd to at most _S seconds resolving and checking any
+               single URL.  The default is 11.  Some spam contains dozens of
+               URLs and that some "spamvertised" URLs contain host names that
+               need minutes to resolve.  Busy mail systems cannot afford to
+               spend minutes checking each incoming mail message.
+
+          --BB _s_e_t_:_r_e_j_-_m_s_g_=_r_e_j_e_c_t_i_o_n_-_m_s_g
+               sets the SMTP rejection message for the following blacklists.
+               _R_e_j_e_c_t_i_o_n_-_m_s_g must be in the same format as for --rr.  If
+               _r_e_j_e_c_t_i_o_n_-_m_s_g is null, the default is restored.  The default
+               DNS blacklist rejection message is the first message set with
+               --rr.
+
+          --BB _s_e_t_:_t_e_m_p_-_f_a_i_l
+               causes ddcccciiffdd to the MTA to answer the SMTP DATA command with
+                  452 4.2.1 mail %ID from %CIP temporary delayed for DNSBL
+               if any DNS answer required for a DNSBL in the current group
+               times out, including resolving names in URLs.
+
+          --BB _s_e_t_:_n_o_-_t_e_m_p_-_f_a_i_l
+               restores the default of assuming a negative answer for DNS
+               responses that take too long.
+
+          --BB _s_e_t_:_m_a_x_j_o_b_s_=_X
+               sets maximum number of helper processes to _X.  In order to use
+               typical single-threaded DNS resolver libraries, ddcccciiffdd uses
+               fleets of helper processes.  It is rarely a good idea to change
+               the default, which is the same as the maximum number of simul-
+               taneous jobs set with --jj.
+
+          --BB _s_e_t_:_p_r_o_g_p_a_t_h_=_/_v_a_r_/_d_c_c_/_l_i_b_e_x_e_c_/_d_n_s_-_h_e_l_p_e_r
+               changes the path to the helper program.
+
+     --LL _l_t_y_p_e_,_f_a_c_i_l_i_t_y_._l_e_v_e_l
+          specifies how messages should be logged.  _L_t_y_p_e must be _e_r_r_o_r, _i_n_f_o,
+          or _o_f_f to indicate which of the two types of messages are being con-
+          trolled or to turn off all syslog(3) messages from ddcccciiffdd.  _L_e_v_e_l
+          must be a syslog(3) level among _E_M_E_R_G, _A_L_E_R_T, _C_R_I_T, _E_R_R, _W_A_R_N_I_N_G,
+          _N_O_T_I_C_E, _I_N_F_O, and _D_E_B_U_G.  _F_a_c_i_l_i_t_y must be among _A_U_T_H, _A_U_T_H_P_R_I_V,
+          _C_R_O_N, _D_A_E_M_O_N, _F_T_P, _K_E_R_N, _L_P_R, _M_A_I_L, _N_E_W_S, _U_S_E_R, _U_U_C_P, and _L_O_C_A_L_0
+          through _L_O_C_A_L_7.  The default is equivalent to
+                --LL _i_n_f_o_,_M_A_I_L_._N_O_T_I_C_E --LL _e_r_r_o_r_,_M_A_I_L_._E_R_R
+
+     ddcccciiffdd normally sends counts of mail rejected and so forth to the system
+     log at midnight.  The SIGUSR1 signal sends an immediate report to the
+     system log.  The reports will be repeated every 24 hours at the same
+     minute as the signal instead of at midnight.
+
+   PPrroottooccooll
+     DDcccciiffdd uses a simple ASCII protocol to receive mail messages to be
+     checked and to return results.  For each message, the MTA must open a
+     connection to the interface daemon, send options, envelope recipients,
+     and the message, receive the results, and close the connection.
+
+     Instead of the ASCII protocol, a subset of ESMTP is enabled by --oo.  Only
+     the familiar HELO, EHLO, Mail, Rcpt, DATA, RSET, and QUIT commands and
+     the Postfix extensions XFORWARD and XCLIENT are honored.  Since SMTP has
+     no provisions for user names, the protocol enabled by --oo depends on a
+     list of local domain names specified with --DD to find per-user log direc-
+     tories and whitelist files.  If neither XFORWARD nor XCLIENT are used,
+     ddcccciiffdd uses the IP address of the MTA and the value of the HELO command.
+
+     In the ASCII protocol, each of the following lines are sent in order to
+     ddcccciiffdd.  Each ends with a newline ('\n') character.
+       options     zero or more blank-separated strings among:
+                     _s_p_a_m        the message is already known to be spam
+                     _b_o_d_y        return all of the headers with the added
+                                 _X_-_D_C_C header line and the body
+                     _h_e_a_d_e_r      return the _X_-_D_C_C header
+                     _q_u_e_r_y       ask the DCC server about the message without
+                                 reporting it, as if ddcccciiffdd were running with
+                                 --QQ.
+                     _g_r_e_y_-_q_u_e_r_y  only query the greylist server for this mes-
+                                 sage.  --GG _o_n must be in use.
+                     _n_o_-_r_e_j_e_c_t   suppress the overall, one character line 'R'
+                                 result.  This can be useful when using ddcccciiffdd
+                                 only for greylisting.
+                     _l_o_g         ensure that this message is logged as if
+                                 ddcccciiffdd were running with --tt --aallll,,00,,
+       client      IP address of the SMTP client in a "dotted" or "coloned"
+                   ASCII string and reverse-DNS host name.  If the host name
+                   is present, it must follow a carriage return character
+                   ('\r') after the IP address.  The client IP address must be
+                   present and non-null if the host name is present.  The
+                   string "0.0.0.0\n" is understood the same as the null
+                   string, meaning that both the IP address and host name are
+                   absent.  If the client IP address is absent, then the IP
+                   address and host name are taken from the first non-local
+                   Received header if it has the standard "name (name [IP
+                   address])..." format.  Non-standard Received headers com-
+                   monly added by qmail as well as Received headers specifying
+                   IP addresses marked _M_X or _M_X_D_C_C in the global --ww _w_h_i_t_e_c_l_n_t
+                   file are skipped.
+       HELO        SMTP HELO value or nothing, followed by a newline ('\n')
+                   character.  If the HELO value is null and the IP address of
+                   the SMTP client are not supplied, they will be taken from
+                   the same Received: header that supplies the IP address.
+       sender      or SMTP _M_a_i_l _F_r_o_m command value for the env_from checksum.
+                   If the sender is null, the contents of the first Return-
+                   Path: or UNIX style From_ header is used.
+       recipients  or SMTP _R_c_p_t _T_o recipient mailboxes followed by correspond-
+                   ing local user names, one (mailbox,user) pair to a line.
+                   Each optional local user name is separated from the corre-
+                   sponding mailbox recipient address by a carriage return
+                   ('\r').  A local user name can be null if it is not known,
+                   but each recipient mailbox must be non-null.  If there are
+                   no lines of (mailbox,user) pairs and if the _s_p_a_m option is
+                   not included, then the _q_u_e_r_y is assumed.  Mailboxes without
+                   user names will lack per-user log files and will not invoke
+                   a per-user whitelist.
+
+     The last recipient-user name pair is followed by an empty line and the
+     headers and body of the message.  The end of the body of the mail message
+     is signaled by the MTA half-closing the connection.  See shutdown(2).
+
+     DDcccciiffdd responds with three things.  First is a one character line of the
+     overall result advising the MTA:
+       A    accept the message for all recipients and answer the SMTP DATA
+            command with a 2yz result.
+       G    answer with a 4yz result to embargo the message for greylisting.
+       R    reject the message and answer the DATA command with a 5yz result.
+       S    accept the message for some recipients and so answer the DATA com-
+            mand with a 2yz result.
+       T    temporary failure by the DCC system and so answer with a 4yz
+            result.
+
+     Second is a line of characters indicating the disposition of the message
+     for each corresponding recipient:
+       A    deliver the message
+       G    discard the message during a greylist embargo
+       R    discard the message as spam
+     The SMTP protocol allows only a single result for the DATA command for
+     all recipients that were not rejected before body of the message was
+     offered with the DATA command.  To accept the message for some recipients
+     and reject it for others, the MTA must tell the SMTP client it is accept-
+     ing the message for all recipients and then discard it for those that
+     would reject it.
+
+     Finally, if the _b_o_d_y or _h_e_a_d_e_r strings are in the first line of _o_p_t_i_o_n_s
+     sent by the MTA to the daemon, then the _X_-_D_C_C header line or the entire
+     body with the _X_-_D_C_C header line follows.
+
+FFIILLEESS
+     /var/dcc    is the DCC home directory in which other files are found.
+     /var/dcc/libexec/start-dccifd
+                 and
+     /var/dcc/libexec/rcDCC
+                 are scripts used to start the daemon.
+     dcc/dcc_conf
+                 contains parameters used by the scripts to start DCC daemons
+                 and cron jobs.
+     logdir      is an optional directory specified with --ll and containing
+                 marked mail.  Each file in the directory contains one mes-
+                 sage, at least one of whose checksums reached its --tt thresh-
+                 olds or that is interesting for some other reason.  Each file
+                 starts with lines containing the date when the message was
+                 received, the IP address of the SMTP client, and SMTP enve-
+                 lope values.  Those lines are followed by the body of the
+                 SMTP message including its header as it was received.  Only
+                 approximately the first 32 KBytes of the body are recorded
+                 unless modified by _._/_c_o_n_f_i_g_u_r_e _-_-_w_i_t_h_-_m_a_x_-_l_o_g_-_s_i_z_e_=_x_x The
+                 checksums for the message follow the body.  They are followed
+                 by lines indicate that one of the checksums is white- or
+                 blacklisted by the --ww _w_h_i_t_e_c_l_n_t file.  Each log file ends
+                 with the _X_-_D_C_C header line added to the message and the dis-
+                 position of the message.
+     map         is the memory mapped file of information concerning DCC
+                 servers in the DCC home directory.
+     whiteclnt   contains the client whitelist in the format described in
+                 dcc(8).
+     whiteclnt.dccw
+                 is a memory mapped hash table of the _w_h_i_t_e_c_l_n_t file.
+     dccifd.pid  in the --RR _r_u_n_d_i_r directory contains daemon's process ID.
+
+EEXXAAMMPPLLEESS
+     Dccifd can be used as Postfix Before-Queue Content filter.  In some tests
+     these values for --pp and --oo in _d_c_c___c_o_n_f.
+
+         DCCIFD_ENABLE=on
+         DCCIFD_ARGS="-p 127.0.0.1,10025,127.0.0.1/32 -o 127.0.0.1,10026
+
+     worked with these lines in /etc/postfix/master.cf
+
+         smtp      inet  n       -       n       -       -       smtpd
+             -o smtpd_proxy_filter=127.0.0.1:10025
+         127.0.0.1:10026 inet n  -       n       -        -      smtpd
+             -o smtpd_authorized_xforward_hosts=127.0.0.0/8
+             -o smtpd_client_restrictions=
+             -o smtpd_helo_restrictions=
+             -o smtpd_sender_restrictions=
+             -o smtpd_recipient_restrictions=permit_mynetworks,reject
+             -o smtpd_data_restrictions=
+             -o mynetworks=127.0.0.0/8
+             -o receive_override_options=no_unknown_recipient_checks
+
+SSEEEE AALLSSOO
+     cdcc(8), dbclean(8), dcc(8), dccd(8), dblist(8), dccm(8), dccproc(8),
+     dccsight(8),
+
+HHIISSTTOORRYY
+     Implementation of ddcccciiffdd Distributed Checksum Clearinghouses are based on
+     an idea of Paul Vixie with code designed and written at Rhyolite Software
+     starting in 2000.  was started at Rhyolite Software in 2002.  This docu-
+     ment describes version 1.3.103.
+
+BBUUGGSS
+     ddcccciiffdd uses --tt where dccproc(8) uses --cc.
+
+     By default ddcccciiffdd look for its UNIX domain socket in the DCC home direc-
+     tory, but dccm(8) looks in its --RR _r_u_n_d_i_r.
+
+     Systems without setrlimit(2) and getrlimit(2) RLIMIT_NOFILE can have
+     problems with the default limit on the number of simultaneous jobs, the
+     value of --jj.  Every job requires four open files.  These problems are
+     usually seen with errors messages that say something like
+           dccifd[24448]: DCC: accept(): Result too large
+     A fix is to use a smaller value for --jj or to allow ddcccciiffdd to open more
+     files.
+
+                               February 26, 2009
diff -r 000000000000 -r c7f6b056b673 dccifd.8.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccifd.8.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,1329 @@
+.\" Copyright (c) 2008 by Rhyolite Software, LLC
+.\"
+.\" This agreement is not applicable to any entity which sells anti-spam
+.\" solutions to others or provides an anti-spam solution as part of a
+.\" security solution sold to other entities, or to a private network
+.\" which employs the DCC or uses data provided by operation of the DCC
+.\" but does not provide corresponding data to other users.
+.\"
+.\" Permission to use, copy, modify, and distribute this software without
+.\" changes for any purpose with or without fee is hereby granted, provided
+.\" that the above copyright notice and this permission notice appear in all
+.\" copies and any distributed versions or copies are either unchanged
+.\" or not called anything similar to "DCC" or "Distributed Checksum
+.\" Clearinghouse".
+.\"
+.\" Parties not eligible to receive a license under this agreement can
+.\" obtain a commercial license to use DCC by contacting Rhyolite Software
+.\" at sales@rhyolite.com.
+.\"
+.\" A commercial license would be for Distributed Checksum and Reputation
+.\" Clearinghouse software.  That software includes additional features.  This
+.\" free license for Distributed ChecksumClearinghouse Software does not in any
+.\" way grant permision to use Distributed Checksum and Reputation Clearinghouse
+.\" software
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+.\" 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.
+.\"
+.\" Rhyolite Software DCC 1.3.103-1.102 $Revision$
+.\"
+.Dd February 26, 2009
+.ds volume-ds-DCC Distributed Checksum Clearinghouse
+.Dt dccifd 8 DCC
+.Os " "
+.Sh NAME
+.Nm dccifd
+.Nd Distributed Checksum Clearinghouse Interface Daemon
+.Sh SYNOPSIS
+.Bk -words
+.Nm
+.Op Fl VdbxANQ
+.Op Fl G Ar on | off | noIP | IPmask/xx
+.Op Fl h Ar homedir
+.Op Fl I Ar user
+.Op Fl p Ar /sock | host,port,rhost/bits
+.Op Fl o Ar /sock | host,port
+.br
+.Op Fl D Ar local-domain
+.Op Fl m Ar map
+.Op Fl w Ar whiteclnt
+.Op Fl U Ar userdirs
+.br
+.Op Fl a Ar IGNORE | REJECT | DISCARD
+.Oo
+.Fl t Xo
+.Sm off
+.Ar type,
+.Op Ar log-thold,
+.Ar rej-thold
+.Sm on
+.Xc
+.Oc
+.br
+.Oo
+.Fl g Xo
+.Sm off
+.Op Ar not-
+.Ar type
+.Sm on
+.Xc
+.Oc
+.Op Fl S Ar header
+.Op Fl l Ar logdir
+.Op Fl R Ar rundir
+.Op Fl r Ar rejection-msg
+.Op Fl T Ar tmpdir
+.Op Fl j Ar maxjobs
+.br
+.Op Fl B Ar dnsbl-option
+.Op Fl L Ar ltype,facility.level
+.Ek
+.Sh DESCRIPTION
+.Pp
+.Nm
+is a daemon intended to connect spam filters such as SpamAssasin
+and mail transfer agents (MTAs) other than sendmail to DCC servers.
+The MTA or filter
+.Nm
+which in turn reports related checksums to the nearest DCC server
+and adds an
+.Em X-DCC
+SMTP header line to the message.
+The MTA is told to reject the message if it is unsolicited bulk.
+.Pp
+.Nm Dccifd
+is similar to the DCC sendmail milter interface,
+.Xr dccm 8
+and the DCC Procmail interface,
+.Xr dccproc 8 .
+.Nm Dccifd
+is more efficient than
+.Xr dccproc 8
+but not restricted to use with sendmail like
+.Xr dccm 8 .
+All three send reports of checksums related to mail received by DCC clients
+and queries about the total number of reports of particular checksums.
+.Pp
+MTA programs use a simple ASCII protocol a subset of SMTP to send
+a mail message including its SMTP envelope to the daemon.
+.Nm Dccifd
+responds with an indication of whether the message is unsolicited bulk
+and an optional copy of the message with an
+.Em X-DCC
+header added.
+The ASCII protocol is described below and in the
+.Pa include/dccif.h
+file in the DCC source.
+There is a sample C interface routine in the
+.Pa dcclib/dccif.c
+file in the DCC source and the
+.Pa dcclib.a
+library generated from the source.
+A
+.Em Perl
+version of the interface routine is in
+.Pa dccifd/dccif.pl .
+Test or demonstration programs in the style of
+.Xr dccproc 8
+that use those interface routines are in
+.Pa dccifd/dccif-test .
+.Pp
+A subset of ESMTP can be used instead of the ASCII protocol
+to connect
+.Nm
+to postfix as a "Before-Queue Content Filter."
+See the
+.Fl o
+flag.
+.Pp
+Since the checksums of messages that are whitelisted locally
+by the
+.Fl w Ar whiteclnt
+file are not reported to the DCC server,
+.Nm
+knows nothing about the total recipient counts for their checksums and
+so cannot add
+.Em X-DCC
+header lines to such messages.
+.Pp
+Enable the daemon and put its parameters in the
+.Pa dcc_conf
+file and start the daemon with the
+.Pa start-dccifd
+script.
+.Pp
+The list of servers that
+.Nm
+contacts is in the memory mapped file
+.Pa map
+shared by local DCC clients.
+The file is  maintained with
+.Xr cdcc 8 .
+.Ss OPTIONS
+The following options are available:
+.Bl -tag -width 3n
+.It Fl V
+displays the version of
+.Nm .
+.It Fl d
+enables debugging output from the DCC client software.
+Additional
+.Fl d
+options increase the number of messages.
+A single
+.Fl d
+ aborted SMTP transactions including those from some "dictionary attacks."
+.It Fl b
+causes the daemon to not detach itself from the controlling tty
+and put itself into the background.
+.It Fl x
+causes the daemon to try "extra hard" to contact a DCC server.
+Since it is usually more important to deliver mail than to report its
+checksums,
+.Nm
+normally does not delay too long while trying to contact a DCC server.
+It will not try again for several seconds after a failure.
+With
+.Fl x ,
+it will always try to contact the DCC server
+and it will tell the MTA to answer the DATA command with a 4yz
+temporary failure.
+.It Fl A
+adds to existing X-DCC headers in the message
+instead of replacing existing headers
+of the brand of the current server.
+.It Fl N
+neither adds, deletes, nor replaces existing X-DCC headers in the message.
+Each message is logged, rejected, and otherwise handled the same.
+.It Fl Q
+only queries the DCC server about the checksums of messages
+instead of reporting and querying.
+This is useful when
+.Nm
+is used to filter mail that has already been reported to a DCC
+server by another DCC client.
+No single mail message should be reported to a DCC
+server more than once per recipient,
+because each report will increase the apparent "bulkness" of the message.
+.Pp
+It is better to use
+.Em MXDCC
+lines in the global
+.Pa whiteclnt
+file for your MX mail servers that use DCC than
+.Fl Q .
+.It Fl G Ar on | off | noIP | IPmask/xx
+controls
+.Em greylisting .
+At least one working greylist server must be listed in the
+.Pa map
+file in the DCC home directory.
+If more than one is named,
+they must "flood" or change checksums and they must use the
+same
+.Fl G
+parameters.
+See
+.Xr dccd 8 .
+Usually all dccm or dccifd DCC client processes use the same
+.Fl G
+parameters.
+.Pp
+.Ar IPmask/xx
+and
+.Ar noIP
+remove part or all of the IP address from the greylist triple.
+The CIDR block size,
+.Ar xx ,
+must be between 1 and 128.
+96 is added to block sizes smaller than 33 to make them appropriate for
+the IPv6 addresses used by the DCC.
+.Ar IPmask/96
+differs from
+.Ar noIP
+for IPv4 addresses,
+because the former retains the IPv4 to IPv6 mapping prefix.
+.It Fl h Ar homedir
+overrides the default DCC home directory,
+.Pa @prefix@ .
+.It Fl I Ar user
+specifies the UID and GID of the process.
+.It Fl p Ar /sock/name | host,port,rhost/bits
+overrides the default address at which programs contact
+.Nm dccifd .
+The default is a UNIX domain socket named dccifd in the DCC home directory.
+.Pp
+The second form specifies a local host name or IP address,
+a local TCP port number,
+and the host names or IP addresses of computers that can use
+.Nm dccifd .
+127.0.0.1 or
+.Em localhost
+are common choices for
+.Ar host .
+The string
+.Ar @
+specifies IN_ADDRANY or all local IP addresses.
+127.0.0.0/8 is a common choice for
+.Ar rhost/bits .
+.It Fl o Ar /sock | host,port
+enables SMTP proxy mode instead of the ASCII protocol
+and specifies the output connection when
+.Nm
+acts as an SMTP proxy.
+It is the address of the SMTP server
+for which
+.Nm
+acts as SMTP client.
+When
+.Ar /sock
+is
+.Pa /dev/null ,
+.Nm
+acts as if there were downstream SMTP server that always answers "250\ ok".
+The string
+.Ar @
+specifies the same IP address as the incoming TCP connection.
+.Pp
+The input to
+.Nm
+in SMTP proxy mode is specified with
+.Fl -p .
+For example,
+.Fl p Ar 127.0.0.1,10025,127.0.0.1/32 Fl o Ar 127.0.0.1,10026
+could be used to connect
+.Nm
+with Postfix as described in the documentation in version 2.2.1 Postfix
+documentation.
+.Pp
+See below concerning the subset of ESMTP used in this mode.
+.It Fl m Ar map
+specifies a name or path of the memory mapped parameter file instead
+of the default
+.Pa map
+file in the DCC home directory.
+It should be created with the
+.Xr cdcc 8
+command.
+.It Fl w Ar whiteclnt
+specifies an optional file containing filtering parameters
+as well as SMTP client IP addresses,
+SMTP envelope values, and header values
+of mail that is spam or is not spam and does not need a
+.Em X-DCC
+header,
+and whose checksums should not be reported to the DCC server.
+.Pp
+If the pathname
+.Ar whiteclnt
+is not absolute, it is relative to the DCC home directory.
+.Pp
+The format of the
+.Nm
+whiteclnt file is the same as the
+.Pa whitelist
+files used by
+.Xr dbclean 8
+and the
+.Pa whiteclnt
+file used by
+.Xr dccproc 8 .
+See
+.Xr dcc 8
+for a description of DCC white and blacklists.
+Because the contents of the
+.Ar whiteclnt
+file are used frequently, a companion file is automatically
+created and maintained.
+It has the same pathname but with an added suffix of
+.Ar .dccw
+and contains a memory mapped hash table of the main file.
+.Pp
+A whitelist entry ("OK") or two or more semi-whitelistings ("OK2")
+for one of the message's checksums prevents all of
+the message's checksums from being reported to the DCC server 
+and the addition of a
+.Em X-DCC
+header line by
+.Nm
+A whitelist entry for a checksum
+also prevents rejecting or discarding the message based on DCC recipient
+counts as specified by
+.Fl a
+and
+.Fl t .
+Otherwise, one or more checksums with blacklisting entries ("MANY") cause
+all of the message's
+checksums to be reported to the server with an addressee count of "MANY".
+.Pp
+If the message has a single recipient, an
+.Ar env_To
+.Ar whiteclnt
+entry of "OK" for the checksum of its recipient address acts like any other
+.Ar whiteclnt
+entry of "OK."
+When the SMTP message has more than one recipient,
+the effects can be complicated.
+When a message has several recipients with some but not all listed in the
+.Ar whiteclnt
+file,
+.Nm
+tries comply with the wishes of the users who want filtering as
+well as those who don't by silently not delivering the message to 
+those who want filtering (i.e. are not whitelisted) and delivering
+the message to don't want filtering.
+.It Fl U Ar userdirs
+enables per-user
+.Pa whiteclnt
+files and log directories.
+Each target of a message can have a directory of log files named
+.Ar userdirs/addr/log
+where
+.Ar addr
+is the local user or mailbox name computed by the MTA.
+The name of each user's log directory must be
+.Ar log .
+If it is not absolute,
+.Ar userdirs
+is relative to the DCC home directory.
+The directory containing the log files must be named
+.Ar log
+and it must be writable by the
+.Nm
+process.
+Each log directory must exist or logging for the corresponding
+is silently disabled.
+The files created in the log directory are owned by the UID of the
+.Nm
+process,
+but they have
+.Em group
+and
+.Em other
+read and write permissions copied from the corresponding
+.Ar log
+directory.
+To ensure the privacy of mail,
+it may be good to make the directories readable only by
+.Em owner
+and
+.Em group ,
+and to use a
+.Xr cron
+script that changes the owner of each file to match the grandparent
+.Ar addr
+directory.
+.Pp
+There can also be a per -user whitelist file named
+.Ar userdirs/addr/whiteclnt
+for each address
+.Ar addr.
+Any checksum that is not white- or blacklisted by an individual
+addressee's 
+.Pa whiteclnt
+file  is checked in the main
+.Fl w whiteclnt
+file.
+A missing per-addressee
+.Ar whiteclnt
+file is the same as an empty file.
+Relative paths for files included in per-addressee files
+are resolved in the DCC home directory.
+The
+.Ar whiteclnt
+files and the
+.Ar addr
+directories containing them must be writable by the
+.Nm
+process.
+.Pp
+.Ar Option
+lines in per-user whiteclnt files can be used to modify many aspects of
+.Nm
+filtering,
+as described in the main
+.Xr dcc
+man page.
+For example, an
+.Ar option dcc-off
+line turns off DCC filtering for individual mailboxes.
+.It Fl a Ar IGNORE | REJECT | DISCARD
+specifies the action taken when
+.Nm
+is in proxy mode with
+.Fl o
+and
+DCC server counts or
+.Fl t
+thresholds say that a message is unsolicited and bulk.
+.Ar IGNORE
+causes the message to be unaffected except for adding the
+.Em X-DCC
+header line to the message.
+This turns off DCC filtering.
+.Pp
+Spam can also be
+.Ar REJECT Ns ed
+or (when in proxy mode with
+.Fl o )
+accepted and silently
+.Ar DISCARD Ns ed
+without being delivered to local mailboxes.
+The default is
+.Ar REJECT .
+.Pp
+Mail forwarded via IP addresses marked
+.Em MX
+or
+.Em MXDCC
+in the main
+.Pa whiteclnt
+file is treated
+as if
+.Fl a Ar DISCARD
+were specified.
+This prevents "bouncing" spam.
+.Pp
+The effects of the
+.Fl w Ar whiteclnt
+are not affected by
+.Fl a .
+.It Fl t Xo
+.Sm off
+.Ar type,
+.Op Ar log-thold,
+.Ar rej-thold
+.Sm on
+.Xc
+sets logging and "spam" thresholds for checksum
+.Ar type .
+The checksum types are
+.Ar IP ,
+.Ar env_From ,
+.Ar From ,
+.Ar Message-ID ,
+.Ar substitute ,
+.Ar Received ,
+.Ar Body ,
+.Ar Fuz1 ,
+.Ar Fuz2 ,
+.Ar rep-total ,
+and
+.Ar rep .
+The first six,
+.Ar IP
+through
+.Ar substitute ,
+have no effect except when a local DCC server configured with
+.Fl K
+is used.
+The
+.Ar substitute
+thresholds apply to the first substitute heading encountered in the mail
+message.
+The string
+.Ar ALL
+sets thresholds for all types, but is unlikely to be useful except for
+setting logging thresholds.
+The string
+.Ar CMN
+specifies the commonly used checksums
+.Ar Body ,
+.Ar Fuz1 ,
+and
+.Ar Fuz2 .
+.Ar Rej-thold
+and
+.Ar log-thold
+must be numbers, the string
+.Ar NEVER ,
+or the string
+.Ar MANY
+indicating millions of targets.
+Counts from the DCC server as large as the threshold for any single type
+are taken as sufficient evidence
+that the message should be logged or rejected.
+.Pp
+.Ar Log-thold
+is the threshold at which messages are logged.
+It can be handy to log messages at a lower threshold to find
+solicited bulk mail sources such as mailing lists.
+If no logging threshold is set,
+only rejected mail and messages with complicated combinations of white
+and blacklisting are logged.
+Messages that reach at least one of their rejection thresholds are
+logged regardless of logging thresholds.
+.Pp
+.Ar Rej-thold
+is the threshold at which messages are considered "bulk,"
+and so should be rejected or discarded if not whitelisted.
+.Pp
+DCC Reputation thresholds in the commercial version
+of the DCC are controlled by thresholds on checksum types
+.Ar rep
+and
+.Ar rep-total .
+Messages from an IP address that the DCC database says has sent
+more than
+.Fl t Ar rep-total,log-thold
+messages are logged.
+A DCC Reputation is computed for messages received
+from IP addresses that
+have sent more than
+.Fl t Ar rep-total,log-thold
+messages.
+The DCC Reputation of an IP address is the percentage of its messages
+that have been detected as bulk
+or having at least 10 recipients.
+The defaults are equivalent to
+.Fl t Ar rep,never
+and
+.Fl t Ar rep-total,never,20 .
+.Pp
+Bad DCC Reputations do not reject mail unless enabled by an
+.Ar option DCC-rep-on
+line in a
+.Pa whiteclnt
+file.
+.Pp
+The checksums of locally whitelisted messages are not checked with
+the DCC server and so only the number of targets of the current copy of
+a whitelisted message are compared against the thresholds.
+.Pp
+The default is
+.Ar ALL,NEVER ,
+so that nothing is discarded, rejected, or logged.
+A common choice is
+.Ar CMN,25,50
+to reject or discard
+mail with common bodies except as overridden by
+the whitelist of the DCC server, the sendmail
+.Em ${dcc_isspam}
+and
+.Em ${dcc_notspam}
+macros, and
+.Fl g ,
+and
+.Fl w .
+.It Fl g Xo
+.Sm off
+.Op Ar not-
+.Ar type
+.Sm on
+.Xc
+indicates that whitelisted,
+.Ar OK
+or
+.Ar OK2 ,
+counts from the DCC server for a type of checksum are to be believed.
+They should be ignored if prefixed with
+.Ar not- .
+.Ar Type
+is one of the same set of strings as for
+.Fl t .
+Only
+.Ar IP ,
+.Ar env_From ,
+and
+.Ar From
+are likely choices.
+By default all three are honored,
+and hence the need for
+.Ar not- .
+.It Fl S Ar hdr
+adds to the list of substitute or locally chosen headers that
+are checked with the
+.Fl w Ar whiteclnt
+file and sent to the DCC server.
+The checksum of the last header of type
+.Ar hdr
+found in the message is checked.
+.Ar Hdr
+can be
+.Em HELO
+to specify the SMTP envelope HELO value.
+.Ar Hdr
+can also be
+.Em mail_host
+to specify the host name from
+the Mail_from value in the SMTP envelope.
+As many as six different substitute headers can be specified, but only
+the checksum of the first of the six will be sent to the DCC server.
+.It Fl l Ar logdir
+specifies a directory in which files containing copies of messages processed by
+.Nm
+are kept.
+They can be copied to per-user directories specified with
+.Fl U .
+Information about other recipients of a message is deleted from
+the per-user copies.
+.Pp
+See the FILES section below concerning the contents of the files.
+See also the
+.Ar option log-subdirectory-{day,hour,minute}
+lines in
+.Pa whiteclnt
+files described in
+.Xr dcc 8 .
+.Pp
+The directory is relative to the DCC home directory if it is not absolute
+.It Fl R Ar rundir
+specifies the "run" directory where the file
+containing the daemon's process ID is stored.
+The default value is
+.Pa @dcc_rundir@ .
+.It Fl T Ar tmpdir
+changes the default directory for temporary files from the default.
+The default is the directory specified with
+.Fl l
+or the system default if
+.Fl l
+is not used.
+The system default is often
+.Pa /tmp .
+.It Fl D Ar local-domain
+specifies a host or domain name by which the system is known.
+There can be several
+.Fl D
+settings.
+.Pp
+To find the per-user log directory and whitelist for each mail recipient,
+.Nm
+must know each recipient's user name.
+The ASCII protocol used between
+.nm
+and the MTA includes an optional user name with each
+SMTP recipient address.
+When the user name is absent when the ASCII protocol is used or when
+the subset of ESMTP enabled with
+.Fl o
+is used,
+and when the SMTP recipient address includes an
+.Em at sign
+(@)
+each mail address is checked against the
+list of
+.Ar local-domain Ns s.
+The part of the recipient address remaining after longest matching
+.Ar local-domain
+(if any) is taken as the user name.
+The match is anchored at the right or the end of the recipient address.
+It must start at a period (.) or
+.Em at sign
+(@) in the domain name part of the address.
+.Pp
+If
+.Ar local-domain
+starts with an asterisk (*) indicating a wildcard,
+preceding sub-domain names are discarded to compute the user name.
+Otherwise, the computed user name will include any unmatched sub-domain
+names.
+.Pp
+The default value of
+.Ar local-domain
+when there are no
+.Fl D
+settings is the host name of the system.
+.It Fl r Ar rejection-msg
+specifies the rejection message
+in
+.Fl o
+proxy mode
+for unsolicited bulk mail or for mail temporarily blocked by
+.Em greylisting
+when
+.Fl G
+is specified.
+The first
+.Fl r Ar rejection-msg
+replaces the default bulk mail rejection message,
+.Bk -words
+"5.7.1 550 mail %ID from %CIP rejected by DCC".
+.Ek
+." see rej_def in reply.c
+The second replaces
+.Bk -words
+"4.2.1 452 mail %ID from %CIP temporary greylist embargoed".
+.Ek
+." see grey_def in reply.c
+The third
+.Fl r Ar rejection-msg
+replaces the default SMTP rejection message
+.Bk -words
+"5.7.1 550 %ID bad reputation; see http://commercial-dcc.rhyolite.com/cgi-bin/reps.cgi?tgt=%CIP"
+.Ek
+for mail with bad DCC Reputations.
+If
+.Ar rejection-msg
+is the zero-length string,
+the
+.Fl r
+setting is counted but the corresponding message is not changed.
+.Pp
+.Ar Rejection-msg
+can contain specific information about the mail message.
+The following strings starting with % are replaced with the corresponding
+values:
+.Bl -tag -width "%BRESULT" -offset 4n -compact
+.It %ID
+message ID such as the unique part of log file name or sendmail queue ID
+.It %CIP
+SMTP client IP address
+.It %BTYPE
+type of DNS blacklist hit, such as "SMTP client", "mail_host", or "URL NS"
+.It %BTGT
+IP address or name declared bad by DNS blacklist
+.It %BPROBE
+domain name found in DNS blacklist such as 4.3.2.10.example.com
+.It %BRESULT
+value of the %BPROBE domain name found in DNS blacklist
+.El
+.Pp
+A common alternate for the bulk mail rejection message is
+.Bk -words
+"4.7.1 451 Access denied by DCC"
+.Ek
+to tell the sending mail system to continue trying.
+Use a 4yz response with caution, because it is likely to delay for days
+a delivery failure message for false positives.
+If the rejection message
+does not start with an RFC 1893 status code and RFC 2821 reply code,
+5.7.1 and 550 or 4.2.1 and 452 are used.
+.Pp
+See also
+.Fl B Ar set:rej-msg=rejection-msg
+to set the status message for mail rejected by DNS blacklists.
+.It Fl j Ar maxjobs
+limits the number of simultaneous requests that will be processed.
+The default value is the maximum number that seems to be possible given system
+limits on open files, select() bit masks, and so forth.
+Start
+.Nm
+with
+.Fl d
+and see the starting message in the system log to see the limit.
+.It Fl B Ar dnsbl-option
+enables DNS blacklist checks of the SMTP client IP address, SMTP envelope
+Mail_From sender domain name, and of host names in URLs in the message body.
+Body URL blacklisting has too many false positives to use on
+abuse mailboxes.
+It is less effective than greylisting with
+.Xr dccm 8
+or
+.Xr dccifd 8
+but can be useful in situations where
+greylisting cannot be used.
+.Pp
+.Ar Dnsbl-option
+is either one of the
+.Fl B Ar set:option
+forms or
+.Bd -literal -compact -offset 4n
+.Fl B Xo
+.Sm off
+.Ar domain Oo Ar ,IPaddr
+.Op Ar /xx Op Ar ,bltype Oc
+.Sm on
+.Xc
+.Ed
+.Ar Domain
+is a DNS blacklist domain such as example.com
+that will be searched.
+.Ar IPaddr Ns Op Ar /xxx
+is the string "any"
+an IP address in the DNS blacklist
+that indicates that the mail message
+should be rejected,
+or a CIDR block covering results from the DNS blacklist.
+"127.0.0.2" is assumed if
+.Ar IPaddr
+is absent.
+IPv6 addresses can be specified with the usual colon (:) notation.
+Names can be used instead of numeric addresses.
+The type of DNS blacklist
+is specified by
+.Ar bltype
+as
+.Ar name ,
+.Ar IPv4 ,
+or
+.Ar IPv6 .
+Given an envelope sender domain name or a domain name in a URL of
+spam.domain.org
+and a blacklist of type
+.Ar name ,
+spam.domain.org.example.com will be tried.
+Blacklist types of
+.Ar IPv4
+and
+.Ar IPv6
+require that the domain name in a URL sender address
+be resolved into an IPv4 or IPv6
+address.
+The address is then written as a reversed string of decimal
+octets to check the DNS blacklist, as in 2.0.0.127.example.com,
+.Pp
+More than one blacklist can be specified and blacklists can be grouped.
+All searching within a group is stopped at the first positive result.
+.Pp
+Positive results are ignored after being logged unless an
+.Ar option\ DNSBL-on
+line appears in the global or per-user
+.Pa whiteclnt
+file.
+.Pp
+.Bl -tag -width 3n
+.It Fl B Ar set:no-client
+says that SMTP client IP addresses and reverse DNS domain names should
+not be checked in the following blacklists.
+.br
+.Fl B Ar set:client
+restores the default for the following blacklists.
+.It Fl B Ar set:no-mail_host
+says that SMTP envelope Mail_From sender domain names should
+not be checked in the following blacklists.
+.Fl B Ar set:mail_host
+restores the default.
+.It Fl B Ar set:no-URL
+says that URLs in the message body should not be checked in the
+in the following blacklists.
+.Fl B Ar set:URL
+restores the default.
+.It Fl B Ar set:no-MX
+says MX servers of sender Mail_From domain names and host names in URLs
+should not be checked in the following blacklists.
+.br
+.Fl B Ar set:MX
+restores the default.
+.It Fl B Ar set:no-NS
+says DNS servers of sender Mail_From domain names and host names in URLs
+should not be checked in the following blacklists.
+.Fl B Ar set:NS
+restores the default.
+.It Fl B Ar set:defaults
+is equivalent to all of
+.Fl B Ar set:no-temp-fail
+.Fl B Ar set:client
+.br
+.Fl B Ar set:mail_host
+.Fl B Ar set:URL
+.Fl B Ar set:MX
+and
+.Fl B Ar set:NS
+.It Fl B Ar set:group=X
+adds later DNS blacklists specified with
+.Bd -literal -compact -offset 4n
+.Fl B Xo
+.Sm off
+.Ar domain Oo Ar ,IPaddr
+.Op Ar /xx Op Ar ,bltype Oc
+.Sm on
+.Xc
+.Ed
+to group 1, 2, or 3.
+.It Fl B Ar set:debug=X
+sets the DNS blacklist logging level
+.It Fl B Ar set:msg-secs=S
+limits
+.Nm
+to
+.Ar S
+seconds total for checking all DNS blacklists.
+The default is 25.
+.It Fl B Ar set:URL-secs=S
+limits
+.Nm
+to at most
+.Ar S
+seconds resolving and checking any single URL.
+The default is 11.
+Some spam contains dozens of URLs and that
+some "spamvertised" URLs contain host names that need minutes to
+resolve.
+Busy mail systems cannot afford to spend minutes checking each incoming
+mail message.
+.It Fl B Ar set:rej-msg=rejection-msg
+sets the SMTP rejection message for the following blacklists.
+.Ar Rejection-msg
+must be in the same format as for
+.Fl r .
+If
+.Ar rejection-msg
+is null, the default is restored.
+The default DNS blacklist rejection message is the first message set
+with
+.Fl r .
+.It Fl B Ar set:temp-fail
+causes
+.Nm
+to the MTA to answer the SMTP DATA command with
+.Bd -literal -offset 3n -compact
+452 4.2.1 mail %ID from %CIP temporary delayed for DNSBL
+.Ed
+if any DNS answer required for a DNSBL in the current group times out,
+including resolving names in URLs.
+.It Fl B Ar set:no-temp-fail
+restores the default of assuming a negative answer for DNS responses
+that take too long.
+.It Fl B Ar set:maxjobs=X
+sets maximum number of helper processes to
+.Ar X .
+In order to use typical single-threaded DNS resolver libraries,
+.Nm
+uses fleets of helper processes.
+It is rarely a good idea to change the default,
+which is the same as the maximum number of simultaneous jobs set with
+.Fl j .
+.It Fl B Ar set:progpath=@libexecdir@/dns-helper
+changes the path to the helper program.
+.El
+.It Fl L Ar ltype,facility.level
+specifies how messages should be logged.
+.Ar Ltype
+must be
+.Ar error ,
+.Ar info ,
+or
+.Ar off
+to indicate which of the two types of messages are being controlled or
+to turn off all
+.Xr syslog 3
+messages from
+.Nm .
+.Ar Level
+must be a
+.Xr syslog 3
+level among
+.Ar EMERG ,
+.Ar ALERT ,
+.Ar CRIT , ERR ,
+.Ar WARNING ,
+.Ar NOTICE ,
+.Ar INFO ,
+and
+.Ar DEBUG .
+.Ar Facility
+must be among
+.Ar AUTH ,
+.Ar AUTHPRIV ,
+.Ar CRON ,
+.Ar DAEMON ,
+.Ar FTP ,
+.Ar KERN ,
+.Ar LPR ,
+.Ar MAIL ,
+.Ar NEWS ,
+.Ar USER ,
+.Ar UUCP ,
+and
+.Ar LOCAL0
+through
+.Ar LOCAL7 .
+The default is equivalent to
+.Dl Fl L Ar info,MAIL.NOTICE  Fl L Ar error,MAIL.ERR
+.El
+.Pp
+.Nm
+normally sends counts of mail rejected and so forth to the system log at
+midnight.
+The SIGUSR1 signal sends an immediate report to the system log.
+The reports will be repeated every 24 hours at the same minute as the signal
+instead of at midnight.
+.Ss Protocol
+.Nm Dccifd
+uses a simple ASCII protocol to receive mail messages to be checked and
+to return results.
+For each message, the MTA must open a connection to the interface daemon,
+send options, envelope recipients, and the message, receive the results,
+and close the connection.
+.Pp
+Instead of the ASCII protocol, a subset of ESMTP is enabled by
+.Fl o .
+Only the familiar HELO, EHLO, Mail, Rcpt, DATA, RSET, and QUIT
+commands and the Postfix extensions XFORWARD and XCLIENT are honored.
+Since SMTP has no provisions for user names,
+the protocol enabled by
+.Fl o
+depends on a list of local domain names specified with
+.Fl D
+to find per-user log directories and whitelist files.
+If neither XFORWARD nor XCLIENT are used,
+.Nm
+uses the IP address of the MTA and the value of the HELO command.
+.Pp
+In the ASCII protocol, each of the following lines are sent in order to
+.Nm .
+Each ends with a newline ('\\n') character.
+.Bl -tag -offset 2n -width "recipients" -compact
+.It options
+zero or more blank-separated strings among:
+.Bl -tag -offset 2n -width grey-query -compact
+.It Ar spam
+the message is already known to be spam
+.It Ar body
+return all of the headers with the added
+.Em X-DCC
+header line and the body
+.It Ar header
+return the
+.Em X-DCC
+header
+.It Ar query
+ask the DCC server about the message without reporting it, as if
+.Nm
+were running with
+.Fl Q .
+.It Ar grey-query
+only query the greylist server for this message.
+.Fl G Ar on
+must be in use.
+.It Ar no-reject
+suppress the overall, one character line 'R' result.
+This can be useful when using
+.Nm
+only for greylisting.
+.It Ar log
+ensure that this message is logged as if
+.Nm
+were running with
+.Fl t all,0,
+.El
+.It client
+IP address of the SMTP client in a "dotted" or "coloned" ASCII string
+and reverse-DNS host name.
+If the host name is present,
+it must follow a carriage return character ('\\r') after the IP address.
+The client IP address must be present and non-null if the host name is present.
+The string "0.0.0.0\\n" is understood the same as the null string,
+meaning that both the IP address and host name are absent.
+If the client IP address is absent, then the IP address and host name
+are taken from the first non-local Received header if it has the standard
+"name (name [IP address])..." format.
+Non-standard Received headers commonly added by qmail as well as
+Received headers specifying IP addresses marked
+.Em MX
+or
+.Em MXDCC
+in the global
+.Fl w Ar whiteclnt
+file are skipped.
+.It HELO
+SMTP HELO value or nothing, followed by a newline ('\\n') character.
+If the HELO value is null and the IP address of the SMTP client are not
+supplied, they will be
+taken from the same Received: header that supplies the IP address.
+.It sender
+or SMTP
+.Em Mail From
+command value for the env_from checksum.
+If the sender is null,
+the contents of the first Return-Path: or UNIX style From_ header
+is used.
+.It recipients
+or SMTP
+.Em Rcpt To
+recipient mailboxes followed by corresponding local user names,
+one (mailbox,user) pair to a line.
+Each optional local user name is separated from the
+corresponding mailbox recipient address by a carriage return ('\\r').
+A local user name can be null if it is not known, but each recipient
+mailbox must be non-null.
+If there are no lines of (mailbox,user) pairs and if the
+.Ar spam
+option is not included, then the
+.Ar query
+is assumed.
+Mailboxes without user names will lack per-user log files
+and will not invoke a per-user whitelist.
+.El
+.Pp
+The last recipient-user name pair is followed by an empty line
+and the headers and body of the message.
+The end of the body of the mail message is signaled by the MTA
+half-closing the connection.
+See
+.Xr shutdown 2 .
+.Pp
+.Nm Dccifd
+responds with three things.
+First is a one character line of the overall result advising the MTA:
+.Bl -tag -offset 2n -width 3n -compact
+.It A
+accept the message for all recipients and answer the SMTP DATA command
+with a 2yz result.
+.It G
+answer with a 4yz result to embargo the message for greylisting.
+.It R
+reject the message and answer the DATA command with a 5yz result.
+.It S
+accept the message for some recipients
+and so answer the DATA command with a 2yz result.
+.It T
+temporary failure by the DCC system and so answer with a 4yz result.
+.El
+.Pp
+Second is a line of characters indicating the disposition of the
+message for each corresponding recipient:
+.Bl -tag -offset 2n -width 3n -compact
+.It A
+deliver the message
+.It G
+discard the message during a greylist embargo
+.It R
+discard the message as spam
+.El
+The SMTP protocol allows only a single
+result for the DATA command for all recipients that were not rejected
+before body of the message was offered with the DATA command.
+To accept the message for some recipients and reject it for others,
+the MTA must tell the SMTP client it is accepting the message for all
+recipients and then discard it for those that would reject it.
+.Pp
+Finally, if the
+.Em body
+or
+.Em header
+strings are in the first line of
+.Em options
+sent by the MTA to the daemon,
+then the
+.Em X-DCC
+header line
+or the entire body with the
+.Em X-DCC
+header line follows.
+.Sh FILES
+.Bl -tag -width dccifd.pid -compact
+.It Pa @prefix@
+is the DCC home directory in which other files are found.
+.It Pa @libexecdir@/start-dccifd
+and
+.It Pa @libexecdir@/rcDCC
+are scripts used to start the daemon.
+.It Pa dcc/dcc_conf
+contains parameters used by the scripts to start DCC daemons and cron jobs.
+.It Pa logdir
+is an optional directory specified with
+.Fl l
+and containing marked mail.
+Each file in the directory contains one message, at least one of whose
+checksums reached its
+.Fl t
+thresholds or that is interesting for some other reason.
+Each file starts with lines containing the date when the message
+was received, the IP address of the SMTP client, and SMTP envelope
+values.
+Those lines are followed by the body of the SMTP message including its header
+as it was received.
+Only approximately the first 32 KBytes of the body are recorded
+unless modified by
+.Em ./configure --with-max-log-size=xx
+The checksums for the message follow the body.
+They are followed by lines indicate that
+one of the checksums is white- or blacklisted by the
+.Fl w Ar whiteclnt
+file.
+Each log file ends with the
+.Em X-DCC
+header line added to the message and the disposition of
+the message.
+.It Pa map
+is the memory mapped file of information concerning DCC servers
+in the DCC home directory.
+.It Pa whiteclnt
+contains the client whitelist in
+the format described in
+.Xr dcc 8 .
+.It Pa whiteclnt.dccw
+is a memory mapped hash table of the
+.Pa whiteclnt
+file.
+.It Pa dccifd.pid
+in the
+.Fl R Ar rundir
+directory contains daemon's process ID.
+.El
+.Sh EXAMPLES
+Dccifd can be used as Postfix Before-Queue Content filter.
+In some tests these
+values for
+.Fl p
+and
+.Fl o
+in
+.Pa dcc_conf .
+.Bd -literal -offset 4n
+DCCIFD_ENABLE=on
+DCCIFD_ARGS="-p 127.0.0.1,10025,127.0.0.1/32 -o 127.0.0.1,10026
+.Ed
+.Pp
+worked with these lines in /etc/postfix/master.cf
+.Bd -literal -offset 4n
+smtp      inet  n       -       n       -       -       smtpd
+    -o smtpd_proxy_filter=127.0.0.1:10025
+127.0.0.1:10026 inet n  -       n       -	 -      smtpd
+    -o smtpd_authorized_xforward_hosts=127.0.0.0/8
+    -o smtpd_client_restrictions=
+    -o smtpd_helo_restrictions=
+    -o smtpd_sender_restrictions=
+    -o smtpd_recipient_restrictions=permit_mynetworks,reject
+    -o smtpd_data_restrictions=
+    -o mynetworks=127.0.0.0/8
+    -o receive_override_options=no_unknown_recipient_checks
+.Ed
+.Sh SEE ALSO
+.Xr cdcc 8 ,
+.Xr dbclean 8 ,
+.Xr dcc 8 ,
+.Xr dccd 8 ,
+.Xr dblist 8 ,
+.Xr dccm 8 ,
+.Xr dccproc 8 ,
+.Xr dccsight 8 ,
+.Sh HISTORY
+Implementation of
+.Nm
+Distributed Checksum Clearinghouses are based on an idea of Paul Vixie
+with code designed and written at Rhyolite Software starting in 2000.
+was started at Rhyolite Software in 2002.
+This document describes version 1.3.103.
+.Sh BUGS
+.Nm
+uses
+.Fl t
+where
+.Xr dccproc 8
+uses
+.Fl c .
+.Pp
+By default
+.Nm
+look for its UNIX domain socket in the DCC home directory,
+but
+.Xr dccm 8
+looks in its
+.Fl R Ar rundir .
+.Pp
+Systems without
+.Xr setrlimit 2
+and
+.Xr getrlimit 2
+RLIMIT_NOFILE
+can have problems with the default limit on the number of simultaneous
+jobs, the value of
+.Fl j .
+Every job requires four open files.
+These problems are usually seen with errors messages that say something like
+.Dl dccifd[24448]: DCC: accept(): Result too large
+A fix is to use a smaller value for
+.Fl j
+or to allow
+.Nm
+to open more files.
diff -r 000000000000 -r c7f6b056b673 dccifd.html.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccifd.html.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,726 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<HTML>
+<HEAD>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <TITLE>dccifd.0.8</TITLE>
+    <META http-equiv="Content-Style-Type" content="text/css">
+    <STYLE type="text/css">
+	BODY {background-color:white; color:black}
+	ADDRESS {font-size:smaller}
+        IMG.logo {width:6em; vertical-align:middle}
+    </STYLE>
+</HEAD>
+<BODY>
+<PRE>
+<!-- Manpage converted by man2html 3.0.1 -->
+<B><A HREF="dccifd.html">dccifd(8)</A></B>             Distributed Checksum Clearinghouse             <B><A HREF="dccifd.html">dccifd(8)</A></B>
+
+
+</PRE>
+<H2><A NAME="NAME">NAME</A></H2><PRE>
+     <B>dccifd</B> -- Distributed Checksum Clearinghouse Interface Daemon
+
+
+</PRE>
+<H2><A NAME="SYNOPSIS">SYNOPSIS</A></H2><PRE>
+     <B>dccifd</B> [<B>-VdbxANQ</B>] [<B>-G</B> <I>on</I> | <I>off</I> | <I>noIP</I> | <I>IPmask/xx</I>] [<B>-h</B> <I>homedir</I>] [<B>-I</B> <I>user</I>]
+            [<B>-p</B> <I>/sock</I> | <I>host,port,rhost/bits</I>] [<B>-o</B> <I>/sock</I> | <I>host,port</I>]
+            [<B>-D</B> <I>local-domain</I>] [<B>-m</B> <I>map</I>] [<B>-w</B> <I>whiteclnt</I>] [<B>-U</B> <I>userdirs</I>]
+            [<B>-a</B> <I>IGNORE</I> | <I>REJECT</I> | <I>DISCARD</I>] [<B>-t</B> <I>type,</I>[<I>log-thold,</I>]<I>rej-thold</I>]
+            [<B>-g</B> [<I>not-</I>]<I>type</I>] [<B>-S</B> <I>header</I>] [<B>-l</B> <I>logdir</I>] [<B>-R</B> <I>rundir</I>]
+            [<B>-r</B> <I>rejection-msg</I>] [<B>-T</B> <I>tmpdir</I>] [<B>-j</B> <I>maxjobs</I>]
+            [<B>-B</B> <I>dnsbl-option</I>] [<B>-L</B> <I>ltype,facility.level</I>]
+
+
+</PRE>
+<H2><A NAME="DESCRIPTION">DESCRIPTION</A></H2><PRE>
+     <B>dccifd</B> is a daemon intended to connect spam filters such as SpamAssasin
+     and mail transfer agents (MTAs) other than sendmail to DCC servers.  The
+     MTA or filter <B>dccifd</B> which in turn reports related checksums to the near-
+     est DCC server and adds an <I>X-DCC</I> SMTP header line to the message.  The
+     MTA is told to reject the message if it is unsolicited bulk.
+
+     <B>Dccifd</B> is similar to the DCC sendmail milter interface, <B><A HREF="dccm.html">dccm(8)</A></B> and the
+     DCC Procmail interface, <B><A HREF="dccproc.html">dccproc(8)</A></B>.  <B>Dccifd</B> is more efficient than
+     <B><A HREF="dccproc.html">dccproc(8)</A></B> but not restricted to use with sendmail like <B><A HREF="dccm.html">dccm(8)</A></B>.  All
+     three send reports of checksums related to mail received by DCC clients
+     and queries about the total number of reports of particular checksums.
+
+     MTA programs use a simple ASCII protocol a subset of SMTP to send a mail
+     message including its SMTP envelope to the daemon.  <B>Dccifd</B> responds with
+     an indication of whether the message is unsolicited bulk and an optional
+     copy of the message with an <I>X-DCC</I> header added.  The ASCII protocol is
+     described below and in the <I>include/dccif.h</I> file in the DCC source.  There
+     is a sample C interface routine in the <I>dcclib/dccif.c</I> file in the DCC
+     source and the <I>dcclib.a</I> library generated from the source.  A <I>Perl</I> ver-
+     sion of the interface routine is in <I>dccifd/dccif.pl</I>.  Test or demonstra-
+     tion programs in the style of <B><A HREF="dccproc.html">dccproc(8)</A></B> that use those interface rou-
+     tines are in <I>dccifd/dccif-test</I>.
+
+     A subset of ESMTP can be used instead of the ASCII protocol to connect
+     <B>dccifd</B> to postfix as a "Before-Queue Content Filter."  See the <B>-o</B> flag.
+
+     Since the checksums of messages that are whitelisted locally by the <B>-w</B>
+     <I>whiteclnt</I> file are not reported to the DCC server, <B>dccifd</B> knows nothing
+     about the total recipient counts for their checksums and so cannot add
+     <I>X-DCC</I> header lines to such messages.
+
+     Enable the daemon and put its parameters in the <I>dcc</I><B>_</B><I>conf</I> file and start
+     the daemon with the <I>start-dccifd</I> script.
+
+     The list of servers that <B>dccifd</B> contacts is in the memory mapped file <I>map</I>
+     shared by local DCC clients.  The file is  maintained with <B><A HREF="cdcc.html">cdcc(8)</A></B>.
+
+   <A NAME="OPTIONS"><B>OPTIONS</B></A>
+     The following options are available:
+
+     <A NAME="OPTION-V"><B>-V</B></A>   displays the version of <B>dccifd</B>.
+
+     <A NAME="OPTION-d"><B>-d</B></A>   enables debugging output from the DCC client software.  Additional
+          <B>-d</B> options increase the number of messages.  A single <B>-d</B>
+           aborted SMTP transactions including those from some "dictionary
+          attacks."
+
+     <A NAME="OPTION-b"><B>-b</B></A>   causes the daemon to not detach itself from the controlling tty and
+          put itself into the background.
+
+     <A NAME="OPTION-x"><B>-x</B></A>   causes the daemon to try "extra hard" to contact a DCC server.
+          Since it is usually more important to deliver mail than to report
+          its checksums, <B>dccifd</B> normally does not delay too long while trying
+          to contact a DCC server.  It will not try again for several seconds
+          after a failure.  With <B>-x</B>, it will always try to contact the DCC
+          server and it will tell the MTA to answer the DATA command with a
+          4yz temporary failure.
+
+     <A NAME="OPTION-A"><B>-A</B></A>   adds to existing X-DCC headers in the message instead of replacing
+          existing headers of the brand of the current server.
+
+     <A NAME="OPTION-N"><B>-N</B></A>   neither adds, deletes, nor replaces existing X-DCC headers in the
+          message.  Each message is logged, rejected, and otherwise handled
+          the same.
+
+     <A NAME="OPTION-Q"><B>-Q</B></A>   only queries the DCC server about the checksums of messages instead
+          of reporting and querying.  This is useful when <B>dccifd</B> is used to
+          filter mail that has already been reported to a DCC server by
+          another DCC client.  No single mail message should be reported to a
+          DCC server more than once per recipient, because each report will
+          increase the apparent "bulkness" of the message.
+
+          It is better to use <I>MXDCC</I> lines in the global <I>whiteclnt</I> file for
+          your MX mail servers that use DCC than <B>-Q</B>.
+
+     <A NAME="OPTION-G"><B>-G</B></A> <I>on</I> | <I>off</I> | <I>noIP</I> | <I>IPmask/xx</I>
+          controls <I>greylisting</I>.  At least one working greylist server must be
+          listed in the <I>map</I> file in the DCC home directory.  If more than one
+          is named, they must "flood" or change checksums and they must use
+          the same <B>-G</B> parameters.  See <B><A HREF="dccd.html">dccd(8)</A></B>.  Usually all dccm or dccifd
+          DCC client processes use the same <B>-G</B> parameters.
+
+          <I>IPmask/xx</I> and <I>noIP</I> remove part or all of the IP address from the
+          greylist triple.  The CIDR block size, <I>xx</I>, must be between 1 and
+          128.  96 is added to block sizes smaller than 33 to make them appro-
+          priate for the IPv6 addresses used by the DCC.  <I>IPmask/96</I> differs
+          from <I>noIP</I> for IPv4 addresses, because the former retains the IPv4 to
+          IPv6 mapping prefix.
+
+     <A NAME="OPTION-h"><B>-h</B></A> <I>homedir</I>
+          overrides the default DCC home directory, <I>@prefix@</I>.
+
+     <A NAME="OPTION-I"><B>-I</B></A> <I>user</I>
+          specifies the UID and GID of the process.
+
+     <A NAME="OPTION-p"><B>-p</B></A> <I>/sock/name</I> | <I>host,port,rhost/bits</I>
+          overrides the default address at which programs contact <B>dccifd</B>.  The
+          default is a UNIX domain socket named dccifd in the DCC home direc-
+          tory.
+
+          The second form specifies a local host name or IP address, a local
+          TCP port number, and the host names or IP addresses of computers
+          that can use <B>dccifd</B>.  127.0.0.1 or <I>localhost</I> are common choices for
+          <I>host</I>.  The string <I>@</I> specifies IN_ADDRANY or all local IP addresses.
+          127.0.0.0/8 is a common choice for <I>rhost/bits</I>.
+
+     <A NAME="OPTION-o"><B>-o</B></A> <I>/sock</I> | <I>host,port</I>
+          enables SMTP proxy mode instead of the ASCII protocol and specifies
+          the output connection when <B>dccifd</B> acts as an SMTP proxy.  It is the
+          address of the SMTP server for which <B>dccifd</B> acts as SMTP client.
+          When <I>/sock</I> is <I>/dev/null</I>, <B>dccifd</B> acts as if there were downstream
+          SMTP server that always answers "250 ok".  The string <I>@</I> specifies
+          the same IP address as the incoming TCP connection.
+
+          The input to <B>dccifd</B> in SMTP proxy mode is specified with <B>--p</B>.  For
+          example, <B>-p</B> <I>127.0.0.1,10025,127.0.0.1/32</I> <B>-o</B> <I>127.0.0.1,10026</I> could be
+          used to connect <B>dccifd</B> with Postfix as described in the documenta-
+          tion in version 2.2.1 Postfix documentation.
+
+          See below concerning the subset of ESMTP used in this mode.
+
+     <A NAME="OPTION-m"><B>-m</B></A> <I>map</I>
+          specifies a name or path of the memory mapped parameter file instead
+          of the default <I>map</I> file in the DCC home directory.  It should be
+          created with the <B><A HREF="cdcc.html">cdcc(8)</A></B> command.
+
+     <A NAME="OPTION-w"><B>-w</B></A> <I>whiteclnt</I>
+          specifies an optional file containing filtering parameters as well
+          as SMTP client IP addresses, SMTP envelope values, and header values
+          of mail that is spam or is not spam and does not need a <I>X-DCC</I>
+          header, and whose checksums should not be reported to the DCC
+          server.
+
+          If the pathname <I>whiteclnt</I> is not absolute, it is relative to the DCC
+          home directory.
+
+          The format of the <B>dccifd</B> whiteclnt file is the same as the <I>whitelist</I>
+          files used by <B><A HREF="dbclean.html">dbclean(8)</A></B> and the <I>whiteclnt</I> file used by <B><A HREF="dccproc.html">dccproc(8)</A></B>.
+          See <B><A HREF="dcc.html">dcc(8)</A></B> for a description of DCC white and blacklists.  Because
+          the contents of the <I>whiteclnt</I> file are used frequently, a companion
+          file is automatically created and maintained.  It has the same path-
+          name but with an added suffix of <I>.dccw</I> and contains a memory mapped
+          hash table of the main file.
+
+          A whitelist entry ("OK") or two or more semi-whitelistings ("OK2")
+          for one of the message's checksums prevents all of the message's
+          checksums from being reported to the DCC server and the addition of
+          a <I>X-DCC</I> header line by <B>dccifd</B> A whitelist entry for a checksum also
+          prevents rejecting or discarding the message based on DCC recipient
+          counts as specified by <B>-a</B> and <B>-t</B>.  Otherwise, one or more checksums
+          with blacklisting entries ("MANY") cause all of the message's check-
+          sums to be reported to the server with an addressee count of "MANY".
+
+          If the message has a single recipient, an <I>env</I><B>_</B><I>To</I> <I>whiteclnt</I> entry of
+          "OK" for the checksum of its recipient address acts like any other
+          <I>whiteclnt</I> entry of "OK."  When the SMTP message has more than one
+          recipient, the effects can be complicated.  When a message has sev-
+          eral recipients with some but not all listed in the <I>whiteclnt</I> file,
+          <B>dccifd</B> tries comply with the wishes of the users who want filtering
+          as well as those who don't by silently not delivering the message to
+          those who want filtering (i.e. are not whitelisted) and delivering
+          the message to don't want filtering.
+
+     <A NAME="OPTION-U"><B>-U</B></A> <I>userdirs</I>
+          enables per-user <I>whiteclnt</I> files and log directories.  Each target
+          of a message can have a directory of log files named
+          <I>userdirs/addr/log</I> where <I>addr</I> is the local user or mailbox name com-
+          puted by the MTA.  The name of each user's log directory must be
+          <I>log</I>.  If it is not absolute, <I>userdirs</I> is relative to the DCC home
+          directory.  The directory containing the log files must be named <I>log</I>
+          and it must be writable by the <B>dccifd</B> process.  Each log directory
+          must exist or logging for the corresponding is silently disabled.
+          The files created in the log directory are owned by the UID of the
+          <B>dccifd</B> process, but they have <I>group</I> and <I>other</I> read and write permis-
+          sions copied from the corresponding <I>log</I> directory.  To ensure the
+          privacy of mail, it may be good to make the directories readable
+          only by <I>owner</I> and <I>group</I>, and to use a cron script that changes the
+          owner of each file to match the grandparent <I>addr</I> directory.
+
+          There can also be a per -user whitelist file named
+          <I>userdirs/addr/whiteclnt</I> for each address <I>addr.</I> Any checksum that is
+          not white- or blacklisted by an individual addressee's <I>whiteclnt</I>
+          file  is checked in the main <B>-w -whiteclnt</B> file.  A missing per-
+          addressee <I>whiteclnt</I> file is the same as an empty file.  Relative
+          paths for files included in per-addressee files are resolved in the
+          DCC home directory.  The <I>whiteclnt</I> files and the <I>addr</I> directories
+          containing them must be writable by the <B>dccifd</B> process.
+
+          <I>Option</I> lines in per-user whiteclnt files can be used to modify many
+          aspects of <B>dccifd</B> filtering, as described in the main dcc man page.
+          For example, an <I>option</I> <I>dcc-off</I> line turns off DCC filtering for
+          individual mailboxes.
+
+     <A NAME="OPTION-a"><B>-a</B></A> <I>IGNORE</I> | <I>REJECT</I> | <I>DISCARD</I>
+          specifies the action taken when <B>dccifd</B> is in proxy mode with <B>-o</B> and
+          DCC server counts or <B>-t</B> thresholds say that a message is unsolicited
+          and bulk.  <I>IGNORE</I> causes the message to be unaffected except for
+          adding the <I>X-DCC</I> header line to the message.  This turns off DCC
+          filtering.
+
+          Spam can also be <I>REJECT</I>ed or (when in proxy mode with <B>-o</B>) accepted
+          and silently <I>DISCARD</I>ed without being delivered to local mailboxes.
+          The default is <I>REJECT</I>.
+
+          Mail forwarded via IP addresses marked <I>MX</I> or <I>MXDCC</I> in the main
+          <I>whiteclnt</I> file is treated as if <B>-a</B> <I>DISCARD</I> were specified.  This
+          prevents "bouncing" spam.
+
+          The effects of the <B>-w</B> <I>whiteclnt</I> are not affected by <B>-a</B>.
+
+     <A NAME="OPTION-t"><B>-t</B></A> <I>type,</I>[<I>log-thold,</I>]<I>rej-thold</I>
+          sets logging and "spam" thresholds for checksum <I>type</I>.  The checksum
+          types are <I>IP</I>, <I>env</I><B>_</B><I>From</I>, <I>From</I>, <I>Message-ID</I>, <I>substitute</I>, <I>Received</I>,
+          <I>Body</I>, <I>Fuz1</I>, <I>Fuz2</I>, <I>rep-total</I>, and <I>rep</I>.  The first six, <I>IP</I> through
+          <I>substitute</I>, have no effect except when a local DCC server configured
+          with <B>-K</B> is used.  The <I>substitute</I> thresholds apply to the first sub-
+          stitute heading encountered in the mail message.  The string <I>ALL</I>
+          sets thresholds for all types, but is unlikely to be useful except
+          for setting logging thresholds.  The string <I>CMN</I> specifies the com-
+          monly used checksums <I>Body</I>, <I>Fuz1</I>, and <I>Fuz2</I>.  <I>Rej-thold</I> and <I>log-thold</I>
+          must be numbers, the string <I>NEVER</I>, or the string <I>MANY</I> indicating
+          millions of targets.  Counts from the DCC server as large as the
+          threshold for any single type are taken as sufficient evidence that
+          the message should be logged or rejected.
+
+          <I>Log-thold</I> is the threshold at which messages are logged.  It can be
+          handy to log messages at a lower threshold to find solicited bulk
+          mail sources such as mailing lists.  If no logging threshold is set,
+          only rejected mail and messages with complicated combinations of
+          white and blacklisting are logged.  Messages that reach at least one
+          of their rejection thresholds are logged regardless of logging
+          thresholds.
+
+          <I>Rej-thold</I> is the threshold at which messages are considered "bulk,"
+          and so should be rejected or discarded if not whitelisted.
+
+          DCC Reputation thresholds in the commercial version of the DCC are
+          controlled by thresholds on checksum types <I>rep</I> and <I>rep-total</I>.  Mes-
+          sages from an IP address that the DCC database says has sent more
+          than <B>-t</B> <I>rep-total,log-thold</I> messages are logged.  A DCC Reputation
+          is computed for messages received from IP addresses that have sent
+          more than <B>-t</B> <I>rep-total,log-thold</I> messages.  The DCC Reputation of an
+          IP address is the percentage of its messages that have been detected
+          as bulk or having at least 10 recipients.  The defaults are equiva-
+          lent to <B>-t</B> <I>rep,never</I> and <B>-t</B> <I>rep-total,never,20</I>.
+
+          Bad DCC Reputations do not reject mail unless enabled by an <I>option</I>
+          <I>DCC-rep-on</I> line in a <I>whiteclnt</I> file.
+
+          The checksums of locally whitelisted messages are not checked with
+          the DCC server and so only the number of targets of the current copy
+          of a whitelisted message are compared against the thresholds.
+
+          The default is <I>ALL,NEVER</I>, so that nothing is discarded, rejected, or
+          logged.  A common choice is <I>CMN,25,50</I> to reject or discard mail with
+          common bodies except as overridden by the whitelist of the DCC
+          server, the sendmail <I>${dcc</I><B>_</B><I>isspam}</I> and <I>${dcc</I><B>_</B><I>notspam}</I> macros, and
+          <B>-g</B>, and <B>-w</B>.
+
+     <A NAME="OPTION-g"><B>-g</B></A> [<I>not-</I>]<I>type</I>
+          indicates that whitelisted, <I>OK</I> or <I>OK2</I>, counts from the DCC server
+          for a type of checksum are to be believed.  They should be ignored
+          if prefixed with <I>not-</I>.  <I>Type</I> is one of the same set of strings as
+          for <B>-t</B>.  Only <I>IP</I>, <I>env</I><B>_</B><I>From</I>, and <I>From</I> are likely choices.  By default
+          all three are honored, and hence the need for <I>not-</I>.
+
+     <A NAME="OPTION-S"><B>-S</B></A> <I>hdr</I>
+          adds to the list of substitute or locally chosen headers that are
+          checked with the <B>-w</B> <I>whiteclnt</I> file and sent to the DCC server.  The
+          checksum of the last header of type <I>hdr</I> found in the message is
+          checked.  <I>Hdr</I> can be <I>HELO</I> to specify the SMTP envelope HELO value.
+          <I>Hdr</I> can also be <I>mail</I><B>_</B><I>host</I> to specify the host name from the
+          Mail_from value in the SMTP envelope.  As many as six different sub-
+          stitute headers can be specified, but only the checksum of the first
+          of the six will be sent to the DCC server.
+
+     <A NAME="OPTION-l"><B>-l</B></A> <I>logdir</I>
+          specifies a directory in which files containing copies of messages
+          processed by <B>dccifd</B> are kept.  They can be copied to per-user direc-
+          tories specified with <B>-U</B>.  Information about other recipients of a
+          message is deleted from the per-user copies.
+
+          See the FILES section below concerning the contents of the files.
+          See also the <I>option</I> <I>log-subdirectory-{day,hour,minute}</I> lines in
+          <I>whiteclnt</I> files described in <B><A HREF="dcc.html">dcc(8)</A></B>.
+
+          The directory is relative to the DCC home directory if it is not
+          absolute
+
+     <A NAME="OPTION-R"><B>-R</B></A> <I>rundir</I>
+          specifies the "run" directory where the file containing the daemon's
+          process ID is stored.  The default value is <I>@dcc_rundir@</I>.
+
+     <A NAME="OPTION-T"><B>-T</B></A> <I>tmpdir</I>
+          changes the default directory for temporary files from the default.
+          The default is the directory specified with <B>-l</B> or the system default
+          if <B>-l</B> is not used.  The system default is often <I>/tmp</I>.
+
+     <A NAME="OPTION-D"><B>-D</B></A> <I>local-domain</I>
+          specifies a host or domain name by which the system is known.  There
+          can be several <B>-D</B> settings.
+
+          To find the per-user log directory and whitelist for each mail
+          recipient, <B>dccifd</B> must know each recipient's user name.  The ASCII
+          protocol used between and the MTA includes an optional user name
+          with each SMTP recipient address.  When the user name is absent when
+          the ASCII protocol is used or when the subset of ESMTP enabled with
+          <B>-o</B> is used, and when the SMTP recipient address includes an <I>at</I> <I>sign</I>
+          (@) each mail address is checked against the list of <I>local-domain</I>s.
+          The part of the recipient address remaining after longest matching
+          <I>local-domain</I> (if any) is taken as the user name.  The match is
+          anchored at the right or the end of the recipient address.  It must
+          start at a period (.) or <I>at</I> <I>sign</I> (@) in the domain name part of the
+          address.
+
+          If <I>local-domain</I> starts with an asterisk (*) indicating a wildcard,
+          preceding sub-domain names are discarded to compute the user name.
+          Otherwise, the computed user name will include any unmatched sub-
+          domain names.
+
+          The default value of <I>local-domain</I> when there are no <B>-D</B> settings is
+          the host name of the system.
+
+     <A NAME="OPTION-r"><B>-r</B></A> <I>rejection-msg</I>
+          specifies the rejection message in <B>-o</B> proxy mode for unsolicited
+          bulk mail or for mail temporarily blocked by <I>greylisting</I> when <B>-G</B> is
+          specified.  The first <B>-r</B> <I>rejection-msg</I> replaces the default bulk
+          mail rejection message, "5.7.1 550 mail %ID from %CIP rejected by
+          DCC".  The second replaces "4.2.1 452 mail %ID from %CIP temporary
+          greylist embargoed".  The third <B>-r</B> <I>rejection-msg</I> replaces the
+          default SMTP rejection message "5.7.1 550 %ID bad reputation; see
+          http://commercial-dcc.rhyolite.com/cgi-bin/reps.cgi?tgt=%CIP" for
+          mail with bad DCC Reputations.  If <I>rejection-msg</I> is the zero-length
+          string, the <B>-r</B> setting is counted but the corresponding message is
+          not changed.
+
+          <I>Rejection-msg</I> can contain specific information about the mail mes-
+          sage.  The following strings starting with % are replaced with the
+          corresponding values:
+              %ID       message ID such as the unique part of log file name or
+                        sendmail queue ID
+              %CIP      SMTP client IP address
+              %BTYPE    type of DNS blacklist hit, such as "SMTP client",
+                        "mail_host", or "URL NS"
+              %BTGT     IP address or name declared bad by DNS blacklist
+              %BPROBE   domain name found in DNS blacklist such as
+                        4.3.2.10.example.com
+              %BRESULT  value of the %BPROBE domain name found in DNS black-
+                        list
+
+          A common alternate for the bulk mail rejection message is "4.7.1 451
+          Access denied by DCC" to tell the sending mail system to continue
+          trying.  Use a 4yz response with caution, because it is likely to
+          delay for days a delivery failure message for false positives.  If
+          the rejection message does not start with an RFC 1893 status code
+          and RFC 2821 reply code, 5.7.1 and 550 or 4.2.1 and 452 are used.
+
+          See also <B>-B</B> <I>set:rej-msg=rejection-msg</I> to set the status message for
+          mail rejected by DNS blacklists.
+
+     <A NAME="OPTION-j"><B>-j</B></A> <I>maxjobs</I>
+          limits the number of simultaneous requests that will be processed.
+          The default value is the maximum number that seems to be possible
+          given system limits on open files, select() bit masks, and so forth.
+          Start <B>dccifd</B> with <B>-d</B> and see the starting message in the system log
+          to see the limit.
+
+     <A NAME="OPTION-B"><B>-B</B></A> <I>dnsbl-option</I>
+          enables DNS blacklist checks of the SMTP client IP address, SMTP
+          envelope Mail_From sender domain name, and of host names in URLs in
+          the message body.  Body URL blacklisting has too many false posi-
+          tives to use on abuse mailboxes.  It is less effective than
+          greylisting with <B><A HREF="dccm.html">dccm(8)</A></B> or <B><A HREF="dccifd.html">dccifd(8)</A></B> but can be useful in situa-
+          tions where greylisting cannot be used.
+
+          <I>Dnsbl-option</I> is either one of the <B>-B</B> <I>set:option</I> forms or
+              <B>-B</B> <I>domain</I>[<I>,IPaddr</I>[<I>/xx</I>[<I>,bltype</I>]]]
+          <I>Domain</I> is a DNS blacklist domain such as example.com that will be
+          searched.  <I>IPaddr</I>[<I>/xxx</I>] is the string "any" an IP address in the DNS
+          blacklist that indicates that the mail message should be rejected,
+          or a CIDR block covering results from the DNS blacklist.
+          "127.0.0.2" is assumed if <I>IPaddr</I> is absent.  IPv6 addresses can be
+          specified with the usual colon (:) notation.  Names can be used
+          instead of numeric addresses.  The type of DNS blacklist is speci-
+          fied by <I>bltype</I> as <I>name</I>, <I>IPv4</I>, or <I>IPv6</I>.  Given an envelope sender
+          domain name or a domain name in a URL of spam.domain.org and a
+          blacklist of type <I>name</I>, spam.domain.org.example.com will be tried.
+          Blacklist types of <I>IPv4</I> and <I>IPv6</I> require that the domain name in a
+          URL sender address be resolved into an IPv4 or IPv6 address.  The
+          address is then written as a reversed string of decimal octets to
+          check the DNS blacklist, as in 2.0.0.127.example.com,
+
+          More than one blacklist can be specified and blacklists can be
+          grouped.  All searching within a group is stopped at the first posi-
+          tive result.
+
+          Positive results are ignored after being logged unless an
+          <I>option</I> <I>DNSBL-on</I> line appears in the global or per-user <I>whiteclnt</I>
+          file.
+
+          <B>-B</B> <I>set:no-client</I>
+               says that SMTP client IP addresses and reverse DNS domain names
+               should not be checked in the following blacklists.
+               <B>-B</B> <I>set:client</I> restores the default for the following black-
+               lists.
+
+          <B>-B</B> <I>set:no-mail</I><B>_</B><I>host</I>
+               says that SMTP envelope Mail_From sender domain names should
+               not be checked in the following blacklists.  <B>-B</B> <I>set:mail</I><B>_</B><I>host</I>
+               restores the default.
+
+          <B>-B</B> <I>set:no-URL</I>
+               says that URLs in the message body should not be checked in the
+               in the following blacklists.  <B>-B</B> <I>set:URL</I> restores the default.
+
+          <B>-B</B> <I>set:no-MX</I>
+               says MX servers of sender Mail_From domain names and host names
+               in URLs should not be checked in the following blacklists.
+               <B>-B</B> <I>set:MX</I> restores the default.
+
+          <B>-B</B> <I>set:no-NS</I>
+               says DNS servers of sender Mail_From domain names and host
+               names in URLs should not be checked in the following black-
+               lists.  <B>-B</B> <I>set:NS</I> restores the default.
+
+          <B>-B</B> <I>set:defaults</I>
+               is equivalent to all of <B>-B</B> <I>set:no-temp-fail</I> <B>-B</B> <I>set:client</I>
+               <B>-B</B> <I>set:mail</I><B>_</B><I>host</I> <B>-B</B> <I>set:URL</I> <B>-B</B> <I>set:MX</I> and <B>-B</B> <I>set:NS</I>
+
+          <B>-B</B> <I>set:group=X</I>
+               adds later DNS blacklists specified with
+                   <B>-B</B> <I>domain</I>[<I>,IPaddr</I>[<I>/xx</I>[<I>,bltype</I>]]]
+               to group 1, 2, or 3.
+
+          <B>-B</B> <I>set:debug=X</I>
+               sets the DNS blacklist logging level
+
+          <B>-B</B> <I>set:msg-secs=S</I>
+               limits <B>dccifd</B> to <I>S</I> seconds total for checking all DNS black-
+               lists.  The default is 25.
+
+          <B>-B</B> <I>set:URL-secs=S</I>
+               limits <B>dccifd</B> to at most <I>S</I> seconds resolving and checking any
+               single URL.  The default is 11.  Some spam contains dozens of
+               URLs and that some "spamvertised" URLs contain host names that
+               need minutes to resolve.  Busy mail systems cannot afford to
+               spend minutes checking each incoming mail message.
+
+          <B>-B</B> <I>set:rej-msg=rejection-msg</I>
+               sets the SMTP rejection message for the following blacklists.
+               <I>Rejection-msg</I> must be in the same format as for <B>-r</B>.  If
+               <I>rejection-msg</I> is null, the default is restored.  The default
+               DNS blacklist rejection message is the first message set with
+               <B>-r</B>.
+
+          <B>-B</B> <I>set:temp-fail</I>
+               causes <B>dccifd</B> to the MTA to answer the SMTP DATA command with
+                  452 4.2.1 mail %ID from %CIP temporary delayed for DNSBL
+               if any DNS answer required for a DNSBL in the current group
+               times out, including resolving names in URLs.
+
+          <B>-B</B> <I>set:no-temp-fail</I>
+               restores the default of assuming a negative answer for DNS
+               responses that take too long.
+
+          <B>-B</B> <I>set:maxjobs=X</I>
+               sets maximum number of helper processes to <I>X</I>.  In order to use
+               typical single-threaded DNS resolver libraries, <B>dccifd</B> uses
+               fleets of helper processes.  It is rarely a good idea to change
+               the default, which is the same as the maximum number of simul-
+               taneous jobs set with <B>-j</B>.
+
+          <B>-B</B> <I>set:progpath=@libexecdir@/dns-helper</I>
+               changes the path to the helper program.
+
+     <A NAME="OPTION-L"><B>-L</B></A> <I>ltype,facility.level</I>
+          specifies how messages should be logged.  <I>Ltype</I> must be <I>error</I>, <I>info</I>,
+          or <I>off</I> to indicate which of the two types of messages are being con-
+          trolled or to turn off all <B>syslog(3)</B> messages from <B>dccifd</B>.  <I>Level</I>
+          must be a <B>syslog(3)</B> level among <I>EMERG</I>, <I>ALERT</I>, <I>CRIT</I>, <I>ERR</I>, <I>WARNING</I>,
+          <I>NOTICE</I>, <I>INFO</I>, and <I>DEBUG</I>.  <I>Facility</I> must be among <I>AUTH</I>, <I>AUTHPRIV</I>,
+          <I>CRON</I>, <I>DAEMON</I>, <I>FTP</I>, <I>KERN</I>, <I>LPR</I>, <I>MAIL</I>, <I>NEWS</I>, <I>USER</I>, <I>UUCP</I>, and <I>LOCAL0</I>
+          through <I>LOCAL7</I>.  The default is equivalent to
+                <B>-L</B> <I>info,MAIL.NOTICE</I> <B>-L</B> <I>error,MAIL.ERR</I>
+
+     <B>dccifd</B> normally sends counts of mail rejected and so forth to the system
+     log at midnight.  The SIGUSR1 signal sends an immediate report to the
+     system log.  The reports will be repeated every 24 hours at the same
+     minute as the signal instead of at midnight.
+
+   <A NAME="Protocol"><B>Protocol</B></A>
+     <B>Dccifd</B> uses a simple ASCII protocol to receive mail messages to be
+     checked and to return results.  For each message, the MTA must open a
+     connection to the interface daemon, send options, envelope recipients,
+     and the message, receive the results, and close the connection.
+
+     Instead of the ASCII protocol, a subset of ESMTP is enabled by <B>-o</B>.  Only
+     the familiar HELO, EHLO, Mail, Rcpt, DATA, RSET, and QUIT commands and
+     the Postfix extensions XFORWARD and XCLIENT are honored.  Since SMTP has
+     no provisions for user names, the protocol enabled by <B>-o</B> depends on a
+     list of local domain names specified with <B>-D</B> to find per-user log direc-
+     tories and whitelist files.  If neither XFORWARD nor XCLIENT are used,
+     <B>dccifd</B> uses the IP address of the MTA and the value of the HELO command.
+
+     In the ASCII protocol, each of the following lines are sent in order to
+     <B>dccifd</B>.  Each ends with a newline ('\n') character.
+       options     zero or more blank-separated strings among:
+                     <I>spam</I>        the message is already known to be spam
+                     <I>body</I>        return all of the headers with the added
+                                 <I>X-DCC</I> header line and the body
+                     <I>header</I>      return the <I>X-DCC</I> header
+                     <I>query</I>       ask the DCC server about the message without
+                                 reporting it, as if <B>dccifd</B> were running with
+                                 <B>-Q</B>.
+                     <I>grey-query</I>  only query the greylist server for this mes-
+                                 sage.  <B>-G</B> <I>on</I> must be in use.
+                     <I>no-reject</I>   suppress the overall, one character line 'R'
+                                 result.  This can be useful when using <B>dccifd</B>
+                                 only for greylisting.
+                     <I>log</I>         ensure that this message is logged as if
+                                 <B>dccifd</B> were running with <B>-t -all,0,</B>
+       client      IP address of the SMTP client in a "dotted" or "coloned"
+                   ASCII string and reverse-DNS host name.  If the host name
+                   is present, it must follow a carriage return character
+                   ('\r') after the IP address.  The client IP address must be
+                   present and non-null if the host name is present.  The
+                   string "0.0.0.0\n" is understood the same as the null
+                   string, meaning that both the IP address and host name are
+                   absent.  If the client IP address is absent, then the IP
+                   address and host name are taken from the first non-local
+                   Received header if it has the standard "name (name [IP
+                   address])..." format.  Non-standard Received headers com-
+                   monly added by qmail as well as Received headers specifying
+                   IP addresses marked <I>MX</I> or <I>MXDCC</I> in the global <B>-w</B> <I>whiteclnt</I>
+                   file are skipped.
+       HELO        SMTP HELO value or nothing, followed by a newline ('\n')
+                   character.  If the HELO value is null and the IP address of
+                   the SMTP client are not supplied, they will be taken from
+                   the same Received: header that supplies the IP address.
+       sender      or SMTP <I>Mail</I> <I>From</I> command value for the env_from checksum.
+                   If the sender is null, the contents of the first Return-
+                   Path: or UNIX style From_ header is used.
+       recipients  or SMTP <I>Rcpt</I> <I>To</I> recipient mailboxes followed by correspond-
+                   ing local user names, one (mailbox,user) pair to a line.
+                   Each optional local user name is separated from the corre-
+                   sponding mailbox recipient address by a carriage return
+                   ('\r').  A local user name can be null if it is not known,
+                   but each recipient mailbox must be non-null.  If there are
+                   no lines of (mailbox,user) pairs and if the <I>spam</I> option is
+                   not included, then the <I>query</I> is assumed.  Mailboxes without
+                   user names will lack per-user log files and will not invoke
+                   a per-user whitelist.
+
+     The last recipient-user name pair is followed by an empty line and the
+     headers and body of the message.  The end of the body of the mail message
+     is signaled by the MTA half-closing the connection.  See <B>shutdown(2)</B>.
+
+     <B>Dccifd</B> responds with three things.  First is a one character line of the
+     overall result advising the MTA:
+       A    accept the message for all recipients and answer the SMTP DATA
+            command with a 2yz result.
+       G    answer with a 4yz result to embargo the message for greylisting.
+       R    reject the message and answer the DATA command with a 5yz result.
+       S    accept the message for some recipients and so answer the DATA com-
+            mand with a 2yz result.
+       T    temporary failure by the DCC system and so answer with a 4yz
+            result.
+
+     Second is a line of characters indicating the disposition of the message
+     for each corresponding recipient:
+       A    deliver the message
+       G    discard the message during a greylist embargo
+       R    discard the message as spam
+     The SMTP protocol allows only a single result for the DATA command for
+     all recipients that were not rejected before body of the message was
+     offered with the DATA command.  To accept the message for some recipients
+     and reject it for others, the MTA must tell the SMTP client it is accept-
+     ing the message for all recipients and then discard it for those that
+     would reject it.
+
+     Finally, if the <I>body</I> or <I>header</I> strings are in the first line of <I>options</I>
+     sent by the MTA to the daemon, then the <I>X-DCC</I> header line or the entire
+     body with the <I>X-DCC</I> header line follows.
+
+
+</PRE>
+<H2><A NAME="FILES">FILES</A></H2><PRE>
+     <A NAME="FILE-@prefix@">@prefix@</A>    is the DCC home directory in which other files are found.
+     <A NAME="FILE-@libexecdir@/start">@libexecdir@/start</A>-dccifd
+                 and
+     <A NAME="FILE-@libexecdir@/rcDCC">@libexecdir@/rcDCC</A>
+                 are scripts used to start the daemon.
+     <A NAME="FILE-dcc/dcc_conf">dcc/dcc_conf</A>
+                 contains parameters used by the scripts to start DCC daemons
+                 and cron jobs.
+     <A NAME="FILE-logdir">logdir</A>      is an optional directory specified with <B>-l</B> and containing
+                 marked mail.  Each file in the directory contains one mes-
+                 sage, at least one of whose checksums reached its <B>-t</B> thresh-
+                 olds or that is interesting for some other reason.  Each file
+                 starts with lines containing the date when the message was
+                 received, the IP address of the SMTP client, and SMTP enve-
+                 lope values.  Those lines are followed by the body of the
+                 SMTP message including its header as it was received.  Only
+                 approximately the first 32 KBytes of the body are recorded
+                 unless modified by <I>./configure</I> <I>--with-max-log-size=xx</I> The
+                 checksums for the message follow the body.  They are followed
+                 by lines indicate that one of the checksums is white- or
+                 blacklisted by the <B>-w</B> <I>whiteclnt</I> file.  Each log file ends
+                 with the <I>X-DCC</I> header line added to the message and the dis-
+                 position of the message.
+     <A NAME="FILE-map">map</A>         is the memory mapped file of information concerning DCC
+                 servers in the DCC home directory.
+     <A NAME="FILE-whiteclnt">whiteclnt</A>   contains the client whitelist in the format described in
+                 <B><A HREF="dcc.html">dcc(8)</A></B>.
+     <A NAME="FILE-whiteclnt.dccw">whiteclnt.dccw</A>
+                 is a memory mapped hash table of the <I>whiteclnt</I> file.
+     <A NAME="FILE-dccifd.pid">dccifd.pid</A>  in the <B>-R</B> <I>rundir</I> directory contains daemon's process ID.
+
+
+</PRE>
+<H2><A NAME="EXAMPLES">EXAMPLES</A></H2><PRE>
+     Dccifd can be used as Postfix Before-Queue Content filter.  In some tests
+     these values for <B>-p</B> and <B>-o</B> in <I>dcc</I><B>_</B><I>conf</I>.
+
+         DCCIFD_ENABLE=on
+         DCCIFD_ARGS="-p 127.0.0.1,10025,127.0.0.1/32 -o 127.0.0.1,10026
+
+     worked with these lines in /etc/postfix/master.cf
+
+         smtp      inet  n       -       n       -       -       smtpd
+             -o smtpd_proxy_filter=127.0.0.1:10025
+         127.0.0.1:10026 inet n  -       n       -        -      smtpd
+             -o smtpd_authorized_xforward_hosts=127.0.0.0/8
+             -o smtpd_client_restrictions=
+             -o smtpd_helo_restrictions=
+             -o smtpd_sender_restrictions=
+             -o smtpd_recipient_restrictions=permit_mynetworks,reject
+             -o smtpd_data_restrictions=
+             -o mynetworks=127.0.0.0/8
+             -o receive_override_options=no_unknown_recipient_checks
+
+
+</PRE>
+<H2><A NAME="SEE-ALSO">SEE ALSO</A></H2><PRE>
+     <B><A HREF="cdcc.html">cdcc(8)</A></B>, <B><A HREF="dbclean.html">dbclean(8)</A></B>, <B><A HREF="dcc.html">dcc(8)</A></B>, <B><A HREF="dccd.html">dccd(8)</A></B>, <B><A HREF="dblist.html">dblist(8)</A></B>, <B><A HREF="dccm.html">dccm(8)</A></B>, <B><A HREF="dccproc.html">dccproc(8)</A></B>,
+     <B><A HREF="dccsight.html">dccsight(8)</A></B>,
+
+
+</PRE>
+<H2><A NAME="HISTORY">HISTORY</A></H2><PRE>
+     Implementation of <B>dccifd</B> Distributed Checksum Clearinghouses are based on
+     an idea of Paul Vixie with code designed and written at Rhyolite Software
+     starting in 2000.  was started at Rhyolite Software in 2002.  This docu-
+     ment describes version 1.3.103.
+
+
+</PRE>
+<H2><A NAME="BUGS">BUGS</A></H2><PRE>
+     <B>dccifd</B> uses <B>-t</B> where <B><A HREF="dccproc.html">dccproc(8)</A></B> uses <B>-c</B>.
+
+     By default <B>dccifd</B> look for its UNIX domain socket in the DCC home direc-
+     tory, but <B><A HREF="dccm.html">dccm(8)</A></B> looks in its <B>-R</B> <I>rundir</I>.
+
+     Systems without <B>setrlimit(2)</B> and <B>getrlimit(2)</B> RLIMIT_NOFILE can have
+     problems with the default limit on the number of simultaneous jobs, the
+     value of <B>-j</B>.  Every job requires four open files.  These problems are
+     usually seen with errors messages that say something like
+           dccifd[24448]: DCC: accept(): Result too large
+     A fix is to use a smaller value for <B>-j</B> or to allow <B>dccifd</B> to open more
+     files.
+
+                               February 26, 2009
+</PRE>
+<HR>
+<ADDRESS>
+Man(1) output converted with
+<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
+modified for the DCC $Date 2001/04/29 03:22:18 $
+<BR>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG SRC="http://logos.dcc-servers.net/border.png"
+            class=logo ALT="DCC logo">
+    </A>
+<A HREF="http://validator.w3.org/check?uri=referer">
+    <IMG class=logo ALT="Valid HTML 4.01 Strict"
+        SRC="http://www.w3.org/Icons/valid-html401">
+    </A>
+</ADDRESS>
+</BODY>
+</HTML>
diff -r 000000000000 -r c7f6b056b673 dccifd/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccifd/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,4 @@
+Makefile.in
+dccif.pl.in
+dccifd.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 dccifd/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccifd/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,53 @@
+# make the Distributed Checksum interface daemon
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.15 $Revision$
+# @configure_input@
+
+DEPTH	=..
+PROG	=dccifd
+SRCS	=$(PROG).c
+
+SUBDIR	=dccif-test
+
+CFLAGS	+=$(THRINC)
+LDADD	+=$(THR_LDADD)
+DPADD	+=$(THR_DPADD)
+LDFLAGS	+=@PTHREAD_LDFLAGS@
+
+DCC_BINDIR=@installroot@@libexecdir@
+@MAKE_PROG@
+@MAKE_INC2@
diff -r 000000000000 -r c7f6b056b673 dccifd/dccif-test/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccifd/dccif-test/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,4 @@
+Makefile.in
+dccif-test.c
+dccif-test.pl
+.manifest
diff -r 000000000000 -r c7f6b056b673 dccifd/dccif-test/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccifd/dccif-test/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,49 @@
+# test Distributed Checksum Clearinghouse interface daemon
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.8 $Revision$
+# @configure_input@
+
+DEPTH	=../..
+PROG	=dccif-test
+SRCS	=$(PROG).c
+
+# this should be installed in /var/dcc/libexec, but it got released into
+#	/usr/local/bin
+
+@MAKE_INC@
+@MAKE_PROG@
+@MAKE_INC2@
diff -r 000000000000 -r c7f6b056b673 dccifd/dccif-test/dccif-test.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccifd/dccif-test/dccif-test.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,196 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * test dccifd via dccif()
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.23 $Revision$
+ */
+
+#include "dccif.h"
+#include "dcc_clnt.h"
+#include "dcc_paths.h"
+#include <arpa/inet.h>
+
+static void NRATTRIB
+usage(void)
+{
+	dcc_logbad(EX_USAGE,
+		   "usage: [-VP] [-h FIFO | homedir | hostname,port] [-o opts]"
+		   " [-c clnt-IP-addr]\n"
+		   "    [-l heLo] [-f env_from] [-I ifile] [-O ofile]"
+		   " [-r rcpt1[,user1]] [...]\n");
+}
+
+
+
+int NRATTRIB
+main(int argc, char **argv)
+{
+	DCC_EMSG emsg;
+	const char *srvr_addr;
+	const char *opts;
+	DCC_SOCKU su, *sup;
+	const char *clnt_nm;
+	const char *helo;
+	const char *env_from;
+	DCCIF_RCPT *rcpts, *rcpt;
+	int in_body_fd, out_body_fd;
+	u_char dccproc;
+	u_char result;
+	char *p;
+	int i, error;
+
+	srvr_addr = 0;
+	opts = 0;
+	sup = 0;
+	clnt_nm = 0;
+	dccproc = 0;
+	helo = 0;
+	env_from = 0;
+	in_body_fd = STDIN_FILENO;
+	out_body_fd = dup(STDOUT_FILENO);
+	rcpts = 0;
+
+	dcc_syslog_init(0, argv[0], 0);
+
+	while (EOF != (i = getopt(argc, argv, "VPh:o:c:l:f:I:O:r:"))) {
+		switch (i) {
+		case 'V':
+			fprintf(stderr, DCC_VERSION"\n");
+			break;
+
+		case 'P':
+			dccproc = 1;
+			dcc_no_syslog = 1;
+			break;
+
+		case 'h':
+			srvr_addr = optarg;
+			break;
+
+		case 'o':
+			/* convert commas that users like to type to blanks */
+			while ((p = strchr(optarg, ',')) != 0)
+				*p = ' ';
+			opts = optarg;
+			break;
+
+		case 'c':
+			clnt_nm = optarg;
+			dcc_host_lock();
+			if (!dcc_get_host(clnt_nm, 2, &error)) {
+				dcc_logbad(EX_USAGE, "%s: %s\n", clnt_nm,
+					   DCC_HSTRERROR(error));
+			}
+			dcc_ipv4sutoipv6(&su, &dcc_hostaddrs[0]);
+			sup = &su;
+			dcc_host_unlock();
+			break;
+
+		case 'l':
+			helo = optarg;
+			break;
+
+		case 'f':
+			env_from = optarg;
+			break;
+
+		case 'I':
+			close(in_body_fd);
+			in_body_fd = open(optarg, O_RDONLY, 0);
+			if (in_body_fd < 0)
+				dcc_logbad(EX_IOERR, "open(%s): %s\n",
+					optarg, strerror(errno));
+			break;
+
+		case 'O':
+			close(out_body_fd);
+			out_body_fd = open(optarg, O_WRONLY | O_CREAT, 0666);
+			if (out_body_fd < 0) {
+				fprintf(stderr, "open(%s): %s\n",
+					optarg, strerror(errno));
+				exit(1);
+			}
+			break;
+
+		case 'r':
+			rcpt = malloc(sizeof(*rcpt));
+			memset(rcpt, 0, sizeof(*rcpt));
+			rcpt->next = rcpts;
+			rcpt->addr = optarg;
+			p = strchr(optarg, ',');
+			if (!p) {
+				rcpt->user = "";
+			} else {
+				*p++ = '\0';
+				rcpt->user = p;
+			}
+			rcpt->ok = '?';
+			rcpts = rcpt;
+			break;
+
+		default:
+			usage();
+			break;
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	if (argc != 0)
+		usage();
+
+	result = dccif(emsg,
+		       out_body_fd, 0,
+		       opts, sup, clnt_nm, helo, env_from,
+		       rcpts, in_body_fd, 0, srvr_addr);
+
+	if (!dccproc) {
+		if (!result) {
+			printf("result 0: %s\n", emsg);
+			exit(0);
+		}
+		printf("overall result = '%c'\n", result);
+		for (rcpt = rcpts; rcpt; rcpt = rcpt->next) {
+			printf("    %s%s%s: '%c'\n",
+			       rcpt->addr,
+			       (rcpt->user[0] != '\0') ? "," : "",
+			       rcpt->user,
+			       rcpt->ok);
+		}
+		putchar('\n');
+	}
+	exit(0);
+}
diff -r 000000000000 -r c7f6b056b673 dccifd/dccif-test/dccif-test.pl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccifd/dccif-test/dccif-test.pl	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,100 @@
+#! /usr/local/bin/perl
+
+# test sample Perl interface to the DCC interface daemon
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.7 $Revision$
+
+use strict 'subs', 'vars';
+use Getopt::Std;
+
+
+my $usage = "usage: [-h homedir] [-o opts] [-c clnt-name] [-l heLo]"
+		. " [-f env_from]\n"
+		. "    [-I infile] [-O outfile]"
+		. " [-r rcpt1[:user1][,rcpt2[:user2],...]]\n";
+
+my(%opts, $clnt_addr, $clnt_name, $rcpt, @rcpts, $body, @result);
+
+
+do('../dccif.pl') || die("could not get sample interface ../dccif.pl: $!\n");
+
+$opts{I} = "-";
+$opts{O} = "-";
+if (!getopts('h:o:c:l:f:I:O:t:r:', \%opts) || @ARGV) {
+    print STDERR $usage;
+    exit 1;
+}
+
+
+if (! $opts{c}) {
+    $clnt_addr = "";
+    $clnt_name = "";
+} else {
+    $clnt_name = $opts{c};
+    $clnt_addr = gethostbyname($clnt_name);
+    if (!$clnt_addr) {
+	$clnt_addr = "";
+    } else {
+	$clnt_addr = inet_ntoa($clnt_addr);
+	$clnt_name = "" if ($clnt_addr eq $clnt_name);
+    }
+}
+
+
+# make array of env_To recipients
+#   With no recipients, dccifd will understand a target count of 0.  The
+#   result is like `dccifd -Q`.
+@rcpts = ();
+if ($opts{r}) {
+    my(@raw_rcpts, @raw_rcpt);
+    @raw_rcpts = split(/,/, $opts{r});
+    while (@raw_rcpts) {
+	$rcpt = shift(@raw_rcpts);
+	$rcpt =~ s/;/\r/;
+	push @rcpts, $rcpt;
+    }
+}
+
+@result = dccif($opts{O},
+		$opts{o} ? $opts{o} : "",
+		$clnt_addr, $clnt_name,
+		$opts{l} ? $opts{l} : "",
+		$opts{f} ? $opts{f} : "",
+		\@rcpts,
+		$opts{I},
+		$opts{h} ? $opts{h} : "");
+
+print STDERR "result=@result\n";
diff -r 000000000000 -r c7f6b056b673 dccifd/dccif.pl.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccifd/dccif.pl.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,200 @@
+#! @PERL@ -w
+
+# a sample Perl interface to the DCC interface daemon, dccifd
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.12 $Revision$
+# @configure_input@
+
+# check this file by running it separately
+use strict 'subs';
+
+use Socket;
+
+# so this file can be used with constructions like do('../dccif.pl')
+#   in dccif-test/dccif-test.pl
+return 1;
+
+
+# Returns a string
+#   The first character indicates the overall result of the the operation.
+#	If the dccifd daemon is not running or there were other problems,
+#	the first character is '?', the second is '\n', and the rest of the
+#	string is an error message.
+#	If things are ok, the first character is one of the
+#	DCCIF_RESULT_* values from include/dccif.h and the second is '\n'.
+#   If things are ok, the second line is a string of characters, each
+#	indicating whether message should be delivered to the corresponding
+#	recipient (DCCIF_RCPT_ACCEPT or 'A') or rejected (DCCIF_RCPT_REJECT
+#	or 'R').  This string is also ended with a newline ('\n') character.
+# The body including headers of the message is read from one file or
+#	file handle (e.g. "-").
+#	The X-DCC header (if the "header" option is present) or the body
+#	with added X-DCC header (if the "body" option is present) is
+#	written to a second file or filehandle (e.g. "-").
+# The result depends on the -t thresholds given dccifd.
+# If the $env_tos or list of targets of the message is empty, this acts
+#	as if dccifd is being run with -Q.
+sub dccif {
+    my($out,			# write X-DCC header or entire body to this
+       $opts,			# blank separated string of "spam", ... options
+       $clnt_addr,		# SMTP client IP address as a string
+       $clnt_name,		# null or SMTP client hostname
+       $helo,			# value of SMTP HELO command
+       $env_from,		# envelope Mail_From value
+       $env_tos,		# array of "address\rname" env_To strings
+       $in,			# read body from this
+       $homedir) = @_;		# DCC home directory
+
+    my($env_to, $result, $body, $oks, $i);
+
+    $homedir = "@prefix@"
+	if (! $homedir);
+
+    if ($clnt_addr) {
+	inet_aton($clnt_addr)
+	    || return ("", "inet_aton($clnt_addr) failed: $!\n");
+    } else {
+	$clnt_name = '';
+    }
+
+    socket(SOCK, AF_UNIX, SOCK_STREAM, 0)
+	|| return("", "socket(AF_UNIX): $!\n");
+    connect(SOCK, sockaddr_un("$homedir/dccifd"))
+	|| return("", "connect($homedir/dccifd): $!\n");
+
+    # send the options and other parameters to the daemon
+    $result = dccif_write($opts . "\012"
+			  . $clnt_addr . "\015" . $clnt_name . "\012"
+			  . $helo . "\012"
+			  . $env_from . "\012",
+			  "opts helo clnt");
+    return $result if ($result);
+
+    foreach $env_to (@$env_tos) {
+	$result = dccif_write($env_to . "\012", "rcpt");
+	return $result if ($result);
+    }
+    $result = dccif_write("\012", "end rcpts");
+    return $result if ($result);
+
+    # send the body of the message to the daemon
+    if (! open(IFH, $in)) {
+	$result = "?\nopen($in): $!\n";
+	close(SOCK);
+	return $result
+    }
+    for (;;) {
+	$i = sysread(IFH, $body, 8192);
+	if (!defined($i)) {
+	    $result = "?\nsysread(body): $!\n";
+	    close(SOCK);
+	    close(IFH);
+	    return $result;
+	}
+	if ($i == 0) {
+	    close(IFH);
+	    last;
+	}
+	$result = dccif_write($body, "body");
+	if ($result) {
+	    close(IFH);
+	    return $result;
+	}
+    }
+
+    # tell the daemon it has all of the message
+    if (!shutdown(SOCK, 1)) {
+	$result = "shutdown($homedir/dccifd): $!\n";
+	close(SOCK);
+	return $result;
+    }
+
+    # get the result from the daemon
+    $result = <SOCK>;
+    if (!defined $result) {
+	$result = "read($homedir/dccifd): $!\n";
+	close(SOCK);
+	return $result;
+    }
+    $oks = <SOCK>;
+    if (!defined $oks) {
+	$result = "read($homedir/dccifd): $!\n";
+	close(SOCK);
+	return $result;
+    }
+
+    # copy the header or body from the daemon
+    if (! open(OFH, ">" . $out)) {
+	$result = "?\nopen($in): $!\n";
+	close(SOCK);
+	return $result
+    }
+    for (;;) {
+	$i = read(SOCK, $body, 8192);
+	if (!defined $i) {
+	    $result = "?\nread(body): $!\n";
+	    close(SOCK);
+	    close(OFH);
+	    return $result;
+	}
+	if ($i == 0) {
+	    close(SOCK);
+	    close(OFH);
+	    return $result . $oks;
+	}
+	if (! syswrite(OFH, $body)) {
+	    $result = "?\nsyswrite($out): $!\n";
+	    close(SOCK);
+	    close(OFH);
+	    return $result;
+	}
+    }
+}
+
+
+
+sub dccif_write {
+    my($buf, $emsg) = @_;
+    my $result;
+
+    if (! syswrite(SOCK, $buf)) {
+	$result = ("?\nsyswrite($emsg): $!\n");
+	close(SOCK);
+	return $result
+    }
+    return "";
+}
diff -r 000000000000 -r c7f6b056b673 dccifd/dccifd.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccifd/dccifd.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,3357 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * DCC interface daemon
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.166 $Revision$
+ */
+
+#include "dccif.h"
+#include "dcc_paths.h"
+#include "cmn_defs.h"
+#include <signal.h>
+
+
+static int stopping;
+
+#define MAX_SMTP_LINE
+
+u_char use_ipv6 = 0;
+
+/* incoming proxy and bidirectional ASCII dccifd protocol connection */
+static char *listen_addr;
+static u_char listen_family;
+static struct sockaddr_un listen_sun;
+
+static const char *lhost_port;
+static const char *rcidr;
+static struct in6_addr raddr, rmask;
+static SOCKET listen_soc = -1;
+
+/* outgoing proxy connection */
+static u_char proxy_out_family;
+static struct sockaddr_un proxy_out_sun;
+static char proxy_out_host[DCC_MAXDOMAINLEN];
+static u_int16_t proxy_out_port;
+static DCC_SOCKU proxy_out_su;
+
+static const char *rundir = DCC_RUNDIR;
+static DCC_PATH pidpath;
+static const char *progpath = DCC_LIBEXECDIR"/dccifd";
+
+static u_char background = 1;
+
+static u_char proxy;
+
+u_char cannot_discard = 0;		/* can trim targets after DATA */
+u_char cannot_reject = 1;		/* ASCII protocol accepts all targets */
+
+
+/* message state or context */
+typedef struct {			/* control an input buffer */
+    char	*base;
+    char	*in;
+    char	*out;
+    char	*next_line;
+    int		size;
+    int		line_len;
+    int		*socp;
+} IN_BC;
+typedef struct {			/* control an ouput buffer */
+    char	*base;
+    char	*in;
+    int		len;
+    int		size;
+    int		*socp;
+} OUT_BC;
+typedef struct work {
+    int		proxy_in_soc;
+    DCC_SOCKU   proxy_in_su;
+    int		proxy_out_soc;
+    char	buf1[DCC_HDR_CK_MAX*8]; /* >=DCC_HDR_CK_MAX*2 & MAX_RCPTS */
+    char	buf2[DCC_HDR_CK_MAX*8];
+    char	buf3[1024];
+    char	buf4[1024];
+    CMN_WORK    cw;
+    enum {
+	SMTP_ST_START,			/* expecting HELO */
+	SMTP_ST_HELO,			/* seen HELO, expecting Mail_From */
+	SMTP_ST_TRANS,			/* seen Mail_From */
+	SMTP_ST_RCPT,			/* seen Rcpt_To */
+	SMTP_ST_ERROR			/* no transaction until RSET or HELO */
+    } smtp_state;
+    /* from here down is zeroed when the structure is allocated */
+#define WORK_ZERO fwd
+    struct work *fwd;
+    IN_BC       msg_rd;			/* incoming mail message */
+    OUT_BC      msg_wt;			/* outoing mail message  */
+    IN_BC       reply_in;		/* incoming SMTP replies */
+    OUT_BC      reply_out;		/* outgoing SMTP replies */
+    int		total_hdrs, cr_hdrs;
+    int		parse_rcvd;		/* which received: header to parse */
+    u_int       dfgs;
+#    define      DFG_WORK_LOCK	    0x0001  /* hold the lock */
+#    define      DFG_MISSING_BODY   0x0002  /* missing message body */
+#    define      DFG_MTA_BODY	    0x0004  /* MTA wants the body */
+#    define      DFG_MTA_HEADER	    0x0008  /* MTA wants the X-DCC header */
+#    define      DFG_SEEN_HDR	    0x0010  /* have at least 1 header */
+#    define      DFG_PARSE_RCVD	    0x0020  /* parse Received header */
+#    define	 DFG_XCLIENT_NAME   0x0040  /* client name via XCLIENT */
+#    define	 DFG_XCLIENT_ADDR   0x0080  /* client addresses via XCLIENT */
+#    define	 DFG_XCLIENT_HELO   0x0100  /* HELO value via XCLIENT */
+#    define	 DFG_RECYCLE (DFG_WORK_LOCK | DFG_MTA_BODY		\
+			      | DFG_XCLIENT_NAME | DFG_XCLIENT_ADDR	\
+			      | DFG_XCLIENT_HELO)
+} WORK;
+
+/* use a free list to avoid malloc() overhead */
+static WORK *work_free;
+
+/* each dccifd job involves
+ *      a socket connected to the MTA or upstream proxy
+ *      a socket connected to the downstream proxy if using SMTP
+ *      a log file,
+ *      and a socket to talk to the DCC server.
+ * The file descriptors for the whitelists are accounted for in EXTRA_FILES */
+#define FILES_PER_JOB   4
+int max_max_work = MAX_SELECT_WORK;
+
+
+typedef struct user_domain {
+    struct user_domain *fwd;
+    const char	*nm;
+    int		len;
+    u_char	wildcard;
+} USER_DOMAIN;
+static USER_DOMAIN *user_domains;
+static char hostname[MAXHOSTNAMELEN+1] = "@";
+
+/* max seconds to wait for MTA
+ *      Longer than the longest timeout in RFC 2821 */
+#define MAX_MTA_DELAY   (10*60+5)
+
+
+static void sigterm(int);
+static u_char set_soc(DCC_EMSG, int, int, const char *);
+static void bind_listen(void);
+static void close_listen_soc(void);
+static void unlink_listen_sun(void);
+static void NRATTRIB *job_start(void *);
+static void job_close(WORK *);
+static void NRATTRIB job_exit(WORK *);
+static void NRATTRIB proxy_msg_truncated(WORK *);
+static void add_work(int);
+
+
+static void
+usage(const char* barg, const char *bvar)
+{
+	const char str[] = {
+	    "usage: [-VdbxANQ] [-G on | off | noIP | IPmask/xx] [-h homedir]"
+	    " [-I user]\n"
+	    "    [-p /sock | host,port,rhost/bits]"
+	    " [-o /sock | host,port]\n"
+	    "    [-D local-domain] [-r rejection-msg] [-m map] [-w whiteclnt]\n"
+	    "    [-U userdirs] [-a IGNORE | REJECT | DISCARD]\n"
+	    "    [-t type,[log-thold,][rej-thold]] [-g [not-]type]"
+	    " [-S header]\n"
+	    "    [-l logdir] [-R rundir] [-T tmpdir] [-j maxjobs]\n"
+	    "    [-B dnsbl-option] [-L ltype,facility.level]\n"
+	};
+	static u_char complained;
+
+	if (!complained) {
+		if (barg)
+			dcc_error_msg("unrecognized \"%s%s\"\nusage: %s\n..."
+				      " continuing",
+				      barg, bvar, str);
+		else
+			dcc_error_msg("%s\n... continuing", str);
+		complained = 1;
+	}
+}
+
+
+int NRATTRIB
+main(int argc, char **argv)
+{
+	DCC_EMSG emsg;
+#ifdef RLIMIT_NOFILE
+	struct rlimit nofile;
+	int old_rlim_cur;
+#endif
+	long l;
+	u_char log_tgts_set = 0;
+	const char *homedir = 0;
+	const char *logdir = 0;
+	const char *tmpdir = 0;
+	WORK *wp;
+	pthread_t tid;
+	DCC_SOCKLEN_T namelen;
+	const char *cp;
+	USER_DOMAIN *udom, *udom2, **udomp;
+	char *p;
+	int error, i;
+
+	emsg[0] = '\0';
+	if (*argv[0] == '/')
+		progpath = argv[0];
+	dcc_syslog_init(1, argv[0], 0);
+	dcc_clear_tholds();
+
+#ifdef RLIMIT_NOFILE
+	if (0 > getrlimit(RLIMIT_NOFILE, &nofile)) {
+		dcc_error_msg("getrlimit(RLIMIT_NOFILE): %s", ERROR_STR());
+		old_rlim_cur = 1000*1000;
+	} else {
+		old_rlim_cur = nofile.rlim_cur;
+		if (nofile.rlim_max < 1000*1000) {
+			i = nofile.rlim_max;
+#ifndef USE_POLL
+			if (i > FD_SETSIZE)
+				i = FD_SETSIZE;
+#endif
+			max_max_work = (i - EXTRA_FILES)/FILES_PER_JOB;
+			max_max_work_src = "RLIMIT_NOFILE limit";
+		}
+	}
+#endif /* RLIMIT_NOFILE */
+	if (max_max_work <= 0) {
+		dcc_error_msg("too few open files allowed");
+		max_max_work = MIN_MAX_WORK;
+	}
+	max_work = max_max_work;
+
+#define SLARGS "64VdbxANQW"		/* fix start-dccifd if these change */
+	while (-1 != (i = getopt(argc, argv, SLARGS"G:h:I:p:o:D:r:m:w:U:"
+				 "a:t:g:S:l:R:T:j:B:L:"))) {
+		switch (i) {
+#ifndef NO_IPV6
+		case '6':
+			use_ipv6 = 1;
+			break;
+#endif
+		case '4':
+			use_ipv6 = 0;
+			break;
+
+		case 'V':
+			fprintf(stderr, DCC_VERSION"\n");
+			exit(EX_OK);
+			break;
+
+		case 'd':
+			++dcc_clnt_debug;
+			break;
+
+		case 'b':
+			background = 0;
+			break;
+
+		case 'x':
+			try_extra_hard = DCC_CLNT_FG_NO_FAIL;
+			break;
+
+		case 'A':
+			chghdr = ADDHDR;
+			break;
+
+		case 'N':
+			chghdr = NOHDR;
+			break;
+
+		case 'Q':
+			dcc_query_only = 1;
+			break;
+
+		case 'G':
+			if (!dcc_parse_client_grey(optarg))
+				usage("-G", optarg);
+			break;
+
+		case 'W':		/* obsolete DCC off by default */
+			to_white_only = 1;
+			break;
+
+		case 'h':
+			homedir = optarg;
+			break;
+
+		case 'I':
+			dcc_daemon_su(optarg);
+			break;
+
+		case 'p':
+			listen_addr = optarg;
+			break;
+
+		case 'o':
+			proxy = 1;
+			cannot_reject = 0;
+			cannot_discard = 1;
+			p = strchr(optarg, ',');
+			if (!p) {
+				/* recognize single-ended (not-really-)-proxy */
+				if (!strcmp(optarg, _PATH_DEVNULL)) {
+					proxy_out_family = AF_UNSPEC;
+					break;
+				}
+				if (strlen(optarg)>=ISZ(proxy_out_sun.sun_path))
+					dcc_logbad(EX_USAGE, "invalid UNIX"
+						   " domain socket: -o %s",
+						   optarg);
+				strcpy(proxy_out_sun.sun_path, optarg);
+#ifdef HAVE_SA_LEN
+				proxy_out_sun.sun_len = SUN_LEN(&proxy_out_sun);
+#endif
+				proxy_out_sun.sun_family = AF_UNIX;
+				proxy_out_family = AF_UNIX;
+			} else {
+				cp = dcc_parse_nm_port(emsg, optarg,
+						       DCC_GET_PORT_INVALID,
+						       proxy_out_host,
+						       sizeof(proxy_out_host),
+						       &proxy_out_port, 0, 0,
+						       0, 0);
+				if (!cp)
+					dcc_logbad(dcc_ex_code, "%s", emsg);
+				if (*cp != '\0')
+					dcc_logbad(EX_USAGE,
+						   "invalid IP address: \"%s\"",
+						   optarg);
+				proxy_out_family = AF_INET; /* includes IPv6 */
+			}
+			break;
+
+		case 'D':
+			/* save user domain names sorted  by length
+			 * so that we can apply the most restrictive */
+			i = strlen(optarg);
+			if (*optarg == '\0'
+			    || ((optarg[0] == '@' || optarg[0] == '*')
+				&& i < 2)
+			    || strpbrk(optarg+1, "@*")) {
+				dcc_logbad(EX_USAGE,
+					   "invalid local-domain \"%s\"",
+					   optarg);
+				break;
+			}
+			udom2 = dcc_malloc(sizeof(*udom2));
+			memset(udom2, 0, sizeof(*udom2));
+			udom2->len = i;
+			udom2->nm = optarg;
+			if (optarg[0] == '*') {
+				udom2->wildcard = 1;
+				++udom2->nm;
+				--udom2->len;
+			}
+			udomp = &user_domains;
+			for (;;) {
+				udom = *udomp;
+				/* insert longer names before shorter names
+				 * insert wildcard before same, non-wildcard
+				 * because i includes the '*' */
+				if (!udom || i > udom->len) {
+					udom2->fwd = udom;
+					*udomp = udom2;
+					break;
+				}
+				udomp = &udom->fwd;
+			}
+			break;
+
+		case 'r':
+			parse_reply_arg(optarg);
+			break;
+
+		case 'm':
+			mapfile_nm = optarg;
+			break;
+
+		case 'w':
+			main_white_nm = optarg;
+			break;
+
+		case 'U':
+			parse_userdirs(optarg);
+			break;
+
+		case 'a':
+			if (!strcasecmp(optarg, "IGNORE")) {
+				action = CMN_IGNORE;
+			} else if (!strcasecmp(optarg, "REJECT")) {
+				action = CMN_REJECT;
+			} else if (!strcasecmp(optarg, "DISCARD")) {
+				action = CMN_DISCARD;
+			} else {
+				dcc_error_msg("unrecognized -a action: %s",
+					      optarg);
+			}
+			break;
+
+		case 't':
+			if (dcc_parse_tholds("-t ", optarg))
+				log_tgts_set = 1;
+			break;
+
+		case 'g':		/* honor not-spam "counts" */
+			dcc_parse_honor(optarg);
+			break;
+
+		case 'S':
+			dcc_add_sub_hdr(0, optarg);
+			break;
+
+		case 'l':		/* log rejected mail here */
+			logdir = optarg;
+			break;
+
+		case 'R':
+			rundir = optarg;
+			break;
+
+		case 'T':
+			tmpdir = optarg;
+			break;
+
+		case 'j':		/* maximum simultaneous jobs */
+			l = strtoul(optarg, &p, 10);
+			if (*p != '\0' || l < MIN_MAX_WORK) {
+				dcc_error_msg("invalid queue length %s",
+					      optarg);
+			} else if (l > max_max_work) {
+				dcc_error_msg("-j queue length %s"
+					      " larger than %s; using %d",
+					      optarg,
+					      max_max_work_src, max_max_work);
+				max_work = max_max_work;
+			} else {
+				max_work = l;
+			}
+			break;
+
+		case 'B':
+			if (!dcc_parse_dnsbl(emsg, optarg, progpath, 0))
+				dcc_error_msg("%s", emsg);
+			break;
+
+		case 'L':
+			if (dcc_parse_log_opt(optarg))
+				helper_save_arg("-L", optarg);
+			break;
+
+		default:
+			usage(optopt2str(optopt), "");
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	if (argc != 0)
+		usage(argv[0], "");
+
+	/* default -D setting to the local host name */
+	if (!user_domains) {
+		if (0 > gethostname(hostname+1, sizeof(hostname)-2)) {
+			dcc_error_msg("gethostname(): %s", ERROR_STR());
+		} else if ((i = strlen(hostname)) > 1) {
+			user_domains = dcc_malloc(sizeof(*user_domains));
+			memset(user_domains, 0, sizeof(*user_domains));
+			user_domains->len = i;
+			user_domains->nm = hostname;
+		}
+	}
+
+	dcc_cdhome(0, homedir, 0);
+	dcc_main_logdir_init(0, logdir);
+	tmp_path_init(tmpdir, logdir);
+
+	if (proxy_out_family == AF_INET) {
+		if (proxy_out_host[0] == '\0'
+		    || !strcmp(proxy_out_host, "@")) {
+			/* null or "@" means incoming host */
+			;
+		} else {
+			dcc_host_lock();
+			if (!dcc_get_host(proxy_out_host, use_ipv6, &error))
+				dcc_logbad(EX_NOHOST, "%s: %s",
+					   proxy_out_host,
+					   DCC_HSTRERROR(error));
+			proxy_out_su = dcc_hostaddrs[0];
+			*DCC_SU_PORTP(&proxy_out_su) = proxy_out_port;
+			dcc_host_unlock();
+		}
+	}
+
+	/* Open the incoming socket before our backgrounding fork() to
+	 * minimize races and allow better error reporting. */
+	bind_listen();
+
+	if (dcc_main_logdir[0] == '\0') {
+		if (log_tgts_set)
+			dcc_error_msg("log thresholds set with -t"
+				      " but no -l directory");
+		if (userdirs != '\0')
+			dcc_error_msg("no -l directory prevents per-user"
+				      " logging with -U");
+	}
+
+#ifdef RLIMIT_NOFILE
+	if (old_rlim_cur < (i = max_work*FILES_PER_JOB+EXTRA_FILES)) {
+		nofile.rlim_cur = i;
+		if (0 > setrlimit(RLIMIT_NOFILE, &nofile)) {
+			dcc_error_msg("setrlimit(RLIMIT_NOFILE,%d): %s",
+				      i, ERROR_STR());
+			max_work = old_rlim_cur/FILES_PER_JOB - EXTRA_FILES;
+			if (max_work <= 0) {
+				dcc_error_msg("only %d open files allowed"
+					      " by RLIMIT_NOFILE",
+					      old_rlim_cur);
+				max_work = MIN_MAX_WORK;
+			}
+		}
+	}
+#endif /* RLIMIT_NOFILE */
+
+	helper_init(max_work);
+
+	if (background) {
+		if (daemon(1, 0) < 0)
+			dcc_logbad(EX_OSERR, "daemon(): %s", ERROR_STR());
+
+		dcc_daemon_restart(rundir, 0);
+		dcc_pidfile(pidpath, rundir);
+	}
+
+	signal(SIGPIPE, SIG_IGN);
+	signal(SIGHUP, sigterm);
+	signal(SIGTERM, sigterm);
+	signal(SIGINT, sigterm);
+#ifdef SIGXFSZ
+	signal(SIGXFSZ, SIG_IGN);
+#endif
+
+	/* Be careful to start all threads only after the fork() in daemon(),
+	 * because some POSIX threads packages (e.g. FreeBSD) get confused
+	 * about threads in the parent.  */
+
+	cmn_init();
+	add_work(init_work);
+
+	if (listen_family != AF_UNIX) {
+		dcc_trace_msg(DCC_VERSION" listening to %s from %s for %s",
+			      lhost_port, rcidr,
+			      proxy ? "SMTP commands" : "ASCII protocol");
+	} else {
+		dcc_trace_msg(DCC_VERSION" listening to %s for %s",
+			      listen_addr,
+			      proxy ? "SMTP commands" : "ASCII protocol");
+	}
+	if (dcc_clnt_debug)
+		dcc_trace_msg("init_work=%d max_work=%d max_max_work=%d (%s)",
+			      total_work, max_work, max_max_work,
+			      max_max_work_src);
+
+	while (!stopping) {
+		/* delay for 1 second instead of forever to notice
+		 * when SIGTERM has said to stop */
+		i = dcc_select_poll(emsg, listen_soc, 1, DCC_US);
+		if (i < 0)
+			dcc_logbad(EX_OSERR, "%s", emsg);
+		if (i == 0)
+			continue;
+
+		/* A new connection is ready.  Allocate a context
+		 * block and create a thread */
+		lock_work();
+		wp = work_free;
+		if (!wp) {
+			if (total_work > max_work) {
+				/* pretend we weren't listening if we
+				 * are out of context blocks */
+				unlock_work();
+				sleep(1);
+				continue;
+			}
+			if (dcc_clnt_debug > 1)
+				dcc_trace_msg("add %d work blocks to %d",
+					      init_work, total_work);
+			add_work(init_work);
+			wp = work_free;
+		}
+		work_free = wp->fwd;
+		unlock_work();
+
+		/* clear most of context block for the new connection */
+		cmn_clear(&wp->cw, wp, 1);
+		wp->cw.helo[0] = '\0';
+		memset(&wp->WORK_ZERO, 0,
+		       sizeof(*wp) - ((char*)&wp->WORK_ZERO - (char*)wp));
+
+		namelen = sizeof(wp->proxy_in_su);
+		wp->proxy_in_soc = accept(listen_soc,
+					  &wp->proxy_in_su.sa, &namelen);
+		if (wp->proxy_in_soc < 0) {
+			dcc_error_msg("accept(): %s", ERROR_STR());
+			job_close(wp);
+			continue;
+		}
+		if (listen_family != AF_UNIX) {
+			struct in6_addr addr6, *ap;
+
+			if (wp->proxy_in_su.sa.sa_family == AF_INET6) {
+				ap = &wp->proxy_in_su.ipv6.sin6_addr;
+			} else {
+				ap = &addr6;
+				dcc_ipv4toipv6(ap,
+					       wp->proxy_in_su.ipv4.sin_addr);
+			}
+			if (!DCC_IN_BLOCK(*ap, raddr, rmask)) {
+				char str[DCC_SU2STR_SIZE];
+				dcc_error_msg("unauthorized client address %s",
+					      dcc_su2str(str, sizeof(str),
+							&wp->proxy_in_su));
+				job_close(wp);
+				continue;
+			}
+		}
+		if (!set_soc(emsg, wp->proxy_in_soc, listen_family, "MTA")) {
+			dcc_error_msg("%s", emsg);
+			job_close(wp);
+			continue;
+		}
+		i = pthread_create(&tid, 0, job_start, wp);
+		if (i) {
+			dcc_error_msg("pthread_create(): %s", ERROR_STR1(i));
+			job_close(wp);
+			continue;
+		}
+		i = pthread_detach(tid);
+		if (i != 0) {
+		   if (i != ESRCH)
+			   dcc_error_msg("pthread_detach(): %s", ERROR_STR1(i));
+		   else if (dcc_clnt_debug)
+			   dcc_trace_msg("pthread_detach(): %s", ERROR_STR1(i));
+		}
+	}
+
+	close_listen_soc();
+
+	totals_stop();
+
+	exit(stopping);
+}
+
+
+
+static void
+unlink_listen_sun(void)
+{
+	if (listen_family != AF_UNIX)
+		return;
+
+	/* there is an unavoidable race here */
+	if (0 > unlink(listen_sun.sun_path)
+	    && errno != ENOENT)
+		dcc_error_msg("unlink(%s): %s",
+			      listen_sun.sun_path, ERROR_STR());
+}
+
+
+
+static void
+bind_unix_listen(void)
+{
+	DCC_EMSG emsg;
+	struct stat sb;
+	int i;
+
+#ifdef HP_UX_BAD_AF_UNIX
+	dcc_logbad(EX_CONFIG, "HP-UX AF_UNIX does not support shutdown()");
+#endif
+
+	emsg[0] = '\0';
+	listen_family = AF_UNIX;
+
+	if (!listen_addr) {
+		/* use default UNIX domain socket */
+		snprintf(listen_sun.sun_path, sizeof(listen_sun.sun_path),
+			 "%s/"DCC_DCCIF_UDS, dcc_homedir);
+		listen_addr = listen_sun.sun_path;
+	} else {
+		if (strlen(listen_addr) >= ISZ(listen_sun.sun_path))
+			dcc_logbad(EX_USAGE, "invalid UNIX domain socket: %s",
+				   listen_addr);
+		strcpy(listen_sun.sun_path, listen_addr);
+	}
+#ifdef HAVE_SA_LEN
+	listen_sun.sun_len = SUN_LEN(&listen_sun);
+#endif
+	listen_sun.sun_family = AF_UNIX;
+
+	if (0 <= stat(listen_sun.sun_path, &sb)
+	    && !(S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode)))
+		dcc_logbad(EX_UNAVAILABLE, "non-socket present at %s",
+			   listen_sun.sun_path);
+
+	/* Look for a daemon already using our socket.  Do not give up
+	 * immediately in case a previous instance is slowly stopping. */
+	i = 0;
+	for (;;) {
+		listen_soc = socket(AF_UNIX, SOCK_STREAM, 0);
+		if (listen_soc < 0)
+			dcc_logbad(EX_OSERR, "socket(AF_UNIX): %s",
+				   ERROR_STR());
+		/* unlink it only if it looks like a dead socket */
+		if (0 > connect(listen_soc, (struct sockaddr *)&listen_sun,
+				sizeof(listen_sun))) {
+			if (errno == ECONNREFUSED || errno == ECONNRESET
+			    || errno == EACCES) {
+				unlink_listen_sun();
+			} else if (dcc_clnt_debug > 2
+				   && errno != ENOENT) {
+				dcc_trace_msg("connect(old server %s): %s",
+					      listen_sun.sun_path, ERROR_STR());
+			}
+			close(listen_soc);
+			break;
+		}
+		/* connect() worked so the socket is alive */
+		if (++i > 5*10)
+			dcc_logbad(EX_UNAVAILABLE,
+				   "something running with socket at %s",
+				   listen_sun.sun_path);
+		close(listen_soc);
+		usleep(100*1000);
+	}
+
+	listen_soc = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (listen_soc < 0)
+		dcc_logbad(EX_OSERR, "socket(AF_UNIX): %s", ERROR_STR());
+	if (0 > bind(listen_soc, (struct sockaddr *)&listen_sun,
+		     sizeof(listen_sun)))
+		dcc_logbad(EX_IOERR, "bind(%s) %s",
+			   listen_sun.sun_path, ERROR_STR());
+	if (0 > chmod(listen_sun.sun_path, 0666))
+		dcc_error_msg("chmod(%s, 0666): %s",
+			      listen_sun.sun_path, ERROR_STR());
+}
+
+
+
+static void
+bind_tcp_listen(void)
+{
+	DCC_EMSG emsg;
+	char lhost[MAXHOSTNAMELEN];
+	u_int16_t lport;
+	DCC_SOCKU su;
+	char *duparg;
+	const char *cp;
+	int on, error;
+
+	emsg[0] = '\0';
+
+	duparg = strdup(listen_addr);
+	lhost_port = duparg;
+	duparg = strchr(duparg, ',');
+	if (!duparg)
+		dcc_logbad(EX_USAGE, "missing port number in \"%s\"",
+			   listen_addr);
+
+	duparg = strchr(duparg+1, ',');
+	if (!duparg)
+		dcc_logbad(EX_USAGE, "missing rhost in \"%s\"",
+			   listen_addr);
+	*duparg++ = '\0';
+
+	rcidr = duparg;
+	if (0 >= dcc_str2cidr(emsg, &raddr, &rmask, 0, rcidr, 0, 0))
+		dcc_logbad(EX_USAGE, "invalid rhost and mask \"%s\"",
+			   rcidr);
+
+	cp = dcc_parse_nm_port(emsg, lhost_port, DCC_GET_PORT_INVALID,
+			       lhost, sizeof(lhost), &lport, 0, 0, 0, 0);
+	if (!cp)
+		dcc_logbad(dcc_ex_code, "%s", emsg);
+	if (*cp != '\0')
+		dcc_logbad(EX_USAGE, "invalid IP address: \"%s\"",
+			   lhost_port);
+
+	if (lhost[0] == '\0' || !strcmp(lhost, "@")) {
+		/* null or "@" means INADDR_ANY */
+		dcc_mk_su(&su, use_ipv6 ? AF_INET6: AF_INET, 0, lport);
+	} else {
+		dcc_host_lock();
+		if (!dcc_get_host(lhost, use_ipv6, &error))
+			dcc_logbad(EX_NOHOST, "%s: %s",
+				   lhost, DCC_HSTRERROR(error));
+		su = dcc_hostaddrs[0];
+		*DCC_SU_PORTP(&su) = lport;
+		dcc_host_unlock();
+	}
+
+	listen_soc = socket(su.sa.sa_family, SOCK_STREAM, 0);
+	if (listen_soc < 0)
+		dcc_logbad(EX_OSERR, "socket(): %s", ERROR_STR());
+
+	on = 1;
+	if (0 > setsockopt(listen_soc, SOL_SOCKET, SO_REUSEADDR,
+			   &on, sizeof(on)))
+		dcc_error_msg("setsockopt(listen %s, SO_REUSADDR): %s",
+			      dcc_su2str_err(&su), ERROR_STR());
+
+	if (0 > bind(listen_soc, &su.sa, DCC_SU_LEN(&su)))
+		dcc_logbad(EX_UNAVAILABLE, "bind(%s) %s",
+			   lhost_port, ERROR_STR());
+
+	listen_family = su.sa.sa_family;
+}
+
+
+
+static u_char
+set_soc(DCC_EMSG emsg, int s, int family, const char *sname)
+{
+	int on;
+
+	if (0 > fcntl(s, F_SETFD, FD_CLOEXEC)) {
+		dcc_pemsg(EX_IOERR, emsg,
+			  "fcntl(%s, F_SETFD, FD_CLOEXEC): %s",
+			  sname, ERROR_STR());
+		return 0;
+	}
+
+	if (family != AF_UNIX) {
+		on = 1;
+		if (0 > setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
+				   &on, sizeof(on))) {
+			dcc_pemsg(EX_IOERR, emsg,
+				  "setsockopt(%s, SO_KEEPALIVE): %s",
+				  sname, ERROR_STR());
+			return 0;
+		}
+	}
+
+	/* use non-blocking sockets so that we can read entire lines
+	 * without knowing how big they are or expecting the operating
+	 * system to allow peeking at its buffers */
+	if (-1 == fcntl(s, F_SETFL,
+			fcntl(s, F_GETFL, 0) | O_NONBLOCK)) {
+		dcc_pemsg(EX_OSERR, emsg, "fcntl(%s, O_NONBLOCK): %s",
+			  sname, ERROR_STR());
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+static void
+bind_listen(void)
+{
+	DCC_EMSG emsg;
+	char *p;
+
+	/* It is a TCP address if it has a port number and mask.
+	 * Otherwise it is a UNIX domain socket.
+	 */
+	if (listen_addr != 0
+	    && (p = strchr(listen_addr, ',')) != 0
+	    && strchr(p, ',')) {
+		bind_tcp_listen();
+	} else {
+		bind_unix_listen();
+	}
+
+	if (!set_soc(emsg, listen_soc, listen_family, "main socket"))
+		dcc_logbad(dcc_ex_code, "%s", emsg);
+	if (0 > listen(listen_soc, 10))
+		dcc_logbad(EX_IOERR, "listen(): %s", ERROR_STR());
+}
+
+
+
+static u_char				/* 0=EOF, 1=read something */
+soc_read(WORK *wp, IN_BC *bc)
+{
+	int fspace, len, total, i;
+
+	if (bc->out >= bc->in)
+		bc->out = bc->in = bc->base;
+
+	fspace = &bc->base[bc->size] - bc->in;
+	if (fspace < bc->size/8) {
+		if (bc->out == bc->base) {
+			thr_error_msg(&wp->cw, "buffer overrun; in=%d",
+				      (int)(bc->in - bc->base));
+			job_exit(wp);
+		}
+		len = bc->in - bc->out;
+		memmove(bc->base, bc->out, len);
+		bc->out = bc->base;
+		bc->in = bc->out+len;
+		fspace = bc->size - len;
+	}
+
+	if (wp->dfgs & DFG_WORK_LOCK)
+		unlock_work();
+
+	if (!bc->socp || *bc->socp < 0)
+		dcc_logbad(EX_SOFTWARE, "attempt to read closed socket");
+
+	for (;;) {
+		i = dcc_select_poll(wp->cw.emsg, *bc->socp, 1,
+				    MAX_MTA_DELAY*DCC_US);
+		if (i > 0)
+			break;
+		if (i < 0) {
+			thr_error_msg(&wp->cw, "%s", wp->cw.emsg);
+		} else {
+			thr_error_msg(&wp->cw, "MTA read timeout");
+		}
+		job_exit(wp);
+	}
+	total = read(*bc->socp, bc->in, fspace);
+	if (total < 0) {
+		if (!proxy || dcc_clnt_debug)
+			thr_error_msg(&wp->cw, "read(sock): %s", ERROR_STR());
+		job_exit(wp);
+	}
+	bc->in += total;
+
+	if (wp->dfgs & DFG_WORK_LOCK)
+		lock_work();
+	return total != 0;
+}
+
+
+
+/* ensure there is another line in the buffer */
+static u_char				/* 0=eof or buffer overflow */
+msg_read_line(WORK *wp, IN_BC *bc)
+{
+	int i;
+	char *p;
+
+	for (;;) {
+		p = bc->out;
+		i = bc->in - p;
+		if (i != 0) {
+			/* look for <LF> for ASCII protocol
+			 * or <CR><LF> for SMTP */
+			p = memchr(p, '\n', i);
+			if (p
+			    && (!proxy
+				|| (p > bc->out && *(p-1) == '\r'))) {
+				bc->next_line = p+1;
+				return 1;
+			}
+		}
+
+		if (!soc_read(wp, bc))
+			return 0;
+	}
+}
+
+
+
+static void
+buf_write_flush(WORK *wp, OUT_BC *bc)
+{
+	const char *buf;
+	int len, i;
+
+	len = bc->len;
+	if (!len)
+		return;
+	bc->len = 0;
+
+	if (!bc->socp || *bc->socp < 0)
+		dcc_logbad(EX_SOFTWARE, "attempt to write closed socket");
+
+	buf = bc->base;
+	do {
+		i = dcc_select_poll(wp->cw.emsg, *bc->socp, 0,
+				    MAX_MTA_DELAY*DCC_US);
+		if (i < 0) {
+			thr_error_msg(&wp->cw, "%s", wp->cw.emsg);
+			job_exit(wp);
+		}
+		if (i == 0) {
+			thr_error_msg(&wp->cw, "MTA write timeout");
+			job_exit(wp);
+		}
+		i = write(*bc->socp, buf, len);
+		if (i < 0) {
+			if (DCC_BLOCK_ERROR())
+				continue;
+			thr_error_msg(&wp->cw, "write(MTA socket,%d): %s",
+				      len, ERROR_STR());
+			job_exit(wp);
+		}
+		if (i == 0) {
+			thr_error_msg(&wp->cw, "write(MTA socket,%d)=%d",
+				      len, i);
+			job_exit(wp);
+		}
+		buf += i;
+		len -= i;
+	} while (len > 0);
+}
+
+
+
+static void
+buf_write(WORK *wp, OUT_BC *bc, const void *buf, u_int len)
+{
+	u_int n;
+
+	for (;;) {
+		n = bc->size - bc->len;
+		if (n == 0)
+			dcc_logbad(EX_SOFTWARE, "impossible buffer space");
+		if (n > len)
+			n = len;
+		memcpy(&bc->base[bc->len], buf, n);
+		if ((bc->len += n) >= bc->size)
+			buf_write_flush(wp, bc);
+		if ((len -= n) == 0)
+			return;
+		buf = (void *)((char *)buf + n);
+	}
+}
+
+
+
+static void
+tmp_write(WORK *wp, const void *buf, int len)
+{
+	if (!cmn_write_tmp(&wp->cw, buf, len)) {
+		thr_error_msg(&wp->cw, "%s", wp->cw.emsg);
+		job_exit(wp);
+	}
+}
+
+
+
+static int
+tmp_read(WORK *wp, void *buf, int len)
+{
+	int i;
+
+	if (wp->cw.tmp_fd < 0)
+		return 0;
+
+	i = read(wp->cw.tmp_fd, buf, len);
+	if (i < 0) {
+		thr_error_msg(&wp->cw, "read(%s,%d): %s",
+			      wp->cw.tmp_nm, len, ERROR_STR());
+		job_exit(wp);
+	}
+	return i;
+}
+
+
+
+/* fill MTA read buffer from temporary file */
+static int
+tmp_read_msg_in(WORK *wp)
+{
+	int i;
+
+	/* preserving what is already in it */
+	i = wp->msg_rd.in - wp->msg_rd.out;
+	if (i > 0)
+		memmove(wp->msg_rd.base, wp->msg_rd.out, i);
+	wp->msg_rd.out = wp->msg_rd.base;
+	wp->msg_rd.in = wp->msg_rd.out+i;
+
+	wp->msg_rd.in += tmp_read(wp, wp->msg_rd.in, wp->msg_rd.size - i);
+	return wp->msg_rd.in > wp->msg_rd.out;
+}
+
+
+
+/* Create the contexts. */
+static void
+add_work(int i)
+{
+	WORK *wp;
+
+	total_work += i;
+
+	wp = dcc_malloc(sizeof(*wp)*i);
+	memset(wp, 0, sizeof(*wp)*i);
+
+	while (i-- != 0) {
+		wp->proxy_in_soc = -1;
+		wp->proxy_out_soc = -1;
+		cmn_create(&wp->cw);
+		wp->fwd = work_free;
+		work_free = wp;
+		++wp;
+	}
+}
+
+
+
+void
+work_clean(void)
+{
+	WORK *wp;
+	int keep, delete;
+
+	lock_work();
+	keep = 5;
+	delete = init_work;
+	for (wp = work_free; wp; wp = wp->fwd) {
+		if (!wp->cw.dcc_ctxt)
+			break;
+		if (--keep > 0)
+			continue;
+		dcc_clnt_soc_close(wp->cw.dcc_ctxt);
+		if (--delete <= 0)
+			break;
+	}
+	unlock_work();
+}
+
+
+
+static void
+job_close(WORK *wp)
+{
+	if (wp->dfgs & DFG_WORK_LOCK) {
+		wp->dfgs &= ~DFG_WORK_LOCK;
+		unlock_work();
+	}
+
+	wp->msg_rd.socp = 0;
+	if (wp->msg_wt.socp) {
+		buf_write_flush(wp, &wp->msg_wt);
+		wp->msg_wt.socp = 0;
+	}
+	wp->reply_in.socp = 0;
+	if (wp->reply_out.socp) {
+		buf_write_flush(wp, &wp->reply_out);
+		wp->reply_out.socp = 0;
+	}
+
+	cmn_close_tmp(&wp->cw);
+
+	if (wp->proxy_in_soc >= 0) {
+		if (0 > close(wp->proxy_in_soc)
+		    && (dcc_clnt_debug
+			|| (errno != ECONNRESET
+			    && errno != ENOTCONN)))
+			thr_error_msg(&wp->cw, "close(proxy input socket): %s",
+				      ERROR_STR());
+		wp->proxy_in_soc = -1;
+	}
+	if (wp->proxy_out_soc >= 0) {
+		if (0 > close(wp->proxy_out_soc))
+			thr_error_msg(&wp->cw, "close(proxy output socket): %s",
+				      ERROR_STR());
+		wp->proxy_out_soc = -1;
+	}
+	log_stop(&wp->cw);
+
+	lock_work();
+	free_rcpt_sts(&wp->cw, 0);
+	wp->fwd = work_free;
+	work_free = wp;
+	unlock_work();
+}
+
+
+
+static void NRATTRIB
+job_exit(WORK *wp)
+{
+	job_close(wp);
+	pthread_exit(0);
+	/* mostly to suppress warning */
+	dcc_logbad(EX_OSERR, "pthread_exit() returned");
+}
+
+
+
+/* write headers or body data from the MTA read buffer to the MTA */
+static void
+mta_write(WORK *wp, char *end)
+{
+	int len;
+
+	len = end - wp->msg_rd.out;
+	if (len <= 0)
+		return;
+	buf_write(wp, &wp->msg_wt, wp->msg_rd.out, len);
+	wp->msg_rd.out = end;
+}
+
+
+
+/* copy headers from the temporary file to the MTA */
+static void
+hdrs_copy(WORK *wp)
+{
+	enum {START_HDR, SKIP_HDR, COPY_HDR} cmode;
+	const char *nl;
+	char *bol, *eol;
+	int i;
+
+	if (-1 == lseek(wp->cw.tmp_fd, 0, SEEK_SET)) {
+		thr_error_msg(&wp->cw, "rewind %s: %s",
+			      wp->cw.tmp_nm, ERROR_STR());
+		job_exit(wp);
+	}
+
+	cmode = START_HDR;
+	wp->msg_rd.in = wp->msg_rd.out = wp->msg_rd.base;
+	for (;;) {
+		/* fill wp->msg_rd while keeping anything already present */
+		if (!tmp_read_msg_in(wp))
+			return;
+
+		bol = wp->msg_rd.out;
+		while ((i = wp->msg_rd.in - bol) > 0) {
+			/* Find the end of the next line. */
+			eol = memchr(bol, '\n', i);
+			if (!eol) {
+				/* Fill the buffer if we can't find '\n''
+				 * and the buffer has room */
+				if (i < wp->msg_rd.size
+				    && wp->msg_rd.out != wp->msg_rd.base)
+					break;
+				/* pretend the header ended with the buffer
+				 * if there is no room */
+				eol = wp->msg_rd.in-1;
+				nl = 0;
+			} else if (eol+1 >= wp->msg_rd.in) {
+				/* get the character after '\n' */
+				if (i < wp->msg_rd.size
+				    && wp->msg_rd.out != wp->msg_rd.base)
+					break;
+				/* pretend we could not find it if there
+				 * is no room in the buffer
+				 * or if we are at end of file.
+				 * This will also end the headers */
+				nl = 0;
+			} else {
+				nl = eol+1;
+			}
+
+			if (cmode == START_HDR) {
+				/* We are at start of a header or the body.
+				 * Quit before line of "\n" or "\r\n" */
+				if (eol == bol
+				    || (eol == bol+1 && *bol == '\r')) {
+					/* write any preceding lines */
+					mta_write(wp, bol);
+					return;
+				}
+
+				/* Look for our header
+				 * Assume the buffer is larger than the
+				 * largest possible X-DCC field name. */
+				if (chghdr == SETHDR
+				    && is_xhdr(bol, eol-bol)) {
+					/* skip it
+					 * and copy any preceding headers. */
+					mta_write(wp, bol);
+					cmode = SKIP_HDR;
+				} else {
+					cmode = COPY_HDR;
+				}
+			}
+			if (cmode == SKIP_HDR)
+				wp->msg_rd.out = eol+1;
+
+			/* Check the character after '\n' for
+			 * whitespace indicating continuation */
+			if (nl && *nl  != ' ' && *nl != '\t') {
+				cmode = START_HDR;
+				/* deal with SMTP transparency */
+				if (proxy && *nl == '.')
+					*eol-- = '.';
+			}
+
+			bol = eol+1;
+		}
+
+		mta_write(wp, bol);
+	}
+}
+
+
+
+static void
+add_hdr(void *wp0, const char *buf, u_int buf_len)
+{
+	WORK *wp = wp0;
+
+	buf_write(wp, &wp->msg_wt, buf, buf_len);
+}
+
+
+
+static void
+body_copy(WORK *wp)
+{
+	u_char seen_crlf;
+	char *p;
+
+	hdrs_copy(wp);
+
+	if (chghdr != NOHDR && wp->cw.header.buf[0] != '\0') {
+		/* write X-DCC header
+		 *	end with "\r\n" if at least
+		 *	half of the header lines ended that way */
+		xhdr_write(add_hdr, wp, wp->cw.header.buf,
+			   wp->cw.header.used,
+			   wp->cr_hdrs > wp->total_hdrs/2);
+	}
+
+	/* copy body */
+	seen_crlf = 2;
+	do {
+		p = wp->msg_rd.out;
+		while (p < wp->msg_rd.in) {
+			if (seen_crlf == 1) {
+				seen_crlf = (*p++ == '\n') ? 2 : 0;
+				continue;
+			}
+			if (seen_crlf == 2) {
+				seen_crlf = 0;
+				if (*p == '.' && proxy) {
+					mta_write(wp, p);
+					buf_write(wp, &wp->msg_wt, ".", 1);
+				}
+			}
+
+			if (!proxy)
+				break;
+			p = memchr(p, '\r', wp->msg_rd.in-p);
+			if (!p)
+				break;
+			seen_crlf = 1;
+			++p;
+		}
+		mta_write(wp, wp->msg_rd.in);
+	} while (tmp_read_msg_in(wp));
+}
+
+
+
+static void
+close_listen_soc(void)
+{
+	if (pidpath[0] != '\0') {
+		unlink(pidpath);
+		pidpath[0] = '\0';
+	}
+
+	if (listen_soc >= 0) {
+		unlink_listen_sun();
+		if (0 > close(listen_soc))
+			dcc_error_msg("close(main socket): %s",
+				      ERROR_STR());
+		listen_soc = -1;
+	}
+}
+
+
+
+/* watch for fatal signals */
+static void
+sigterm(int sig)
+{
+	stopping = 100 + sig;
+	unlink_listen_sun();
+	dcc_clnt_stop_resolve();
+	signal(sig, SIG_DFL);		/* quit on repeated signals */
+}
+
+
+
+void
+user_reject_discard(UATTRIB CMN_WORK *cwp, UATTRIB RCPT_ST *rcpt_st)
+{
+	/* dccifd has no way to tell the MTA to remove a recipient
+	 * after the Rcpt_To command */
+	thr_error_msg(cwp, "cannot discard an individual target");
+}
+
+
+
+static void
+get_helo(WORK *wp, const char *vp, int len)
+{
+	if (len < DCC_HELO_MAX-1) {
+		memcpy(wp->cw.helo, vp, len);
+		wp->cw.helo[len] = '\0';
+	} else {
+		/* if the HELO value is too long, capture what we can
+		 * and indicate that we got only part of it */
+		len = DCC_HELO_MAX-1;
+		memcpy(wp->cw.helo, vp, len);
+		strcpy(&wp->msg_rd.out[DCC_HELO_MAX-ISZ(DCC_HELO_CONT)],
+		       DCC_HELO_CONT);
+	}
+}
+
+
+
+/* Get the next header field
+ *      The line is copied to the temporary file and then null terminated
+ *      in the buffer */
+static u_char				/* 1=have one, 0=end of headers */
+get_hdr(WORK *wp)
+{
+	int tf_len;			/* header bytes written to temp file */
+	int hdr_len;			/* total header length */
+	int nlen;			/* length of next chunk of header */
+	char *np;			/* next byte after field */
+	char *p;
+	u_char at_bol;			/* 1=at start of a line of header */
+
+	tf_len = 0;
+	hdr_len = 0;
+	at_bol = 1;
+	for (;;) {
+		/* get another line of the header */
+		np = wp->msg_rd.out + hdr_len;
+		nlen = wp->msg_rd.in - np;
+		if (nlen <= 1) {
+			/* we do not have enough data */
+read_more:;
+			if (hdr_len > DCC_HDR_CK_MAX) {
+				/* we already have more than DCC_HDR_CK_MAX
+				 * bytes of the header.
+				 * Keep only DCC_HDR_CK_MAX bytes of it
+				 * in the buffer.  We will eventually
+				 * return only the first DCC_HDR_CK_MAX
+				 * bytes and an arbitrary part of the tail
+				 * to our caller. */
+				if (tf_len < hdr_len)
+					tmp_write(wp, wp->msg_rd.out+tf_len,
+						  hdr_len - tf_len);
+				/* discard all but the first DCC_HDR_CK_MAX
+				 * bytes of the header */
+				hdr_len = DCC_HDR_CK_MAX;
+				tf_len = DCC_HDR_CK_MAX;
+				if (nlen != 0)
+					wp->msg_rd.out[DCC_HDR_CK_MAX] = *np;
+				wp->msg_rd.in = (wp->msg_rd.out + DCC_HDR_CK_MAX
+						 + nlen);
+			}
+			/* Get more data, possibly sliding what we already
+			 * have down in the buffer.  That would move
+			 * wp->msg_rd.in to wp->msg_rd.base. */
+			if (!soc_read(wp, &wp->msg_rd)) {
+				/* EOF implies the message body including the
+				 * separating blank line is missing.
+				 * That is impossible for the ASCII dccifd
+				 * protocol because there should be at least
+				 * a Received header.
+				 * When running as a proxy, we should get
+				 * "\r\n.\r\n"
+				 * Fake newlines until we get out of here
+				 * so that we will log whatever we got */
+				wp->dfgs |= DFG_MISSING_BODY;
+				if (wp->cr_hdrs >= wp->total_hdrs/2)
+					*wp->msg_rd.in++ = '\r';
+				*wp->msg_rd.in++ = '\n';
+			}
+			continue;
+		}
+
+		if (at_bol) {
+			/* deal with SMTP transparency */
+			if (*np == '.' && proxy) {
+				if (nlen < 3)
+					goto read_more;
+				if (np[1] != '\r' || np[2] != '\n') {
+					memmove(np, np+1, --nlen);
+				} else if (hdr_len == 0) {
+					/* ".\r\n" at the start of a line
+					 * ends the message. In this case
+					 * the message body including the
+					 * separating blank line is missing.
+					 * Stop short before the ".\r\n" */
+					return 0;
+				}
+			}
+
+			/* stop before the next line if it is
+			 * not a continuation of the current header */
+			if (hdr_len != 0
+			    && *np != ' '
+			    && *np != '\t') {
+				if (tf_len < hdr_len)
+					tmp_write(wp, wp->msg_rd.out+tf_len,
+						  hdr_len - tf_len);
+				wp->msg_rd.out[hdr_len-1] = '\0';
+				if (hdr_len > 1
+				    && wp->msg_rd.out[hdr_len-2] == '\r')
+					++wp->cr_hdrs;
+				wp->msg_rd.next_line = &wp->msg_rd.out[hdr_len];
+				return 1;
+			}
+		}
+
+		/* find the end of the next line */
+		p = memchr(np, '\n', nlen);
+		if (p) {
+			hdr_len += ++p - np;
+			/* quit at the end of headers */
+			if (hdr_len == 1
+			    || (hdr_len == 2 && *wp->msg_rd.out == '\r'))
+				return 0;
+			at_bol = 1;
+		} else {
+			hdr_len += nlen;
+			at_bol = 0;
+		}
+	}
+}
+
+
+
+static void
+get_hdrs(WORK *wp)
+{
+	const char *p, *rh;
+
+	for (;;) {
+		/* stop at the separator between the body and headers */
+		if (!get_hdr(wp))
+			break;
+		++wp->total_hdrs;
+
+#define GET_HDR_CK(h,t) {						\
+			if (!CLITCMP(wp->msg_rd.out, h)) {		\
+				dcc_get_cks(&wp->cw.cks, DCC_CK_##t,    \
+					    &wp->msg_rd.out[LITZ(h)], 1); \
+				wp->dfgs |= DFG_SEEN_HDR;		\
+				wp->msg_rd.out = wp->msg_rd.next_line;  \
+				continue;}}
+		GET_HDR_CK(DCC_XHDR_TYPE_FROM":", FROM);
+		GET_HDR_CK(DCC_XHDR_TYPE_MESSAGE_ID":", MESSAGE_ID);
+#undef GET_HDR_CK
+
+		/* notice UNIX From_ line */
+		if (!(wp->dfgs & DFG_SEEN_HDR)
+		    && wp->cw.env_from[0] == '\0'
+		    && parse_unix_from(wp->msg_rd.out, wp->cw.env_from,
+				       sizeof(wp->cw.env_from))) {
+			wp->dfgs |= DFG_SEEN_HDR;
+			wp->msg_rd.out = wp->msg_rd.next_line;
+			continue;
+		}
+
+		if (wp->cw.env_from[0] == '\0'
+		    && parse_return_path(wp->msg_rd.out, wp->cw.env_from,
+					 sizeof(wp->cw.env_from))) {
+			wp->dfgs |= DFG_SEEN_HDR;
+			wp->msg_rd.out = wp->msg_rd.next_line;
+			continue;
+		}
+
+		if (!CLITCMP(wp->msg_rd.out, DCC_XHDR_TYPE_RECEIVED":")) {
+			p = &wp->msg_rd.out[LITZ(DCC_XHDR_TYPE_RECEIVED":")];
+
+			/* compute checksum of the last Received: header */
+			dcc_get_cks(&wp->cw.cks, DCC_CK_RECEIVED, p, 1);
+
+			wp->dfgs |= DFG_SEEN_HDR;
+			wp->msg_rd.out = wp->msg_rd.next_line;
+
+			/* pick IP address out of first Received: header */
+			if (!(wp->dfgs & DFG_PARSE_RCVD)
+			    || --wp->parse_rcvd >= 0)
+				continue;
+			rh = parse_received(p, &wp->cw.cks,
+					    (wp->cw.helo[0] == '\0')
+					    ? wp->cw.helo : 0,
+					    sizeof(wp->cw.helo),
+					    wp->cw.sender_str,
+					    sizeof(wp->cw.sender_str),
+					    wp->cw.sender_name,
+					    sizeof(wp->cw.sender_name));
+			if (rh == 0) {
+				/* to avoid being fooled by forged
+				 * headers, stop at a strange one */
+				wp->dfgs &= ~DFG_PARSE_RCVD;
+
+			} else if (*rh != '\0') {
+				thr_log_print(&wp->cw, 1,
+					      "skip %s Received: header\n", rh);
+
+			} else if (!check_mx_listing(&wp->cw)) {
+				/* we know the client */
+				wp->dfgs &= ~DFG_PARSE_RCVD;
+			}
+			continue;
+		}
+
+		/* Notice MIME multipart boundary definitions */
+		dcc_ck_mime_hdr(&wp->cw.cks, wp->msg_rd.out, 0);
+
+		if (dcc_ck_get_sub(&wp->cw.cks, wp->msg_rd.out, 0))
+			wp->dfgs |= DFG_SEEN_HDR;
+
+		/* notice any sort of header */
+		if (!(wp->dfgs & DFG_SEEN_HDR)) {
+			for (p = wp->msg_rd.out; ; ++p) {
+				if (*p == ':') {
+					wp->dfgs |= DFG_SEEN_HDR;
+					break;
+				}
+				if (*p <= ' ' || *p >= 0x7f)
+					break;
+			}
+		}
+
+		wp->msg_rd.out = wp->msg_rd.next_line;
+	}
+
+	/* Create a checksum for a null Message-ID header if there
+	 * was no Message-ID header.  */
+	if (wp->cw.cks.sums[DCC_CK_MESSAGE_ID].type != DCC_CK_MESSAGE_ID)
+		dcc_get_cks(&wp->cw.cks, DCC_CK_MESSAGE_ID, "", 0);
+}
+
+
+
+/* Assume for now that the sender is the SMTP client.  Received: headers
+ * might challenge that assumption */
+static void
+get_sender(WORK *wp)
+{
+	strcpy(wp->cw.sender_name, wp->cw.clnt_name);
+	strcpy(wp->cw.sender_str, wp->cw.clnt_str);
+	if (wp->cw.sender_str[0] == '\0'
+	    || check_mx_listing(&wp->cw)) {
+		/* try Received: header if client is unknown or MX server*/
+		wp->dfgs |= DFG_PARSE_RCVD;
+	}
+}
+
+
+
+static u_char				/* 0=temporary failure, 1=ok */
+get_body(WORK *wp)
+{
+	char *p;
+	char buf[1024];
+	u_char bol;
+	int buflen, i;
+
+	/* We must make a copy of the entire message in a temporary file
+	 * if the MTA wants a copy with the X-DCC header added
+	 * Copy the headers to a temporary file because the official
+	 * log file needs the SMTP client IP address and envelope information
+	 * before the header lines.  The log file needs all of the header
+	 * lines including stray X-DCC lines, but those lines must be removed
+	 * from the output file. */
+	if (!cmn_open_tmp(&wp->cw)) {
+		if (wp->dfgs & DFG_MTA_BODY) {
+			dcc_error_msg("fatal error: %s", wp->cw.emsg);
+			return 0;
+		}
+		dcc_error_msg("%s", wp->cw.emsg);
+	}
+
+	get_hdrs(wp);
+
+	/* log IP address and so forth that we may have collected from
+	 * the headers or Postfix XFORWARD or XCLIENT ESTMP extension */
+	thr_log_envelope(&wp->cw, 0);
+
+	/* Check DNS blacklists for STMP client and envelope sender
+	 * unless DNSBL checks are turned off for all of the recipients */
+	if (wp->cw.cks.dnsbl) {
+		if (wp->cw.cks.sums[DCC_CK_IP].type == DCC_CK_IP)
+			dcc_client_dnsbl(wp->cw.cks.dnsbl, &wp->cw.cks.ip_addr,
+					 wp->cw.sender_name);
+		if (wp->cw.mail_host[0] != '\0')
+			dcc_mail_host_dnsbl(wp->cw.cks.dnsbl, wp->cw.mail_host);
+	}
+
+	/* copy headers from temporary file to log file */
+	if (wp->cw.log_fd >= 0 && wp->cw.tmp_fd >= 0) {
+		if (0 > lseek(wp->cw.tmp_fd, 0, SEEK_SET)) {
+			thr_error_msg(&wp->cw, "rewind %s: %s",
+				      wp->cw.tmp_nm, ERROR_STR());
+			job_exit(wp);
+		}
+		for (;;) {
+			buflen = tmp_read(wp, buf, sizeof(buf));
+			if (buflen <= 0)
+				break;
+			log_body_write(&wp->cw, buf, buflen);
+		}
+	}
+
+	if (wp->dfgs & DFG_MISSING_BODY)
+		thr_error_msg(&wp->cw, "missing message body");
+
+	/* collect the body */
+	bol = 1;
+	for (;;) {
+		buflen = wp->msg_rd.in - wp->msg_rd.out;
+		if (buflen <= 0) {
+			if (!soc_read(wp, &wp->msg_rd))
+				break;
+			buflen = wp->msg_rd.in - wp->msg_rd.out;
+		}
+
+		/* deal with SMTP transparency and detect end of message */
+		if (proxy) {
+			if (bol) {
+				/* We are at the beginning of a line.
+				 * There will always be at least 3 more bytes
+				 * in the message, ".\r\n" */
+				if (buflen < 3) {
+					if (!soc_read(wp, &wp->msg_rd))
+						proxy_msg_truncated(wp);
+					buflen = wp->msg_rd.in - wp->msg_rd.out;
+				}
+				/* Whether a '.' is the end of the data
+				 * or an escaped '.', we will discard it. */
+				if (*wp->msg_rd.out == '.'
+				    && buflen >= 1) {
+					++wp->msg_rd.out;
+					--buflen;
+					/* if it is followed by "\r\n", then
+					 * we have the end of the message */
+					if (buflen >= 2
+					    && wp->msg_rd.out[0] == '\r'
+					    && wp->msg_rd.out[1] == '\n') {
+					    wp->msg_rd.out += 2;
+					    break;
+					}
+				}
+				/* we are still at the beginning of a line */
+			}
+
+			/* check all of the lines in the buffer */
+			p = wp->msg_rd.out;
+			for (;;) {
+				i = p - wp->msg_rd.out;
+				p = memchr(p, '\r', buflen-i);
+				if (!p) {
+					/* We have checked all of the buffer.
+					 * It does not end near an end of line.
+					 * So log the block of body lines
+					 * and leave the part of a line
+					 * ending the buffer for later. */
+					bol = 0;
+					break;
+				}
+				/* We have found '\r'
+				 * Maybe it will be followed by '\n' */
+				i = ++p - wp->msg_rd.out;
+				if (i+2 >= buflen) {
+					/* '\r' ends or almost ends buffer. */
+					bol = 0;
+					buflen = i-1;
+					if (buflen > 0) {
+					    /* The buffer ends with "\rX"
+					     * and contains text before '\r'
+					     * Process up to the '\r' */
+					    break;
+					}
+					/* buffer starts with "\r" */
+					if (!soc_read(wp, &wp->msg_rd))
+					    proxy_msg_truncated(wp);
+					p = wp->msg_rd.out;
+					buflen = wp->msg_rd.in - p;
+
+				} else if (*p == '\n') {
+					/* We have "\r\n"
+					 * If "\r\n" is followed by '.',
+					 * then process up to the '.' */
+					if (*++p == '.') {
+					    buflen = i+1;
+					    bol = 1;
+					    break;
+					}
+				}
+			}
+		}
+
+		/* Log the body block */
+		log_body_write(&wp->cw, wp->msg_rd.out, buflen);
+
+		if (wp->dfgs & DFG_MTA_BODY)
+			tmp_write(wp, wp->msg_rd.out, buflen);
+
+		dcc_ck_body(&wp->cw.cks, wp->msg_rd.out, buflen);
+		wp->msg_rd.out += buflen;
+	}
+	dcc_cks_fin(&wp->cw.cks);
+
+	LOG_CAPTION(wp, DCC_LOG_MSG_SEP);
+	thr_log_late(&wp->cw);
+
+	/* check the grey and white lists */
+	cmn_ask_white(&wp->cw);
+
+	/* report spam to the DCC server in the case without a recipient
+	 * for the ASCII protocol,
+	 * which normally causes a query instead of a report. */
+	if (!wp->cw.rcpt_st_first
+	    && ((wp->cw.ask_st & ASK_ST_MTA_ISSPAM)
+		|| (wp->cw.init_sws & FLTR_SW_TRAPS)
+		|| dcc_query_only
+		|| wp->cw.ask_st & ASK_ST_QUERY))
+		++wp->cw.tgts;
+
+	wp->cw.header.buf[0] = '\0';
+	wp->cw.header.used = 0;
+	if (wp->cw.tgts > wp->cw.white_tgts) {
+		/* Report to the DCC and add our header if allowed.
+		 * After serious errors, act as if DCC server said not-spam
+		 * but remove our X-DCC header */
+		i = cmn_ask_dcc(&wp->cw);
+		if (!i && try_extra_hard)
+			return 0;
+
+	} else {
+		/* The message is whitelisted or the MTA told us there are 0
+		 * recipients, so we cannot ask the DCC server.
+		 * If it was whitelisted, add X-DCC header saying so. */
+		if (wp->cw.tgts > 0)
+			xhdr_whitelist(&wp->cw.header);
+		/* Use the local target count to decide whether to log
+		 * the mail message */
+		dcc_honor_log_cnts(&wp->cw.ask_st, &wp->cw.cks, wp->cw.tgts);
+	}
+
+	totals.tgts += wp->cw.tgts;
+
+	return 1;
+}
+
+
+
+/* ensure there is another line, trim its terminal "\r\n",
+ *      and add a terminal '\0'
+ * This cannot be used with the proxy code because it often needs to
+ *	send the original buffer downstream. */
+static int				/* # of bytes available */
+ascii_read_line(WORK *wp)
+{
+	char *p;
+
+	if (!msg_read_line(wp, &wp->msg_rd)) {
+		thr_error_msg(&wp->cw, "truncated request");
+		job_exit(wp);
+	}
+
+	p = wp->msg_rd.next_line-1;
+	for (;;) {
+		*p-- = '\0';
+		if (p < wp->msg_rd.out)
+			return 0;
+		if (*p != '\r')
+			return p+1 - wp->msg_rd.out;
+	}
+}
+
+
+
+/* Look for a string from the MTA
+ *      while ignoring case and skipping whitespace
+ *      The next line must already be in the buffer */
+static u_char				/* 1=matched it */
+ascii_opt_str(WORK *wp, const char *st, int stlen)
+{
+	/* skip initial white space */
+	while (wp->msg_rd.out < wp->msg_rd.next_line
+	       && (*wp->msg_rd.out == '\t' || *wp->msg_rd.out == ' '))
+		++wp->msg_rd.out;
+
+	if (stlen <= wp->msg_rd.next_line - wp->msg_rd.out
+	    && !strncasecmp(wp->msg_rd.out, st, stlen)) {
+		if (wp->msg_rd.out[stlen] == '\0') {
+			wp->msg_rd.out += stlen;
+			return 1;
+		}
+
+		/* skip trailing whitespace */
+		if (wp->msg_rd.out[stlen] == '\t'
+		    || wp->msg_rd.out[stlen] == ' ') {
+			do {
+				++stlen;
+			} while (wp->msg_rd.out[stlen] == '\t'
+				 || wp->msg_rd.out[stlen] == ' ');
+			wp->msg_rd.out += stlen;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+
+static u_char				/* 0=bad recipient */
+check_addr(WORK *wp,
+	   const char **addrp, int *addr_lenp,
+	   const char **userp, int *user_lenp)
+{
+	USER_DOMAIN *udom;
+	const char *atchr, *addr, *cp;
+	int addr_len, i;
+
+	addr = *addrp;
+	addr_len = *addr_lenp;
+	if (addr_len > RCTP_MAXNAME-1)
+		addr_len = *addr_lenp = RCTP_MAXNAME-1;
+	if (*addr == '<' && addr_len >= 2 && addr[addr_len-1] == '>') {
+		++addr;
+		addr_len -= 2;
+		if (addr_len == 0) {
+			if (wp->dfgs & DFG_WORK_LOCK)
+				unlock_work();
+			thr_error_msg(&wp->cw, "null recipient address");
+			if (wp->dfgs & DFG_WORK_LOCK)
+				lock_work();
+			return 0;
+		}
+	}
+
+	/* strip source route from recipient */
+	while (addr_len != 0
+	       && (cp = memchr(addr, ',', addr_len)) != 0) {
+		++cp;
+		addr_len -= cp-addr;
+		addr = cp;
+		*addr_lenp = addr_len;
+		*addrp = addr;
+	}
+
+	if (addr_len >= RCTP_MAXNAME) {
+		if (wp->dfgs & DFG_WORK_LOCK)
+			unlock_work();
+		thr_error_msg(&wp->cw, "recipient \"%s\" is too long", addr);
+		if (wp->dfgs & DFG_WORK_LOCK)
+			lock_work();
+		return 0;
+	}
+	if (addr_len <= 0) {
+		if (wp->dfgs & DFG_WORK_LOCK)
+			unlock_work();
+		thr_error_msg(&wp->cw, "null recipient");
+		if (wp->dfgs & DFG_WORK_LOCK)
+			lock_work();
+		return 0;
+	}
+
+	if (*user_lenp) {
+		if (*user_lenp > RCTP_MAXNAME-1)
+			*user_lenp = RCTP_MAXNAME-1;
+	} else {
+		/* Use the list of local domain names to guess a user name
+		 * from the recipient address
+		 * Take the whole recipient name if it does not
+		 * include a domain name */
+		atchr = memchr(addr, '@', addr_len);
+		if (!atchr) {
+			*userp = addr;
+			*user_lenp = addr_len;
+			return 1;
+		}
+
+		for (udom = user_domains; udom; udom = udom->fwd) {
+			i = addr_len - udom->len;
+			if (i > 0 && !strncasecmp(addr+i, udom->nm,
+						  udom->len)) {
+				if (*udom->nm == '@'
+				    || *udom->nm == '.') {
+					;   /* got it */
+				} else {
+					/* match must start at '.' or '@'
+					 * if target did not start with
+					 * '.' or '@' then the name must
+					 * end with '.' or '@' */
+					if (i-- < 2
+					    || (addr[i] != '.'
+						&& addr[i] != '@'))
+					    continue;
+				}
+				/* we have something like user or user@sub
+				 * from user@sub.domain.com */
+				*userp = addr;
+				if (!udom->wildcard) {
+					*user_lenp = i;
+				} else {
+					*user_lenp = atchr - addr;
+				}
+				return 1;
+			}
+		}
+	}
+
+	return 1;
+}
+
+
+
+/* The mutex must be locked */
+static RCPT_ST *
+set_rcpt(WORK *wp,
+	 const char *rcpt, int rcpt_len,
+	 const char *user, int user_len)
+{
+	RCPT_ST *rcpt_st;
+
+	rcpt_st = alloc_rcpt_st(&wp->cw, 0);
+	if (!rcpt_st)
+		return 0;
+
+	++wp->cw.tgts;
+	memcpy(rcpt_st->env_to, rcpt, rcpt_len);
+	rcpt_st->env_to[rcpt_len] = '\0';
+	if (user_len)
+		memcpy(rcpt_st->user, user, user_len);
+	rcpt_st->user[user_len] = '\0';
+
+	rcpt_st->dir[0] = '\0';
+	if (user_len != 0
+	    && !get_user_dir(rcpt_st, user, user_len, 0, 0))
+		thr_trace_msg(&wp->cw, "%s", wp->cw.emsg);
+
+	return rcpt_st;
+}
+
+
+
+/* read lines of pairs if (env_To, username) values from the MTA */
+static void
+get_ascii_rcpts(WORK *wp)
+{
+	const char *addr, *user, *user_end;
+	char c;
+	int addr_len, user_len;
+	RCPT_ST *rcpt_st;
+
+	lock_work();
+	wp->dfgs |= DFG_WORK_LOCK;
+	for (;;) {
+		ascii_read_line(wp);
+
+		/* stop after the empty line */
+		addr = wp->msg_rd.out;
+		if (*addr == '\0') {
+			wp->msg_rd.out = wp->msg_rd.next_line;
+			break;
+		}
+
+		user_end = wp->msg_rd.next_line;
+		while (user_end > addr
+		       && ((c = *(user_end-1)) == ' ' || c == '\t'))
+			--user_end;
+
+		/* bytes up to '\r' are the env_To value
+		 * and bytes after are the local recipient */
+		user = strchr(addr, '\r');
+		if (!user) {
+			addr_len = user_end - wp->msg_rd.out;
+			user = user_end;
+		} else {
+			addr_len  = user - addr;
+			++user;
+		}
+
+		user_len = user_end - user;
+		if (!check_addr(wp, &addr, &addr_len, &user, &user_len))
+			job_exit(wp);
+		rcpt_st = set_rcpt(wp, addr, addr_len, user, user_len);
+		if (!rcpt_st)
+			job_exit(wp);
+
+		/* Don't worry if this recipient is incompatible with preceding
+		 * recipients, because there is nothing we can do.
+		 * We cannot reject a recipient when the ASCII protocol is used,
+		 * but side effects of the check are required */
+		cmn_compat_whitelist(&wp->cw, rcpt_st);
+
+		wp->msg_rd.out = wp->msg_rd.next_line;
+	}
+	unlock_work();
+	wp->dfgs &= ~DFG_WORK_LOCK;
+}
+
+
+
+/* We are finished with one SMTP message with the ASCII protocol
+ *      Send the result to the MTA and end this thread */
+static void NRATTRIB
+ascii_done(WORK *wp, DCCIF_RESULT_CHAR result_char)
+{
+	const char *result_str;
+	RCPT_ST *rcpt_st;
+	char *p;
+
+	result_str = wp->cw.reply.log_result;
+	if (!result_str)
+		thr_error_msg(&wp->cw, "rejection reason undecided");
+	else
+		thr_log_print(&wp->cw, 0, DCC_XHDR_RESULT"%s\n", result_str);
+
+	/* tell MTA the overall result */
+	p = wp->msg_rd.base;
+	*p++ = result_char;
+	*p++ = '\n';
+
+	/* output list of recipients */
+	for (rcpt_st = wp->cw.rcpt_st_first; rcpt_st; rcpt_st = rcpt_st->fwd) {
+		if (p >= &wp->msg_rd.base[wp->msg_rd.size-1]) {
+			buf_write(wp, &wp->msg_wt,
+				  wp->msg_rd.base, wp->msg_rd.size-1);
+			p = wp->msg_rd.base;
+		}
+		if (result_char == DCCIF_RESULT_GREY) {
+			*p++ = DCCIF_RCPT_GREY;
+		} else if (rcpt_st->fgs & RCPT_FG_ISSPAM) {
+			*p++ = DCCIF_RCPT_REJECT;
+		} else {
+			*p++ = DCCIF_RCPT_ACCEPT;
+		}
+	}
+	*p++ = '\n';
+	buf_write(wp, &wp->msg_wt, wp->msg_rd.base, p-wp->msg_rd.base);
+
+	/* send the body from the temporary file to the MTA */
+	if (wp->dfgs & DFG_MTA_BODY) {
+		body_copy(wp);
+
+	} else if (wp->dfgs & DFG_MTA_HEADER) {
+		/* MTA wants only the header, if we have it */
+		if (wp->cw.header.used != 0)
+			buf_write(wp, &wp->msg_wt, wp->cw.header.buf,
+				       wp->cw.header.used);
+		buf_write(wp, &wp->msg_wt, "\n", 1);
+	}
+
+	job_exit(wp);
+}
+
+
+
+/* use the dccifd ASCII protocol to talk to an MTA */
+static void NRATTRIB
+ascii_job(WORK *wp)
+{
+#	define AOPT(s) ascii_opt_str(wp, s, LITZ(s))
+	char *p;
+	int i;
+
+	log_start(&wp->cw);
+
+	wp->msg_rd.base = wp->buf1;
+	wp->msg_rd.size = sizeof(wp->buf1);
+	wp->msg_rd.socp = &wp->proxy_in_soc;
+
+	wp->msg_wt.base = wp->buf2;
+	wp->msg_wt.size = sizeof(wp->buf2);
+	wp->msg_wt.socp = &wp->proxy_in_soc;
+
+	/* get any options */
+	ascii_read_line(wp);
+	while (*wp->msg_rd.out != '\0') {
+		if (AOPT(DCCIF_OPT_LOG)) {
+			wp->cw.ask_st |= ASK_ST_LOGIT;
+			continue;
+		}
+		if (AOPT(DCCIF_OPT_SPAM)) {
+			wp->cw.ask_st |= ASK_ST_MTA_ISSPAM;
+			continue;
+		}
+		if (AOPT(DCCIF_OPT_BODY)) {
+			wp->dfgs |= DFG_MTA_BODY;
+			continue;
+		}
+		if (AOPT(DCCIF_OPT_HEADER)) {
+			wp->dfgs |= DFG_MTA_HEADER;
+			continue;
+		}
+		if (AOPT(DCCIF_OPT_QUERY)) {
+			wp->cw.ask_st |= ASK_ST_QUERY;
+			continue;
+		}
+		if (AOPT(DCCIF_OPT_GREY_QUERY)) {
+			wp->cw.ask_st |= ASK_ST_QUERY_GREY;
+			continue;
+		}
+		if (AOPT(DCCIF_OPT_NO_REJECT)) {
+			wp->cw.action = CMN_IGNORE;
+			continue;
+		}
+		if (AOPT(DCCIF_OPT_RCVD_NXT)
+		    || AOPT(DCCIF_OPT_RCVD_NEXT)) {
+			++wp->parse_rcvd;
+			continue;
+		}
+
+		thr_error_msg(&wp->cw, "unrecognized option value: \"%s\"",
+			      wp->msg_rd.out);
+		job_exit(wp);
+	}
+	if ((wp->cw.ask_st & ASK_ST_MTA_ISSPAM)
+	    && (wp->cw.ask_st & ASK_ST_QUERY)) {
+		thr_error_msg(&wp->cw, DCCIF_OPT_SPAM" and "DCCIF_OPT_QUERY
+			      " are incompatible");
+		wp->cw.ask_st &= ~ASK_ST_MTA_ISSPAM;
+	}
+	if ((wp->cw.ask_st & ASK_ST_MTA_ISSPAM)
+	    && (wp->cw.ask_st & ASK_ST_QUERY_GREY)) {
+		thr_error_msg(&wp->cw, DCCIF_OPT_SPAM" and "DCCIF_OPT_GREY_QUERY
+			      " are incompatible");
+		wp->cw.ask_st &= ~ASK_ST_MTA_ISSPAM;
+	}
+	if (dcc_query_only
+	    && (wp->cw.ask_st & ASK_ST_MTA_ISSPAM)) {
+		thr_error_msg(&wp->cw, DCCIF_OPT_SPAM" and -Q"
+			      " are incompatible");
+		wp->cw.ask_st &= ~ASK_ST_MTA_ISSPAM;
+	}
+	wp->msg_rd.out = wp->msg_rd.next_line;
+
+	/* open the connection to the nearest DCC server
+	 * and figure out our X-DCC header */
+	if (!ck_dcc_ctxt(&wp->cw)) {
+		/* failed to create context */
+		make_reply(&wp->cw.reply,  &dcc_fail_reply, &wp->cw, 0);
+		ascii_done(wp, DCCIF_RESULT_TEMP);
+	}
+
+	dcc_cks_init(&wp->cw.cks);
+	dcc_dnsbl_init(&wp->cw.cks, wp->cw.dcc_ctxt, &wp->cw, wp->cw.id);
+	wp->cw.cmn_fgs |= CMN_FG_LOG_EARLY;
+
+	/* get the SMTP client IP address and host name */
+	i = ascii_read_line(wp);
+	if (i == 0
+	    || !strcmp("0.0.0.0", wp->msg_rd.out)
+	    || !strcmp("0.0.0.0\r0.0.0.0", wp->msg_rd.out)) {
+		/* it is absent or the SpamAssassin junk */
+		wp->dfgs |= DFG_PARSE_RCVD; /* try a Received header */
+	} else {
+		/* the host name follows the IP address */
+		p = strchr(wp->msg_rd.out, '\r');
+		if (p) {
+			*p++ = '\0';
+			BUFCPY(wp->cw.clnt_name, p);
+		}
+		/* convert ASCCI representation of IP address to a
+		 * canonical form and to a checksum */
+		if (!dcc_get_str_ip_ck(&wp->cw.cks, wp->msg_rd.out)) {
+			thr_error_msg(&wp->cw, "unrecognized IP address \"%s\"",
+				      wp->msg_rd.out);
+		} else {
+			/* convert IPv6 address to a canonical string */
+			wp->cw.clnt_addr = wp->cw.cks.ip_addr;
+			dcc_ipv6tostr(wp->cw.clnt_str, sizeof(wp->cw.clnt_str),
+				      &wp->cw.clnt_addr);
+		}
+	}
+	wp->msg_rd.out = wp->msg_rd.next_line;
+
+	/* get the HELO value */
+	i = ascii_read_line(wp);
+	get_helo(wp, wp->msg_rd.out, i);
+	wp->msg_rd.out = wp->msg_rd.next_line;
+
+	/* get the envelope Mail_From value */
+	i = ascii_read_line(wp);
+	if (i > ISZ(wp->cw.env_from)-1)
+		i = ISZ(wp->cw.env_from)-1;
+	memcpy(wp->cw.env_from, wp->msg_rd.out, i);
+	wp->cw.env_from[i] = '\0';
+	wp->msg_rd.out = wp->msg_rd.next_line;
+
+	get_sender(wp);
+
+	/* get the list of recipients from the MTA */
+	get_ascii_rcpts(wp);
+
+	if (!get_body(wp)) {
+		/* something wrong while collecting the message body
+		 * such as contacting the DCC server */
+		ascii_done(wp, DCCIF_RESULT_TEMP);
+	}
+
+	/* get consensus of targets' wishes */
+	users_process(&wp->cw);
+	totals.tgts_rejected += wp->cw.reject_tgts;
+	/* log the consensus & generate SMTP rejection message if needed */
+	users_log_result(&wp->cw, 0);
+
+	if (wp->cw.ask_st & ASK_ST_GREY_EMBARGO) {
+		totals.tgts_embargoed += wp->cw.tgts;
+		ascii_done(wp, DCCIF_RESULT_GREY);
+	}
+
+	if (wp->cw.reject_tgts != 0
+	    || (wp->cw.tgts == 0 && (wp->cw.ask_st & ASK_ST_CLNT_ISSPAM))) {
+		if (wp->cw.action != CMN_IGNORE) {
+			if (!wp->cw.reply.log_result)
+				wp->cw.reply.log_result=DCC_XHDR_RESULT_REJECT;
+			ascii_done(wp, DCCIF_RESULT_REJECT);
+		} else {
+			wp->cw.reply.log_result = DCC_XHDR_RESULT_I_A;
+			ascii_done(wp, DCCIF_RESULT_OK);
+		}
+	}
+
+	wp->cw.reply.log_result = DCC_XHDR_RESULT_ACCEPT;
+	ascii_done(wp, DCCIF_RESULT_OK);
+
+#	undef AOPT
+}
+
+
+
+#define SMTP_REPLY_221		"221 dccifd closing connection"
+#define SMTP_REPLY_220		"220 dccifd proxy ready"
+#define SMTP_REPLY_250_DATA	"250 dccifd mail ok"
+#define SMTP_REPLY_250_RSET	"250 dccifd RSET ok"
+#define SMTP_REPLY_250_HELO	"250 dccifd HELO ok"
+#define SMTP_REPLY_250_POSTFIX	"250 dccifd Postfix extension ok"
+#define SMTP_REPLY_250_RCPT	"250 dccifd Recipient ok"
+#define SMTP_REPLY_250_MAIL	"250 dccifd Sender ok"
+#define SMTP_REPLY_350		"354 Enter mail to dccifd"
+#define SMTP_REPLY_452_WLIST	"452 4.5.3 "DCC_XHDR_INCOMPAT_WLIST
+#define SMTP_REPLY_452_2MANY	"452 4.5.3 "DCC_XHDR_TOO_MANY_RCPTS
+#define SMTP_REPLY_500		"500 5.0.0 dccifd command unrecognized"
+#define SMTP_REPLY_501_NO_ARG	"501 5.5.4 dccifd command arg required"
+#define SMTP_REPLY_501_BAD_ARG	"501 5.5.1 dccifd command unrecognized"
+#define SMTP_REPLY_501_RCPT	"501 5.5.2 dccifd RCPT command syntax error"
+#define SMTP_REPLY_501_POSTFIX	"501 5.5.2 dccifd Postfix extension syntax error"
+#define SMTP_REPLY_503_DATA	"503 5.0.0 dccifd need RCPT"
+#define SMTP_REPLY_503		"503 5.0.0 bad dccifd command sequence"
+
+#define SMTP_DEBUG_TRACE 3
+
+
+static void
+smtp_trace(WORK *wp, const char *type, const char *buf, int len)
+{
+	if (dcc_clnt_debug < SMTP_DEBUG_TRACE)
+		return;
+
+	log_start(&wp->cw);
+	if (len > 0 && buf[len-1] == '\n')
+		--len;
+	if (len > 0 && buf[len-1] == '\r')
+		--len;
+	thr_trace_msg(&wp->cw, "%s %s: %.*s", wp->cw.id, type, len, buf);
+}
+
+
+
+/* send an SMTP reply upstream to the SMTP client of our proxy */
+static void
+smtp_send_reply(WORK *wp, const char *reply, int len)
+{
+	if (dcc_clnt_debug >= SMTP_DEBUG_TRACE)
+		smtp_trace(wp, "SMTP response", reply, len);
+	buf_write(wp, &wp->reply_out, reply, len);
+	buf_write(wp, &wp->reply_out, "\r\n", 2);
+}
+
+#define SMTP_REPLY(r) smtp_send_reply(wp,SMTP_REPLY_##r,LITZ(SMTP_REPLY_##r))
+
+static void
+smtp_reply_error(WORK *wp, const char *reply, int len)
+{
+	smtp_send_reply(wp, reply, len);
+	wp->msg_rd.out = wp->msg_rd.next_line;
+	wp->smtp_state = SMTP_ST_ERROR;
+	if (dcc_clnt_debug)
+		wp->cw.ask_st |= ASK_ST_LOGIT;
+	/* depend on '\n' ending the SMTP message */
+	thr_log_print(&wp->cw, 1, "%s", reply);
+}
+
+#define SMTP_ERROR(r) smtp_reply_error(wp,SMTP_REPLY_##r,LITZ(SMTP_REPLY_##r))
+
+
+
+/* look for an SMTP verb */
+typedef enum {
+    SMTP_VERB_ERR,			/* bad command */
+    SMTP_VERB_UNREC,			/* unrecognized command */
+    SMTP_VERB_RSET,
+    SMTP_VERB_HELO,
+    SMTP_VERB_XCLIENT,			/* Postfix extension */
+    SMTP_VERB_XFORWARD,			/* Postfix extension */
+    SMTP_VERB_MAIL,			/* Mail From */
+    SMTP_VERB_RCPT,			/* Rcpt To */
+    SMTP_VERB_DATA,
+    SMTP_VERB_QUIT
+} VERB_SMTP;
+typedef struct {
+    const char	*str;
+    int		str_len;
+    const char	*parm;
+    int		parm_len;
+    VERB_SMTP	verb;
+    u_char	arg_required;
+} PT;
+PT pt[] = {
+#define PT_M(s,p,v,r) {s,LITZ(s),p,LITZ(p),SMTP_VERB_##v,r}
+    PT_M("HELO","", HELO, 1),
+    PT_M("EHLO","", HELO, 1),
+    PT_M("Mail","From:", MAIL, 1),
+    PT_M("Rcpt","To:", RCPT, 1),
+    PT_M("DATA","", DATA, 0),
+    PT_M("XFORWARD","", XFORWARD, 1),
+    PT_M("XCLIENT","", XCLIENT, 1),
+    PT_M("RSET","", RSET, 0),
+    PT_M("QUIT","", QUIT, 0),
+#undef PT_M
+};
+
+#define RESP_DIGIT(c) ((c) >= '0' && (c) <= '9')
+
+static VERB_SMTP
+get_smtp_verb(WORK *wp,
+	      const char **ppp,		/* parameter after command */
+	      const char **pep)		/* end of parameter */
+{
+	PT *ptp;
+	const char *lp, *pp;
+	int i, len;
+
+	/* skip leading whitespace */
+	wp->msg_rd.out += strspn(wp->msg_rd.out, " \t");
+	lp = wp->msg_rd.out;
+
+	if (dcc_clnt_debug >= SMTP_DEBUG_TRACE)
+		smtp_trace(wp, "SMTP command", lp, wp->msg_rd.next_line - lp);
+
+	for (ptp = pt; ptp < &pt[DIM(pt)]; ++ptp) {
+		len = ptp->str_len;
+		pp = lp+len;
+		if (pp+2 > wp->msg_rd.next_line
+		    || strncasecmp(lp, ptp->str, len))
+			continue;
+
+		if (pp+2 == wp->msg_rd.next_line) {
+			/* SMTP command by itself on the line */
+			*ppp = 0;
+			*pep = 0;
+			if (ptp->arg_required) {
+				SMTP_REPLY(501_NO_ARG);
+				return SMTP_VERB_ERR;
+			}
+			/* finished, having matched the command */
+			return ptp->verb;
+		}
+
+		/* all other commands require following text separated
+		 * from the command with whitespace, as in "Mail From:" */
+		i = strspn(pp, " \t");
+		if (i == 0)
+			continue;
+		pp += i;
+
+		/* skip the initial part of the parameter, such as "From:" */
+		if (ptp->parm_len) {
+			if (*pp == '\r') {
+				SMTP_REPLY(501_NO_ARG);
+				return SMTP_VERB_ERR;
+			}
+			if (strncasecmp(pp, ptp->parm, ptp->parm_len)) {
+				SMTP_REPLY(501_BAD_ARG);
+				return SMTP_VERB_ERR;
+			}
+			pp += ptp->parm_len;
+			pp += strspn(pp, " \t");
+		}
+
+		*ppp = pp;
+		*pep = strpbrk(pp, " \t\r");
+		return ptp->verb;
+	}
+
+	*ppp = 0;
+	return SMTP_VERB_UNREC;
+}
+
+
+
+/* reset and restart the SMTP proxy state */
+static void
+proxy_rset(WORK *wp)
+{
+	if (wp->smtp_state == SMTP_ST_START)
+		return;
+
+	cmn_clear(&wp->cw, wp, 0);
+	wp->dfgs &= DFG_RECYCLE;
+	wp->smtp_state = SMTP_ST_START;
+
+	dcc_cks_init(&wp->cw.cks);
+	dcc_dnsbl_init(&wp->cw.cks, wp->cw.dcc_ctxt, &wp->cw, wp->cw.id);
+	wp->cw.cmn_fgs |= CMN_FG_LOG_EARLY;
+
+	/* Values from the Postfix XCLIENT ESMTP extension endure for the
+	 * entire session.  Values from the XFORWARD extension or guesses from
+	 * a HELO command are cleared at the end of the SMTP transaction. */
+	if (!(wp->dfgs & DFG_XCLIENT_NAME))
+		wp->cw.clnt_name[0] = '\0';
+	if (wp->dfgs & DFG_XCLIENT_ADDR) {
+		dcc_get_ipv6_ck(&wp->cw.cks, &wp->cw.clnt_addr);
+	} else {
+		memset(&wp->cw.clnt_addr, 0, sizeof(wp->cw.clnt_addr));
+		wp->cw.clnt_str[0] = '\0';
+	}
+	if (!(wp->dfgs & DFG_XCLIENT_HELO))
+		wp->cw.helo[0] = '\0';
+}
+
+
+
+/* deal with aborted SMTP sessions */
+static void
+proxy_abort(WORK *wp, const char *log_msg)
+{
+	if (dcc_clnt_debug >= SMTP_DEBUG_TRACE)
+		smtp_trace(wp, "reset", log_msg, strlen(log_msg));
+
+	if (wp->smtp_state != SMTP_ST_START
+	    && wp->smtp_state != SMTP_ST_HELO
+	    && wp->smtp_state != SMTP_ST_ERROR) {
+
+		wp->cw.ask_st |= ASK_ST_INVALID_MSG;
+
+		if (!(wp->cw.cmn_fgs & CMN_FG_ENV_LOGGED))
+			thr_log_envelope(&wp->cw, 1);
+		dcc_cks_fin(&wp->cw.cks);
+		LOG_CAPTION(wp, "\n"DCC_LOG_MSG_SEP);
+		thr_log_late(&wp->cw);
+		cmn_ask_white(&wp->cw);
+
+		users_process(&wp->cw);
+		users_log_result(&wp->cw, log_msg);
+
+		/* create log files for -d
+		 * and without any recipents but with "option log-all" */
+		if (dcc_clnt_debug
+		    || (wp->cw.init_sws & FLTR_SW_LOG_ALL))
+			wp->cw.ask_st |= ASK_ST_LOGIT;
+
+		if (wp->cw.ask_st & ASK_ST_LOGIT)
+			thr_log_print(&wp->cw, 0,
+				      DCC_XHDR_RESULT"%s\n", log_msg);
+	}
+	proxy_rset(wp);
+}
+
+
+
+/* quit a truncated message */
+static void NRATTRIB
+proxy_msg_truncated(WORK *wp)
+{
+	if (!proxy) {
+		thr_error_msg(&wp->cw, "truncated message");
+	} else {
+		proxy_abort(wp, "SMTP session aborted");
+	}
+	job_exit(wp);
+}
+
+
+
+/* pass a line from the downstream proxy to our upstream */
+static void
+smtp_pass_line(WORK *wp)
+{
+	int len;
+
+	len = wp->reply_in.next_line - wp->reply_in.out;
+	if (dcc_clnt_debug >= SMTP_DEBUG_TRACE)
+		smtp_trace(wp, "pass SMTP response", wp->reply_in.out, len);
+
+	buf_write(wp, &wp->reply_out, wp->reply_in.out,
+		  wp->reply_in.next_line - wp->reply_in.out);
+
+	wp->reply_in.out = wp->reply_in.next_line;
+}
+
+
+
+static int				/* -1 or 1st digit of response */
+smtp_get_resp(WORK *wp,
+	      u_char pass,		/* 1=pass it upstream */
+	      char *logbuf,		/* log response here */
+	      int logbuflen)
+{
+	int len;
+	char dig, cont;
+
+	for (;;) {
+		if (!msg_read_line(wp, &wp->reply_in)) {
+			if (pass)
+				job_exit(wp);
+			return -1;
+		}
+		len = wp->reply_in.next_line - wp->reply_in.out;
+		if (len < 5) {
+			thr_error_msg(&wp->cw, "short SMTP response"
+				      " \"%s\" from proxy server",
+				      wp->reply_in.out);
+			return -1;
+		}
+		dig = wp->reply_in.out[0];
+		cont = wp->reply_in.out[3];
+		if ((dig < '1' || dig > '5')
+		    || !RESP_DIGIT(wp->reply_in.out[1])
+		    || !RESP_DIGIT(wp->reply_in.out[2])
+		    || (cont != ' ' && cont != '-')) {
+			*wp->reply_in.next_line = '\0';
+			thr_error_msg(&wp->cw, "unrecognized SMTP response"
+				      " \"%s\" from proxy",
+				      wp->reply_in.out);
+			return -1;
+		}
+
+		if (logbuflen != 0) {
+			if (logbuflen > len)
+				logbuflen = len;
+			memcpy(logbuf, wp->reply_in.out, logbuflen);
+			while (logbuflen > 0
+			       && (logbuf[logbuflen-1] == '\r'
+				   || logbuf[logbuflen-1] == '\n'))
+				--logbuflen;
+			logbuf[logbuflen] = '\0';
+
+			/* log only the first line */
+			logbuflen = 0;
+		}
+
+		if (pass) {
+			/* pass the next line upstream */
+			smtp_pass_line(wp);
+			if (cont == ' ')
+				return dig;
+		} else {
+			/* we don't like multi-line responses */
+			if (cont == ' ')
+				return dig;
+
+			*wp->reply_in.next_line = '\0';
+			thr_error_msg(&wp->cw, "unacceptable multi-line"
+				      " SMTP response"
+				      " \"%s\" from proxy",
+				      wp->reply_in.out);
+			return -1;
+		}
+	}
+}
+
+
+
+/* pass an SMTP command from upstream or the SMTP client of our proxy
+ *	downstream to the SMTP server of our proxy.
+ *	Then relay the SMTP server's response upstream to the SMTP client */
+static u_char				/* 0=server was unhappy */
+smtp_pass_cmd(WORK *wp,
+	      const char *reply,	/* send this response upstream if */
+	      int reply_len,		/*	if downstream is happy */
+	      char *logbuf,		/* logbuf response here */
+	      int logbuflen)
+{
+	int len, result;
+
+	/* fake it if the SMTP server is /dev/null */
+	if (proxy_out_family == AF_UNSPEC) {
+		smtp_send_reply(wp, reply, reply_len);
+		return 1;
+	}
+
+	len = wp->msg_rd.next_line - wp->msg_rd.out;
+	if (len != 0) {
+		buf_write(wp, &wp->msg_wt, wp->msg_rd.out, len);
+		buf_write_flush(wp, &wp->msg_wt);
+	}
+
+	/* wait for & pass on response */
+	result = smtp_get_resp(wp, 1, logbuf, logbuflen);
+	if (result < 0)
+		job_exit(wp);
+	return (result == reply[0]);
+}
+
+#define SMTP_PASS_CMD(r) smtp_pass_cmd(wp,SMTP_REPLY_##r,LITZ(SMTP_REPLY_##r), \
+				       0, 0)
+
+
+
+/* parse Postfix XFORWARD and XCLIENT commands
+ * see http://www.postfix.org/XCLIENT_README.html
+ * and http://www.postfix.org/XFORWARD_README.html
+ *	Depend on the Postfix downstream to answer EHLO with XFORWRD */
+static void
+smtp_xpostfix(WORK *wp,
+	      VERB_SMTP verb,		/* SMTP_VERB_XFORWARD or _XCLIENT */
+	      const char *parm)
+{
+	const char *val, *end_parm;
+	char addr[INET6_ADDRSTRLEN+1];
+	int i;
+
+	wp->smtp_state = SMTP_ST_HELO;
+
+	for (; ; parm = end_parm + strspn(end_parm, " \t")) {
+		if (*parm == '\r' || *parm == '\n') {
+			SMTP_PASS_CMD(250_POSTFIX);
+			break;
+		}
+
+		/* find the value */
+		val = strpbrk(parm, "= \t\r\n");
+		if (*val++ != '=') {
+			SMTP_REPLY(501_POSTFIX);
+			break;
+		}
+
+		end_parm = strpbrk(val, " \t\r\n");
+
+		if (!CLITCMP(parm, "NAME=")) {
+			if (!CLITCMP(val, "[UNAVAILABLE]")
+			    || !CLITCMP(val, "[TEMPUNAVAIL]")) {
+				wp->cw.clnt_name[0] = '\0';
+				continue;
+			}
+			i = min(ISZ(wp->cw.clnt_name)-1, end_parm-val);
+			memcpy(wp->cw.clnt_name, val, i);
+			wp->cw.clnt_name[i] = '\0';
+			if (verb == SMTP_VERB_XCLIENT)
+				wp->dfgs |= DFG_XCLIENT_NAME;
+			else
+				wp->dfgs &= ~DFG_XCLIENT_NAME;
+			continue;
+		}
+
+		if (!CLITCMP(parm, "ADDR=")) {
+			if (!CLITCMP(val, "[UNAVAILABLE]")
+			    || !CLITCMP(val, "[TEMPUNAVAIL]")) {
+				wp->cw.clnt_str[0] = '\0';
+				continue;
+			}
+			if (!CLITCMP(val, "IPV6"))
+				val += LITZ("IPV6");
+			i = min(ISZ(addr)-1, end_parm-val);
+			memcpy(addr, val, i);
+			addr[i] = '\0';
+			/* try to convert ASCCI representation of IP address to
+			 * a canonical form and to a checksum */
+			if (!dcc_get_str_ip_ck(&wp->cw.cks, addr)) {
+				thr_error_msg(&wp->cw,
+					      "unrecognized IP address \"%s\"",
+					      addr);
+				wp->cw.clnt_str[0] = '\0';
+				continue;
+			}
+			wp->cw.clnt_addr = wp->cw.cks.ip_addr;
+			dcc_ipv6tostr(wp->cw.clnt_str, sizeof(wp->cw.clnt_str),
+				      &wp->cw.clnt_addr);
+			if (verb == SMTP_VERB_XCLIENT) {
+				wp->dfgs |= DFG_XCLIENT_ADDR;
+			} else {
+				wp->dfgs &= ~DFG_XCLIENT_ADDR;
+			}
+			continue;
+		}
+
+		if (!CLITCMP(parm, "HELO=")) {
+			if (!CLITCMP(val, "[UNAVAILABLE]")
+			    || !CLITCMP(val, "[TEMPUNAVAIL]")) {
+				wp->cw.helo[0] = '\0';
+			} else {
+				get_helo(wp, val, end_parm-val);
+			}
+			if (verb == SMTP_VERB_XCLIENT)
+				wp->dfgs |= DFG_XCLIENT_HELO;
+			else
+				wp->dfgs &= ~DFG_XCLIENT_HELO;
+			continue;
+		}
+	}
+
+	wp->msg_rd.out = wp->msg_rd.next_line;
+}
+
+
+
+/* parse SMTP Rcpt_To command */
+static u_char				/* 1=now seen >=1 good Rcpt command*/
+get_smtp_rcpt(WORK *wp,
+	      const char *path,
+	      const char *epath)
+{
+	const char *user;
+	int path_len, user_len;
+	RCPT_ST *rcpt_st;
+
+	path_len = epath - path;
+	user = "";
+	user_len = 0;
+	if (!check_addr(wp, &path, &path_len, &user, &user_len)) {
+		SMTP_REPLY(501_RCPT);
+		wp->msg_rd.out = wp->msg_rd.next_line;
+		return 0;
+	}
+
+	lock_work();
+	wp->dfgs |= DFG_WORK_LOCK;
+	rcpt_st = set_rcpt(wp, path, path_len, user, user_len);
+	unlock_work();
+	wp->dfgs &= ~DFG_WORK_LOCK;
+	if (!rcpt_st) {
+		SMTP_REPLY(452_2MANY);
+		wp->msg_rd.out = wp->msg_rd.next_line;
+		return 0;
+	}
+
+	/* if this recipient is incompatible with preceding recipients
+	 * then reject it and remember to put something into the log */
+	if (!cmn_compat_whitelist(&wp->cw, rcpt_st)) {
+		--wp->cw.tgts;
+		SMTP_REPLY(452_WLIST);
+		wp->msg_rd.out = wp->msg_rd.next_line;
+		BUFCPY(rcpt_st->rej_msg, SMTP_REPLY_452_WLIST);
+		rcpt_st->rej_result = DCC_XHDR_INCOMPAT_WLIST;
+		rcpt_st->fgs |= RCPT_FG_REJ_FILTER;
+		return 0;
+	}
+
+	/* Try to pass the command in the buffer downstream.
+	 * Forget this recipient if it is not good enough downstream.
+	 * Let the downstream proxy do its own logging.
+	 *	That way we need save only one bit instead of an SMTP
+	 *	rejection message for our eventual per-user log file. */
+	if (!smtp_pass_cmd(wp, SMTP_REPLY_250_RCPT, LITZ(SMTP_REPLY_250_RCPT),
+			   rcpt_st->rej_msg, sizeof(rcpt_st->rej_msg))) {
+		--wp->cw.tgts;
+		wp->msg_rd.out = wp->msg_rd.next_line;
+		rcpt_st->rej_result = rcpt_st->rej_msg;
+		rcpt_st->rej_result += strspn(rcpt_st->rej_result,
+					      "0123456789. ");
+		if (rcpt_st->rej_msg[0] != '4')
+			++wp->cw.mta_rej_tgts;
+		wp->cw.ask_st |= ASK_ST_LOGIT;
+		rcpt_st->fgs |= RCPT_FG_REJ_FILTER;
+		return 0;
+	}
+
+	/* there was no problem if the downstream was happy */
+	rcpt_st->rej_msg[0] = '\0';
+
+	wp->msg_rd.out = wp->msg_rd.next_line;
+	return 1;
+}
+
+
+
+static void
+smtp_data_abort(WORK *wp)
+{
+	/* After a rejection Postfix wants a before-queue proxy to
+	 * "abort the connnection"  to the SMTP server downstream.
+	 * That might mean a simple TCP shutdown, but for testing with
+	 * sendmail, send an SMTP RSET command. */
+	if (proxy_out_family != AF_UNSPEC) {
+		buf_write(wp, &wp->msg_wt, "RSET\r\n", LITZ("RSET\r\n"));
+		buf_write_flush(wp, &wp->msg_wt);
+		/* sendmail will respond, but what about Postfix? */
+		if (smtp_get_resp(wp, 0, 0, 0) >= 0
+		    && dcc_clnt_debug >= SMTP_DEBUG_TRACE) {
+			smtp_trace(wp, "ignore response to RSET",
+				   wp->reply_in.out,
+				   wp->reply_in.next_line
+				   - wp->reply_in.out);
+			wp->reply_in.out = wp->reply_in.next_line;
+		}
+	}
+}
+
+
+
+/* We are rejecting the transaction with our generated reply */
+static void
+smtp_trans_reject(WORK *wp)
+{
+	if (dcc_clnt_debug >= SMTP_DEBUG_TRACE)
+		thr_trace_msg(&wp->cw, "%s SMTP response: %s %s %s",
+			      wp->cw.id,
+			      wp->cw.reply.rcode, wp->cw.reply.xcode,
+			      wp->cw.reply.str);
+
+	if (wp->proxy_in_soc >= 0) {
+		buf_write(wp, &wp->reply_out,
+			  wp->cw.reply.rcode, strlen(wp->cw.reply.rcode));
+		buf_write(wp, &wp->reply_out, " ", 1);
+		buf_write(wp, &wp->reply_out,
+			  wp->cw.reply.xcode, strlen(wp->cw.reply.xcode));
+		buf_write(wp, &wp->reply_out, " ", 1);
+		buf_write(wp, &wp->reply_out,
+			  wp->cw.reply.str, strlen(wp->cw.reply.str));
+		buf_write(wp, &wp->reply_out, "\r\n", 2);
+		buf_write_flush(wp, &wp->reply_out);
+		log_smtp_reply(&wp->cw);
+	}
+
+	if (wp->proxy_out_soc >= 0)
+		smtp_data_abort(wp);
+}
+
+
+
+static void NRATTRIB
+smtp_temp_fail(WORK *wp)
+{
+	make_reply(&wp->cw.reply, &dcc_fail_reply, &wp->cw, 0);
+	smtp_trans_reject(wp);
+	job_exit(wp);
+}
+
+
+
+/* Send the mail message to the SMTP server for our proxy. */
+static const char *
+smtp_data_send(WORK *wp)
+{
+	int class;
+	int len;
+	const char *result;
+
+	/* First send the DATA command. */
+	buf_write(wp, &wp->msg_wt, "DATA\r\n", LITZ("DATA\r\n"));
+	buf_write_flush(wp, &wp->msg_wt);
+
+	/* the server should respond with a 350 result */
+	class = smtp_get_resp(wp, 0, 0, 0);
+	if (class < 0)
+		job_exit(wp);
+
+	if (class == '3') {
+		wp->reply_in.out = wp->reply_in.next_line;
+
+		/* send the mail message to the SMTP server */
+		body_copy(wp);
+		buf_write(wp, &wp->msg_wt, ".\r\n", 3);
+		buf_write_flush(wp, &wp->msg_wt);
+
+		/* see what it has to say */
+		class = smtp_get_resp(wp, 0, 0, 0);
+
+	} else if (class != '4' && class != '5') {
+		/* or quit if response to DATA was not 4yz or 5yz */
+		thr_error_msg(&wp->cw, "unrecognized SMTP response"
+			      " \"%s\" from proxy to DATA command",
+			      wp->reply_in.out);
+		job_exit(wp);
+	}
+
+	if (class == '2') {
+		result = DCC_XHDR_RESULT_ACCEPT;
+	} else {
+		len = wp->reply_in.next_line - wp->reply_in.out;
+		while (len > 0 && (wp->reply_in.out[len-1] == '\r'
+				   || wp->reply_in.out[len-1] == '\n'))
+			--len;
+		if (len > ISZ(wp->cw.reply.str)-2)
+			len = ISZ(wp->cw.reply.str)-2;
+		memcpy(wp->cw.reply.str_buf, wp->reply_in.out, len);
+		wp->cw.reply.str_buf[len++] = '\n';
+		wp->cw.reply.str_buf[len] = '\0';
+		wp->cw.reply.str = wp->cw.reply.str_buf;
+		thr_log_print(&wp->cw, 0,
+			      "SMTP server "DCC_XHDR_REJ_DATA_MSG"%.*s",
+			      len, wp->cw.reply.str);
+		if (dcc_clnt_debug)
+			wp->cw.ask_st |= ASK_ST_LOGIT;
+		result = "rejected by SMTP server";
+	}
+
+	/* pass SMTP server's response to the SMTP client */
+	smtp_pass_line(wp);
+
+	return result;
+}
+
+
+
+static void
+smtp_data(WORK *wp)
+{
+	const char *global_result, *user_result;
+
+	if (!wp->cw.num_rcpts) {
+		/* never got SMTP DATA command */
+		SMTP_ERROR(503_DATA);
+		return;
+	}
+
+	/* tell STMP client to send the mail message */
+	wp->msg_rd.out = wp->msg_rd.next_line;
+	SMTP_REPLY(350);
+	buf_write_flush(wp, &wp->reply_out);
+
+	if (!get_body(wp)) {
+		/* something went wrong while collecting the message body
+		 * such as contacting the DCC server */
+		smtp_temp_fail(wp);
+		return;
+	}
+
+	/* get consensus of targets' wishes */
+	users_process(&wp->cw);
+
+	if (wp->cw.ask_st & ASK_ST_GREY_EMBARGO) {
+		totals.tgts_embargoed += wp->cw.tgts;
+		smtp_trans_reject(wp);
+		global_result = wp->cw.reply.log_result;
+		user_result = global_result;
+
+	} else if (wp->cw.reject_tgts != 0) {
+		switch (wp->cw.action) {
+		case CMN_IGNORE:
+			totals.tgts_ignored += wp->cw.reject_tgts;
+			++totals.msgs_spam;
+			if (proxy_out_family == AF_UNSPEC) {
+				SMTP_REPLY(250_DATA);
+				global_result = DCC_XHDR_RESULT_I_A;
+			} else {
+				global_result = smtp_data_send(wp);
+			}
+			user_result = global_result;
+			break;
+
+		case CMN_DISCARD:
+			totals.tgts_discarded += wp->cw.reject_tgts;
+			++totals.msgs_spam;
+			/* discard the message by aborting the downstream
+			 * SMTP session and telling the upstream 250-OK */
+			if (proxy_out_family != AF_UNSPEC)
+				smtp_data_abort(wp);
+			SMTP_REPLY(250_DATA);
+			global_result = DCC_XHDR_RESULT_DISCARD;
+			user_result = global_result;
+			break;
+
+		case CMN_REJECT:
+		default:
+			totals.tgts_rejected += wp->cw.reject_tgts;
+			++totals.msgs_spam;
+			smtp_trans_reject(wp);
+			global_result = wp->cw.reply.log_result;
+			user_result = 0;
+			break;
+		}
+
+	} else if (proxy_out_family == AF_UNSPEC) {
+		SMTP_REPLY(250_DATA);
+		global_result = DCC_XHDR_RESULT_ACCEPT;
+		user_result = 0;
+
+	} else {
+		global_result = smtp_data_send(wp);
+		user_result = 0;
+	}
+
+	/* log the consensus & generate SMTP rejection message if needed */
+	users_log_result(&wp->cw, user_result);
+	thr_log_print(&wp->cw, 0, DCC_XHDR_RESULT"%s\n", global_result);
+
+	proxy_rset(wp);
+}
+
+
+
+/* use a subset of SMTP to talk to an MTA */
+static void NRATTRIB
+proxy_job(WORK *wp)
+{
+	char str[DCC_SU2STR_SIZE];
+	DCC_SOCKU su;
+	int out_soc;
+	VERB_SMTP verb;
+	const char *parmp, *eparmp;
+	int i;
+
+	wp->msg_rd.base = wp->buf1;
+	wp->msg_rd.size = sizeof(wp->buf1);
+	wp->msg_rd.socp = &wp->proxy_in_soc;
+
+	wp->msg_wt.base = wp->buf2;
+	wp->msg_wt.size = sizeof(wp->buf2);
+	wp->msg_wt.socp = &wp->proxy_out_soc;
+
+	wp->reply_in.base = wp->buf3;
+	wp->reply_in.size = sizeof(wp->buf3);
+	wp->reply_in.socp = &wp->proxy_out_soc;
+
+	wp->reply_out.base = wp->buf4;
+	wp->reply_out.size = sizeof(wp->buf4);
+	wp->reply_out.socp = &wp->proxy_in_soc;
+
+	if (proxy_out_family == AF_UNSPEC) {
+		/* single-ended */
+		;
+
+	} else if (proxy_out_family == AF_UNIX) {
+		out_soc = socket(AF_UNIX, SOCK_STREAM, 0);
+		if (out_soc < 0) {
+			thr_error_msg(&wp->cw, "proxy_out socket(AF_UNIX): %s",
+				      ERROR_STR());
+			smtp_temp_fail(wp);
+		}
+		if (0 > connect(out_soc,
+				(struct sockaddr *)&proxy_out_sun,
+				sizeof(listen_sun))) {
+			thr_error_msg(&wp->cw,
+				      "proxy_out connect(AF_UNIX,%s): %s",
+				      proxy_out_sun.sun_path,
+				      ERROR_STR());
+			close(out_soc);
+			smtp_temp_fail(wp);
+		}
+		if (!set_soc(wp->cw.emsg, out_soc, AF_UNIX,
+			     "proxy output")) {
+			thr_error_msg(&wp->cw, "%s", wp->cw.emsg);
+			close(out_soc);
+			smtp_temp_fail(wp);
+		}
+		wp->proxy_out_soc = out_soc;
+		wp->dfgs |= DFG_MTA_BODY;
+
+	} else {
+		if (proxy_out_su.sa.sa_family == AF_UNSPEC) {
+			/* use SMTP client's IP address */
+			if (wp->proxy_in_su.sa.sa_family == AF_INET)
+				dcc_mk_su(&su, AF_INET,
+					  &wp->proxy_in_su.ipv4.sin_addr,
+					  proxy_out_port);
+			else
+				dcc_mk_su(&su, AF_INET6,
+					  &wp->proxy_in_su.ipv6.sin6_addr,
+					  proxy_out_port);
+		} else {
+			su = proxy_out_su;
+		}
+		out_soc = socket(su.sa.sa_family, SOCK_STREAM, 0);
+		if (out_soc < 0) {
+			thr_error_msg(&wp->cw, "proxy_out socket(%s): %s",
+				      dcc_su2str(str, sizeof(str), &su),
+				      ERROR_STR());
+			close(out_soc);
+			smtp_temp_fail(wp);
+		}
+		if (0 > connect(out_soc, &su.sa, DCC_SU_LEN(&su))) {
+			thr_error_msg(&wp->cw,
+				      "proxy_out connect(%s): %s",
+				      dcc_su2str(str, sizeof(str), &su),
+				      ERROR_STR());
+			close(out_soc);
+			smtp_temp_fail(wp);
+		}
+		if (!set_soc(wp->cw.emsg, out_soc, su.sa.sa_family,
+			     "proxy output")) {
+			thr_error_msg(&wp->cw, "%s", wp->cw.emsg);
+			close(out_soc);
+			smtp_temp_fail(wp);
+		}
+		wp->proxy_out_soc = out_soc;
+		wp->dfgs |= DFG_MTA_BODY;
+	}
+
+	/* open the connection to the nearest DCC server
+	 * and figure out our X-DCC header */
+	if (!ck_dcc_ctxt(&wp->cw)) {
+		/* failed to create context */
+		smtp_temp_fail(wp);
+	}
+	dcc_cks_init(&wp->cw.cks);
+	dcc_dnsbl_init(&wp->cw.cks, wp->cw.dcc_ctxt, &wp->cw, wp->cw.id);
+	wp->cw.cmn_fgs |= CMN_FG_LOG_EARLY;
+
+	/* pass the banner upstream */
+	wp->smtp_state = SMTP_ST_START;
+	SMTP_PASS_CMD(220);
+
+	/* assume for now that the IP address is the SMTP client to our proxy */
+	if (wp->proxy_in_su.sa.sa_family != AF_UNIX) {
+		if (wp->proxy_in_su.sa.sa_family == AF_INET) {
+			dcc_ipv4toipv6(&wp->cw.clnt_addr,
+				       wp->proxy_in_su.ipv4.sin_addr);
+		} else {
+			wp->cw.clnt_addr = (wp->proxy_in_su.ipv6.sin6_addr);
+		}
+		dcc_ipv6tostr(wp->cw.clnt_str, sizeof(wp->cw.clnt_str),
+			      &wp->cw.clnt_addr);
+	}
+
+	for (;;) {
+		buf_write_flush(wp, &wp->reply_out);
+		if (!msg_read_line(wp, &wp->msg_rd)) {
+			proxy_abort(wp, "SMTP session aborted");
+			job_exit(wp);
+		}
+		verb = get_smtp_verb(wp, &parmp, &eparmp);
+
+		/* deal with common SMTP commands */
+		switch (verb) {
+		case SMTP_VERB_ERR:
+			wp->msg_rd.out = wp->msg_rd.next_line;
+			continue;
+		case SMTP_VERB_UNREC:
+			/* pass anything we don't recognize to the SMTP server
+			 * for our proxy.
+			 * If we don't have a server, reject it */
+			SMTP_PASS_CMD(500);
+			wp->msg_rd.out = wp->msg_rd.next_line;
+			continue;
+		case SMTP_VERB_QUIT:
+			SMTP_PASS_CMD(221);
+			proxy_abort(wp, "SMTP session aborted with QUIT");
+			job_exit(wp);
+		case SMTP_VERB_RSET:
+			SMTP_PASS_CMD(250_RSET);
+			proxy_abort(wp, "SMTP transaction aborted with RSET");
+			wp->msg_rd.out = wp->msg_rd.next_line;
+			continue;
+		case SMTP_VERB_HELO:
+			proxy_abort(wp, "SMTP transaction aborted with HELO");
+			wp->smtp_state = SMTP_ST_HELO;
+			/* save helo value in case there is no XPOSTFIX */
+			if (SMTP_PASS_CMD(250_HELO)
+			    && wp->cw.helo[0] == '\0'
+			    && !(wp->dfgs & DFG_XCLIENT_HELO))
+				get_helo(wp, parmp, eparmp - parmp);
+			wp->msg_rd.out = wp->msg_rd.next_line;
+			continue;
+		case SMTP_VERB_XCLIENT:
+		case SMTP_VERB_XFORWARD:
+		case SMTP_VERB_MAIL:
+		case SMTP_VERB_RCPT:
+		case SMTP_VERB_DATA:
+			break;
+		}
+
+		switch (wp->smtp_state) {
+		case SMTP_ST_START:	/* expecting HELO or perhaps MAIL */
+		case SMTP_ST_HELO:	/* seen HELO */
+			if (verb == SMTP_VERB_XCLIENT
+			    || verb == SMTP_VERB_XFORWARD) {
+				smtp_xpostfix(wp, verb, parmp);
+				continue;
+			}
+			if (verb == SMTP_VERB_MAIL) {
+				/* This might be a second message for the
+				 * connection.
+				 * Get sender, start logging, etc. at
+				 * the Mail_From command. */
+				if (SMTP_PASS_CMD(250_MAIL)) {
+					log_start(&wp->cw);
+					i = eparmp - parmp;
+					if (i > ISZ(wp->cw.env_from)-1)
+					    i = ISZ(wp->cw.env_from)-1;
+					memcpy(wp->cw.env_from, parmp, i);
+					wp->cw.env_from[i] = '\0';
+					wp->smtp_state = SMTP_ST_TRANS;
+				}
+				wp->msg_rd.out = wp->msg_rd.next_line;
+				get_sender(wp);
+				continue;
+			}
+			break;
+
+		case SMTP_ST_TRANS:	/* seen Mail_From */
+			if (verb ==  SMTP_VERB_RCPT) {
+				if (get_smtp_rcpt(wp, parmp, eparmp))
+					wp->smtp_state = SMTP_ST_RCPT;
+				continue;
+			}
+			break;
+
+		case SMTP_ST_RCPT:	/* seen Rctp_To */
+			if (verb == SMTP_VERB_RCPT) {
+				get_smtp_rcpt(wp, parmp, eparmp);
+				continue;
+			}
+			if (verb == SMTP_VERB_DATA) {
+				smtp_data(wp);
+				continue;
+			}
+			break;
+
+		case SMTP_ST_ERROR:	/* no transaction until RSET or HELO */
+			break;
+		}
+
+		/* reject anything out of sequence */
+		SMTP_ERROR(503);
+	}
+}
+
+
+
+/* start a new connection to an MTA */
+static void * NRATTRIB
+job_start(void *wp0)
+{
+	WORK *wp = wp0;
+
+	/* working threads do not deal with signals */
+	clnt_sigs_off(0);
+
+	if (proxy)
+		proxy_job(wp);
+	else
+		ascii_job(wp);
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,55 @@
+Makefile.in
+aop.c
+ask.c
+ck.c
+ck2str.c
+ckbody.c
+ckfuz1.c
+ckfuz2.c
+ckfuz2_tbl.h
+ckmime.c
+ckparse.c
+ckwhite.c
+clnt_init.c
+clnt_send.c
+clnt_unthreaded.c
+daemon.c
+dccif.c
+dnsbl.c
+error_msg.c
+fnm2path.c
+get_id.c
+get_port.c
+get_secs.c
+getifaddrs.c
+getopt.c
+heap_debug.c
+helper.c
+hstrerror.c
+id2str.c
+inet_ntop.c
+ipv6_conv.c
+load_ids.c
+lock_open.c
+logbad.c
+md5.c
+mkstemp.c
+op2str.c
+parse_log_opt.c
+parse_srvr_nm.c
+parse_whitefile.c
+parse_word.c
+print_info.c
+restart.c
+sign.c
+str2cnt.c
+str2type.c
+strlcat.c
+su2str.c
+tgts2str.c
+type2str.c
+vsyslog.c
+win32.c
+win32.mak
+xhdr.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 dcclib/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,62 @@
+# make DCC common code
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.39 $Revision$
+# @configure_input@
+
+DEPTH	=..
+LIB	=dcc
+SRCS	=aop.c ask.c ck.c ck2str.c ckbody.c ckfuz1.c ckfuz2.c ckmime.c	\
+	ckparse.c ckwhite.c clnt_init.c clnt_send.c clnt_unthreaded.c	\
+	ipv6_conv.c daemon.c dccif.c dnsbl.c error_msg.c		\
+	fnm2path.c get_id.c get_port.c get_secs.c getifaddrs.c		\
+	heap_debug.c helper.c hstrerror.c id2str.c inet_ntop.c		\
+	load_ids.c lock_open.c logbad.c md5.c mkstemp.c op2str.c	\
+	parse_log_opt.c parse_srvr_nm.c parse_whitefile.c parse_word.c	\
+	print_info.c sign.c restart.c str2type.c str2cnt.c strlcat.c	\
+	su2str.c tgts2str.c type2str.c vsyslog.c xhdr.c
+
+install:
+	@:
+
+deinstall:
+	@:
+
+# This would be nice, but it is incompatible with the bogus use of :: in
+#   old versions of FreeBSD bsd.lib.mk
+# lib$(LIB).a:Makefile
+
+@MAKE_LIB@
diff -r 000000000000 -r c7f6b056b673 dcclib/aop.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/aop.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,201 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * ask for and administrative operation
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.39 $Revision$
+ */
+
+#include "dcc_clnt.h"
+
+
+/* ask for an administrative operation */
+DCC_OPS					/* DCC_OP_INVALID=failed, else result */
+dcc_aop(DCC_EMSG emsg,			/* result if DCC_OP_ERROR or _INVALID */
+	DCC_CLNT_CTXT *ctxt,
+	DCC_CLNT_FGS clnt_fgs,		/* DCC_CLNT_FG_* */
+	SRVR_INX anum,			/* server index or NO_SRVR */
+	time_t dsecs,			/* fudge timestamp by this */
+	DCC_AOPS aop,
+	u_int32_t val1, u_char val2, u_char val3,
+	u_char val4, u_char *val5, u_int val5_len,
+	DCC_OP_RESP *resp,
+	DCC_SOCKU *resp_su)		/* IP address of server used */
+{
+	DCC_ADMN_REQ req;
+	DCC_EMSG loc_emsg;
+	DCC_OP_RESP resp0;
+	char resp_buf[DCC_OPBUF];
+
+	memset(&req, 0, sizeof(req));
+	req.date = htonl(time(0) + dsecs);
+	req.aop = aop;
+	req.val1 = ntohl(val1);
+	req.val2 = val2;
+	req.val3 = val3;
+	req.val4 = val4;
+	if (val5_len != 0) {
+		if (val5_len > MAX_DCC_ADMN_REQ_VAL5)
+			dcc_logbad(EX_SOFTWARE, "bogus aop val5 length %d",
+				   val5_len);
+		memcpy(req.val5, val5, val5_len);
+	}
+	if (!resp) {
+		resp = &resp0;
+	} else if (clnt_fgs & DCC_CLNT_FG_RETRANS) {
+		req.hdr.op_nums.r = resp->hdr.op_nums.r;
+	}
+	memset(resp, 0, sizeof(*resp));
+	if (!dcc_clnt_op(loc_emsg, ctxt,
+			 clnt_fgs | DCC_CLNT_FG_NO_FAIL, &anum, 0, resp_su,
+			 &req.hdr, sizeof(req)-MAX_DCC_ADMN_REQ_VAL5+val5_len,
+			 DCC_OP_ADMN,
+			 resp, sizeof(*resp))) {
+		dcc_pemsg(dcc_ex_code, emsg, "%s: %s",
+			  dcc_aop2str(resp_buf, sizeof(resp_buf), aop, val1),
+			  loc_emsg);
+		return DCC_OP_INVALID;
+	}
+
+	if (resp->hdr.op ==  DCC_OP_OK)
+		return DCC_OP_OK;
+	if (resp->hdr.op == DCC_OP_ADMN) {
+		/* clear signature after possible string */
+		int len = (ntohs(resp->hdr.len)
+			   - (sizeof(resp->resp)
+			      - sizeof(resp->resp.val.string)));
+		if (len < ISZ(resp->resp.val.string))
+			resp->resp.val.string[len] = '\0';
+		return DCC_OP_ADMN;
+	}
+	if (resp->hdr.op == DCC_OP_ERROR) {
+		dcc_pemsg(dcc_ex_code, emsg, "%s: %s",
+			  dcc_aop2str(resp_buf, sizeof(resp_buf), aop, val1),
+			  resp->resp.val.string);
+		return DCC_OP_ERROR;
+	}
+	dcc_pemsg(EX_PROTOCOL, emsg, "%s unexpected response: %s",
+		  dcc_aop2str(0, 0, aop, val1),
+		  dcc_hdr_op2str(resp_buf, sizeof(resp_buf), &resp->hdr));
+	return DCC_OP_INVALID;
+}
+
+
+
+/* try for a long time or until the server hears */
+u_char					/* 1=ok, 0=failed */
+dcc_aop_persist(DCC_EMSG emsg,
+		DCC_CLNT_CTXT *ctxt,
+		DCC_CLNT_FGS clnt_fgs,
+		u_char debug,
+		DCC_AOPS aop, u_int32_t val1,
+		int secs,		/* try for this long */
+		DCC_OP_RESP *aop_resp)	/* results here */
+{
+	struct timeval begin, now, op_start;
+	char buf1[DCC_OPBUF];
+	char buf2[DCC_OPBUF];
+	u_char complained, fast;
+	time_t us;
+	DCC_OPS result;
+
+	gettimeofday(&begin, 0);
+	complained = 0;
+	fast = 0;
+
+	for (;;) {
+		/* This kludge on the transaction ID makes all of our
+		 * requests appear to be retransmissions of a single request.
+		 * This is nice for operations such as DCC_AOP_FLOD_SHUTDOWN
+		 * that are not idempotent when the server has been stalled by
+		 * the operating system. */
+		gettimeofday(&op_start, 0);
+
+		result = dcc_aop(emsg, ctxt, clnt_fgs, NO_SRVR, 0,
+				 aop, val1, 0, 0, 0, 0, 0, aop_resp, 0);
+		/* finished if that worked */
+		if (result == DCC_OP_ADMN || result == DCC_OP_OK)
+			return 1;
+
+		/* use the same transaction ID next time */
+		if (aop_resp)
+			clnt_fgs |= DCC_CLNT_FG_RETRANS;
+
+		if (result != DCC_OP_ERROR && result != DCC_OP_INVALID)
+			dcc_pemsg(EX_UNAVAILABLE, emsg, "%s: %s",
+				  dcc_aop2str(buf1, sizeof(buf1), aop, val1),
+				  aop_resp
+				  ? dcc_hdr_op2str(buf2, sizeof(buf2),
+						   &aop_resp->hdr)
+				  : "");
+
+		/* deal with time change */
+		gettimeofday(&now, 0);
+		us = tv_diff2us(&now, &begin);
+		if (us < 0)
+			begin = op_start = now;
+
+		/* eventually give up */
+		if (us/DCC_US >= secs)
+			return 0;
+
+		us = tv_diff2us(&now, &op_start);
+		if (us >= DCC_US) {
+			fast = 0;
+		} else {
+			/* assume the server is dead if it persistently fails
+			 * immediately */
+			if (result == DCC_OP_INVALID && ++fast > 4) {
+				if (debug)
+					quiet_trace_msg("%s: assume dccd dead",
+							dcc_aop2str(buf1,
+							    sizeof(buf1),
+							    aop, val1));
+				return 0;
+			}
+			/* explicit delay since the request didn't delay */
+			usleep(1000);
+		}
+
+		/* sometimes emit a message if we are going to try again */
+		if ((result !=  DCC_OP_ERROR && result != DCC_OP_INVALID)
+		    && (debug || !complained)) {
+			complained = 1;
+			dcc_error_msg("%s", emsg);
+			emsg[0] = '\0';
+		}
+	}
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/ask.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/ask.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,1092 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * ask about a batch of checksums
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.146 $Revision$
+ */
+
+#include "dcc_ck.h"
+#include "dcc_heap_debug.h"
+#include "dcc_xhdr.h"
+
+static DCC_CKSUM_THOLDS dcc_tholds_log;
+DCC_CKSUM_THOLDS dcc_tholds_rej;
+static u_char dcc_honor_nospam[DCC_DIM_CKS];
+
+static u_char trim_grey_ip_addr;	/* remove IP address from grey triple */
+static struct in6_addr grey_ip_mask;
+
+static void honor_cnt(const DCC_GOT_CKS *cks, u_int *, DCC_CK_TYPES, DCC_TGTS);
+
+
+
+#ifdef DCC_PKT_VERSION5
+/* figure old server's target count before our latest report */
+static DCC_TGTS				/* return corrected current count */
+save_p_tgts(DCC_GOT_SUM *g,		/* put previous count in g->tgts */
+	    DCC_OPS op,
+	    const DCC_TGTS local_tgts,	/* real local target count */
+	    const DCC_TGTS gross_tgts,	/* local count adjusted by blacklist */
+	    DCC_TGTS c_tgts)		/* what the old DCC server said */
+{
+	DCC_CK_TYPES type = g->type;
+
+	if (op == DCC_OP_QUERY) {
+		/* if we switched servers and converted a report
+		 * to a query, then guess the total that the
+		 * server would have produced for a report
+		 * instead of the query we sent.
+		 *
+		 * Assume the server is not running with -K.
+		 * If the server's current value is 0 for a body checksum
+		 * then assume the report we sent to the other server has not
+		 * been flooded.
+		 * Assume other checksums will always be zero/unknown. */
+		if (DB_GLOBAL_NOKEEP(0, type))
+			return 0;
+
+		/* Assume the current value is really the previous value
+		 * because flooding has not happened */
+		g->tgts = c_tgts;
+
+		if (c_tgts < DCC_TGTS_TOO_MANY
+		    && DCC_CK_IS_BODY(type)) {
+			c_tgts += local_tgts;
+			if (c_tgts > DCC_TGTS_TOO_MANY)
+				c_tgts = DCC_TGTS_TOO_MANY;
+		}
+		return c_tgts;
+
+	} else if (c_tgts >= gross_tgts
+		   && gross_tgts < DCC_TGTS_TOO_MANY) {
+		/* if possible infer server's value before our report */
+		if (c_tgts >= DCC_TGTS_TOO_MANY)
+			g->tgts = c_tgts;
+		else
+			g->tgts = c_tgts - gross_tgts;
+	}
+
+	return c_tgts;
+}
+
+
+
+#endif /* DCC_PKT_VERSION5 */
+int					/* 1=ok, 0=no answer, -1=fatal */
+ask_dcc(DCC_EMSG emsg,
+	DCC_CLNT_CTXT *ctxt,
+	DCC_CLNT_FGS clnt_fgs,		/* DCC_CLNT_FG_* */
+	DCC_HEADER_BUF *hdr,		/* put results here */
+	DCC_GOT_CKS *cks,		/*	and here */
+	ASK_ST *ask_stp,		/*	and here */
+	u_char spam,			/* spam==0 && local_tgts==0 --> query */
+	DCC_TGTS local_tgts)		/* report these targets to DCC server */
+{
+	union {
+	    DCC_HDR	hdr;
+	    DCC_REPORT	r;
+	} rpt;
+	DCC_OP_RESP resp;
+	DCC_OPS op;
+	DCC_CK *ck;
+	DCC_GOT_SUM *g;
+	DCC_TGTS gross_tgts;
+	DCC_TGTS c_tgts;		/* server's current, total count */
+	DCC_CKS_WTGTS hdr_tgts;		/* values for X-DCC header */
+	DCC_CK_TYPES type;
+	DCC_SRVR_ID srvr_id;
+	int pkt_len, recv_len, exp_len;
+	int num_cks, ck_num, result;
+
+	memset(hdr_tgts, 0, sizeof(hdr_tgts));
+
+	/* prepare a report for the nearest DCC server */
+	if (local_tgts == 0 && !spam) {
+		/* because of greylisting, we can have a target count of 0
+		 * but need to report spam discovered by a DNSBL */
+		op = DCC_OP_QUERY;
+		gross_tgts = 0;
+		rpt.r.tgts = 0;
+	} else {
+		op = DCC_OP_REPORT;
+		if (local_tgts == DCC_TGTS_TOO_MANY
+		    || local_tgts == 0) {
+			spam = 1;
+			local_tgts = 1;
+		}
+		if (spam) {
+			*ask_stp |= (ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT);
+			gross_tgts = DCC_TGTS_TOO_MANY;
+			rpt.r.tgts = htonl(local_tgts | DCC_TGTS_SPAM);
+		} else {
+			gross_tgts = local_tgts;
+			rpt.r.tgts = htonl(local_tgts);
+		}
+	}
+
+	ck = rpt.r.cks;
+	num_cks = 0;
+	for (g = cks->sums; g <= &cks->sums[DCC_CK_TYPE_LAST]; ++g) {
+		/* never tell the DCC server about some headers */
+		if (!g->rpt2srvr)
+			continue;
+		ck->len = sizeof(*ck);
+		ck->type = g->type;
+		memcpy(ck->sum, g->sum, sizeof(ck->sum));
+		++ck;
+		++num_cks;
+	}
+	if (num_cks == 0) {
+		/* pretend we always have at least a basic body checksum
+		 * guess the DCC would have answered 0 */
+		xhdr_init(hdr, 0);
+		xhdr_add_ck(hdr, DCC_CK_BODY, gross_tgts);
+		honor_cnt(cks, ask_stp, DCC_CK_BODY, local_tgts);
+		return 1;
+	}
+
+	/* send the report and see what the DCC has to say */
+	pkt_len = (sizeof(rpt.r) - sizeof(rpt.r.cks)
+		   + num_cks * sizeof(rpt.r.cks[0]));
+	result = dcc_clnt_op(emsg, ctxt, clnt_fgs, 0, &srvr_id, 0,
+			     &rpt.hdr, pkt_len, op, &resp, sizeof(resp));
+
+	/* try a query to different server if the first failed
+	 * but a second was found */
+	if (!result && srvr_id != DCC_ID_INVALID) {
+		if (dcc_clnt_debug) {
+			if (emsg && *emsg != '\0') {
+				dcc_trace_msg("retry with different server"
+					      " after: %s", emsg);
+				*emsg = '\0';
+			} else {
+				dcc_trace_msg("retry with different server");
+			}
+		}
+		op = DCC_OP_QUERY;
+		result = dcc_clnt_op(emsg, ctxt, clnt_fgs | DCC_CLNT_FG_RETRY,
+				     0, &srvr_id, 0,
+				     &rpt.hdr, pkt_len,
+				     op, &resp, sizeof(resp));
+	}
+	if (!result) {
+		*ask_stp |= ASK_ST_LOGIT;
+	} else {
+		/* forget about it if the DCC server responded too strangely */
+		recv_len = ntohs(resp.hdr.len);
+#ifdef DCC_PKT_VERSION5
+		if (resp.hdr.pkt_vers <= DCC_PKT_VERSION5)
+			exp_len = (sizeof(resp.ans5) - sizeof(resp.ans5.b)
+				   + num_cks*sizeof(DCC_TGTS));
+		else
+#endif
+			exp_len = (sizeof(resp.ans) - sizeof(resp.ans.b)
+				   + num_cks*sizeof(resp.ans.b[0]));
+		if (recv_len != exp_len) {
+			dcc_pemsg(EX_UNAVAILABLE, emsg,
+				  "DCC %s: answered with %d instead of %d bytes",
+				  dcc_srvr_nm(0), recv_len, exp_len);
+			*ask_stp |= ASK_ST_LOGIT;
+			result = -1;
+		}
+	}
+
+	/* check the server's response to see if we have spam */
+	ck_num = 0;
+	for (g = cks->sums; g <= &cks->sums[DCC_CK_TYPE_LAST]; ++g) {
+		if (!g->rpt2srvr) {
+			/* pretend we always have a basic body checksum */
+			if (g == &cks->sums[DCC_CK_BODY])
+				honor_cnt(cks, ask_stp,
+					  DCC_CK_BODY, local_tgts);
+			continue;
+		}
+		type = g->type;		/* g->type is valid only if rpt2srvr */
+
+		if (result <= 0) {
+			c_tgts = (DCC_CK_IS_BODY(type)) ? gross_tgts : 0;
+
+#ifdef DCC_PKT_VERSION5
+		} else if (resp.hdr.pkt_vers <= DCC_PKT_VERSION5) {
+			c_tgts = save_p_tgts(g, op,
+					     local_tgts, gross_tgts,
+					     ntohl(resp.ans5.b[ck_num]));
+		} else {
+#endif /* DCC_PKT_VERSION5 */
+			/* server's total before our report */
+			g->tgts = ntohl(resp.ans.b[ck_num].p);
+			/* new total */
+			c_tgts = ntohl(resp.ans.b[ck_num].c);
+#ifdef DCC_PKT_VERSION5
+		}
+#endif
+		++ck_num;
+
+		hdr_tgts[type] = c_tgts;
+
+		/* notice DCC server's whitelist */
+		if (dcc_honor_nospam[type]) {
+			if (c_tgts == DCC_TGTS_OK) {
+				*ask_stp |= ASK_ST_SRVR_NOTSPAM;
+
+			} else if (c_tgts == DCC_TGTS_OK2) {
+				/* if server says it is half ok,
+				 * look for two halves */
+				if (*ask_stp & ASK_ST_SRVR_OK2) {
+					*ask_stp |= ASK_ST_SRVR_NOTSPAM;
+				} else {
+					*ask_stp |= ASK_ST_SRVR_OK2;
+				}
+			}
+		}
+
+		honor_cnt(cks, ask_stp, type, c_tgts);
+	}
+
+	/* honor server whitelist */
+	if (*ask_stp & ASK_ST_SRVR_NOTSPAM)
+		*ask_stp &= ~ASK_ST_SRVR_ISSPAM;
+
+	/* generate the header line now that we have checked all of
+	 * the counts against their thresholds and so know if we
+	 * must add "bulk".  Add the header even if checking is turned off
+	 * and we won't reject affected messages.  Say "many" for DNSBL
+	 * or local blacklist spam even without an answer from the DCC server
+	 * so that SpamAssassin gets the message. */
+	xhdr_init(hdr, srvr_id);
+	if (*ask_stp & ASK_ST_SRVR_ISSPAM) {
+		xhdr_add_str(hdr, DCC_XHDR_BULK);
+	} else if (*ask_stp & ASK_ST_CLNT_ISSPAM) {
+		xhdr_add_str(hdr, DCC_XHDR_BULK);
+		hdr_tgts[DCC_CK_BODY] = DCC_TGTS_TOO_MANY;
+	} else if (*ask_stp & ASK_ST_REP_ISSPAM) {
+		xhdr_add_str(hdr, DCC_XHDR_BULK_REP);
+		hdr_tgts[DCC_CK_BODY] = DCC_TGTS_TOO_MANY;
+	}
+
+	for (g = cks->sums; g <= &cks->sums[DCC_CK_TYPE_LAST]; ++g) {
+		if (!g->rpt2srvr) {
+			/* pretend we always have a body checksum */
+			if (g == &cks->sums[DCC_CK_BODY])
+				xhdr_add_ck(hdr, DCC_CK_BODY,
+					    hdr_tgts[DCC_CK_BODY]);
+			continue;
+		}
+		/* Add interesing counts to the header.
+		 * Body checksums are always interestig if we have them.
+		 * Pretend we always have a basic body checksum. */
+		type = g->type;
+		if (DCC_CK_IS_BODY(type)) {
+			xhdr_add_ck(hdr, type, hdr_tgts[type]);
+			continue;
+		}
+		if (hdr_tgts[type] != 0)
+			xhdr_add_ck(hdr, type, hdr_tgts[type]);
+	}
+
+	return result;
+}
+
+
+
+/* check message's checksums in whiteclnt for dccproc or dccsight */
+u_char					/* 1=ok 0=something to complain about */
+unthr_ask_white(DCC_EMSG emsg,
+		ASK_ST *ask_stp,
+		FLTR_SWS *swsp,
+		const char *white_nm,
+		DCC_GOT_CKS *cks,
+		DCC_CKS_WTGTS wtgts)
+{
+	DCC_WHITE_LISTING listing;
+	int retval;
+
+	/* assume DNSBLs are on unless turned off, because there is no reason
+	 * to use `dccproc -B` if you don't want to use them */
+	*swsp |= FLTR_SW_DNSBL_M;
+
+	/* fake whiteclnt if not specified */
+	if (!white_nm) {
+		dcc_merge_tholds(cks->tholds_rej, dcc_tholds_rej, 0);
+		return 1;
+	}
+
+	/* don't filter if something is wrong with the file */
+	if (!dcc_new_white_nm(emsg, &cmn_wf, white_nm)) {
+		*ask_stp |= ASK_ST_WLIST_NOTSPAM | ASK_ST_LOGIT;
+		return 0;
+	}
+
+	/* let whiteclnt file turn off the DCC and other filters */
+	*swsp = wf2sws(*swsp, &cmn_wf);
+
+	/* combine the command-line thresholds with the thresholds from
+	 * from the common /var/dcc/whiteclnt file */
+	dcc_merge_tholds(cks->tholds_rej, dcc_tholds_rej, cmn_wf.wtbl);
+
+	retval = 1;
+	switch (dcc_white_cks(emsg, &cmn_wf, cks, wtgts, &listing)) {
+	case DCC_WHITE_OK:
+	case DCC_WHITE_NOFILE:
+		break;
+	case DCC_WHITE_SILENT:
+		*ask_stp |= ASK_ST_LOGIT;
+		break;
+	case DCC_WHITE_COMPLAIN:
+	case DCC_WHITE_CONTINUE:
+		retval = 0;
+		*ask_stp |= ASK_ST_LOGIT;
+		break;
+	}
+
+	switch (listing) {
+	case DCC_WHITE_LISTED:
+		/* do not send whitelisted checksums to DCC server */
+		*ask_stp |= ASK_ST_WLIST_NOTSPAM;
+		break;
+	case DCC_WHITE_USE_DCC:
+	case DCC_WHITE_UNLISTED:
+		if (*swsp & FLTR_SW_TRAPS)
+			*ask_stp |= (ASK_ST_CLNT_ISSPAM | ASK_ST_WLIST_ISSPAM
+				     | ASK_ST_LOGIT);
+		break;
+	case DCC_WHITE_BLACK:
+		*ask_stp |= (ASK_ST_WLIST_ISSPAM
+			     | ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT);
+		break;
+	}
+
+	if (*swsp & FLTR_SW_LOG_ALL)
+		*ask_stp |= ASK_ST_LOGIT;
+
+	return retval;
+}
+
+
+
+/* ask the DCC for dccproc or dccsight but not dccifd or dccm */
+u_char					/* 1=ok 0=something to complain about */
+unthr_ask_dcc(DCC_EMSG emsg,
+	      DCC_CLNT_CTXT *ctxt,
+	      DCC_HEADER_BUF *hdr,	/* put header here */
+	      ASK_ST *ask_stp,		/* put state bites here */
+	      DCC_GOT_CKS *cks,		/* these checksums */
+	      u_char spam,		/* spam==0 && local_tgts==0 --> query */
+	      DCC_TGTS local_tgts)	/* number of addressees */
+{
+	if (*ask_stp & ASK_ST_WLIST_NOTSPAM) {
+		if (spam) {
+			/* if dccproc says it is spam, then it is, even if
+			 * the whiteclnt file says we cannot report it */
+			*ask_stp |= (ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT);
+			xhdr_init(hdr, 0);
+			xhdr_add_ck(hdr, DCC_CK_BODY, DCC_TGTS_TOO_MANY);
+		} else {
+			xhdr_whitelist(hdr);
+		}
+		/* honor log threshold for whitelisted messages */
+		dcc_honor_log_cnts(ask_stp, cks, local_tgts);
+		return 1;
+
+	} else {
+		/* if allowed by whitelisting, report our checksums to the DCC
+		 * and return with that result including setting logging */
+		return (0 < ask_dcc(emsg, ctxt, DCC_CLNT_FG_NONE,
+				    hdr, cks, ask_stp, spam,
+				    local_tgts));
+	}
+}
+
+
+
+/* parse -g for dccm and dccproc */
+void
+dcc_parse_honor(const char *arg0)
+{
+	const char *arg;
+	DCC_CK_TYPES type, t2;
+	int i;
+
+	arg = arg0;
+	if (!CLITCMP(arg, "not_") || !CLITCMP(arg, "not-")) {
+		arg += LITZ("not_");
+		i = 0;
+	} else if (!CLITCMP(arg, "no_") || !CLITCMP(arg, "no-")) {
+		arg += LITZ("no_");
+		i = 0;
+	} else {
+		i = 1;
+	}
+
+	/* allow -g for ordinary checksums but not reputations or greylisting */
+	type = dcc_str2type_thold(arg, -1);
+	if (type == DCC_CK_INVALID) {
+		dcc_error_msg("unrecognized checksum type in \"-g %s\"",
+			      arg0);
+		return;
+	}
+	for (t2 = DCC_CK_TYPE_FIRST; t2 <= DCC_CK_TYPE_LAST; ++t2) {
+		if (t2 == type
+		    || (type == SET_ALL_THOLDS && IS_ALL_CKSUM(t2))
+		    || (type == SET_CMN_THOLDS && IS_CMN_CKSUM(t2)))
+			dcc_honor_nospam[t2] = i;
+	}
+}
+
+
+
+void
+dcc_clear_tholds(void)
+{
+	DCC_CK_TYPES type;
+
+	memset(dcc_honor_nospam, 0, sizeof(dcc_honor_nospam));
+	dcc_honor_nospam[DCC_CK_IP] = 1;
+	dcc_honor_nospam[DCC_CK_ENV_FROM] = 1;
+	dcc_honor_nospam[DCC_CK_FROM] = 1;
+
+	for (type = DCC_CK_TYPE_FIRST; type <= DCC_CK_TYPE_LAST; ++type) {
+		dcc_tholds_log[type] = DCC_THOLD_UNSET;
+		dcc_tholds_rej[type] = DCC_THOLD_UNSET;
+	}
+}
+
+
+
+u_char					/* 1=merged from whiteclnt wtbl */
+dcc_merge_tholds(DCC_CKSUM_THOLDS out,
+		 const DCC_CKSUM_THOLDS in,
+		 const DCC_WHITE_TBL *wtbl)
+{
+	DCC_CK_TYPES type;
+	DCC_TGTS tgts;
+	u_char result;
+
+	if (in != out)
+		memcpy(out, in, sizeof(DCC_CKSUM_THOLDS));
+	if (!wtbl)
+		return 0;
+
+	result = 0;
+	for (type = DCC_CK_TYPE_FIRST; type <= DCC_CK_TYPE_LAST; ++type) {
+		tgts = wtbl->hdr.tholds_rej[type];
+		if (tgts != DCC_THOLD_UNSET) {
+			out[type] = tgts;
+			result = 1;
+		}
+	}
+	return result;
+}
+
+
+
+/* parse type,[log-thold,]rej-thold */
+u_char					/* 1=need a log directory */
+dcc_parse_tholds(const char *f,		/* "-c " or "-t " */
+		 const char *arg)	/* optarg */
+{
+	DCC_CK_TYPES type;
+	DCC_TGTS log_tgts, rej_tgts;
+	char *thold_rej, *thold_log;
+	u_char log_tgts_set, rej_tgts_set;
+
+	thold_log = strchr(arg, ',');
+	if (!thold_log) {
+		dcc_error_msg("missing comma in \"%s%s\"", f, arg);
+		return 0;
+	}
+	type = dcc_str2type_thold(arg, thold_log-arg);
+	if (type == DCC_CK_INVALID) {
+		dcc_error_msg("unrecognized checksum type in \"%s%s\"", f, arg);
+		return 0;
+	}
+
+	thold_log = dcc_strdup(++thold_log);
+
+	/* if there is only one threshold, take it as the spam threshold */
+	thold_rej = strchr(thold_log, ',');
+	if (!thold_rej) {
+		thold_rej = thold_log;
+		thold_log = 0;
+	} else {
+		*thold_rej++ = '\0';
+	}
+
+	log_tgts_set = log_tgts = 0;
+	if (thold_log && *thold_log != '\0') {
+		log_tgts = dcc_str2thold(type, thold_log);
+		if (log_tgts == DCC_TGTS_INVALID)
+			dcc_error_msg("unrecognized logging threshold"
+				      " \"%s\" in \"%s%s\"",
+				      thold_log, f, arg);
+		else
+			log_tgts_set = 1;
+	}
+
+
+	rej_tgts_set = rej_tgts = 0;
+	if (!thold_rej || *thold_rej == '\0') {
+		if (!thold_log || *thold_log == '\0')
+			dcc_error_msg("no thresholds in \"%s%s\"", f, arg);
+	} else {
+		rej_tgts = dcc_str2thold(type, thold_rej);
+		if (rej_tgts == DCC_TGTS_INVALID)
+			dcc_error_msg("unrecognized rejection threshold"
+				      " \"%s\" in \"%s%s\"",
+				      thold_rej, f, arg);
+		else
+			rej_tgts_set = 1;
+	}
+
+
+	if (log_tgts_set || rej_tgts_set) {
+		DCC_CK_TYPES t2;
+
+		for (t2 = DCC_CK_TYPE_FIRST; t2 <= DCC_CK_TYPE_LAST; ++t2) {
+			if (t2 == type
+			    || (type == SET_ALL_THOLDS && IS_ALL_CKSUM(t2))
+			    || (type == SET_CMN_THOLDS && IS_CMN_CKSUM(t2))) {
+				if (log_tgts_set)
+					dcc_tholds_log[t2] = log_tgts;
+				if (rej_tgts_set)
+					dcc_tholds_rej[t2] = rej_tgts;
+			}
+		}
+	}
+
+	dcc_free(thold_log);
+	return log_tgts_set;
+}
+
+
+
+static void
+honor_cnt(const DCC_GOT_CKS *cks,
+	  ASK_ST *ask_stp,		/* previous flag bits */
+	  DCC_CK_TYPES type,		/* which kind of checksum */
+	  DCC_TGTS type_tgts)		/* total count for the checksum */
+{
+	if (type >= DIM(dcc_honor_nospam))
+		return;
+
+	/* reject and log spam */
+	if (cks->tholds_rej[type] <= DCC_TGTS_TOO_MANY
+	    && cks->tholds_rej[type] <= type_tgts
+	    && type_tgts <= DCC_TGTS_TOO_MANY) {
+		*ask_stp |= (ASK_ST_SRVR_ISSPAM | ASK_ST_LOGIT);
+		return;
+	}
+
+	/* log messages that are bulkier than the log threshold */
+	if (dcc_tholds_log[type] <= DCC_TGTS_TOO_MANY
+	    && dcc_tholds_log[type] <= type_tgts)
+		*ask_stp |= ASK_ST_LOGIT;
+}
+
+
+
+/* honor log threshold for local counts and white-/blacklists */
+void
+dcc_honor_log_cnts(ASK_ST *ask_stp,	/* previous flag bits */
+		   const DCC_GOT_CKS *cks,  /* these server counts */
+		   DCC_TGTS tgts)
+{
+	const DCC_GOT_SUM *g;
+	DCC_CK_TYPES type;
+
+	if (*ask_stp & ASK_ST_LOGIT)
+		return;
+
+	if (tgts == DCC_TGTS_TOO_MANY) {
+		*ask_stp |= ASK_ST_LOGIT;
+		return;
+	}
+
+	/* pretend we always have a body checksum for the log threshold */
+	if (dcc_tholds_log[DCC_CK_BODY] <= DCC_TGTS_TOO_MANY
+	    && dcc_tholds_log[DCC_CK_BODY] <= tgts) {
+		*ask_stp |= ASK_ST_LOGIT;
+		return;
+	}
+
+	for (g = cks->sums; g <= LAST(cks->sums); ++g) {
+		type = g->type;
+		if (type == DCC_CK_INVALID
+		    || type == DCC_CK_ENV_TO)
+			continue;
+		if (dcc_tholds_log[type] > DCC_TGTS_TOO_MANY)
+			continue;
+		if (dcc_tholds_log[type] <= tgts) {
+			*ask_stp |= ASK_ST_LOGIT;
+			return;
+		}
+	}
+}
+
+
+
+/* compute switch settings from bits in a whiteclnt file */
+FLTR_SWS
+wf2sws(FLTR_SWS sws, const DCC_WF *wf)
+{
+	static time_t complained;
+	time_t now;
+	DCC_PATH abs_nm;
+	int i;
+
+	if (!grey_on
+	    && (wf->wtbl_flags & (DCC_WHITE_FG_GREY_ON
+				  | DCC_WHITE_FG_GREY_LOG_ON))
+	    && (now = time(0)) > complained+24*60*60) {
+		complained = now;
+		dcc_error_msg("%s wants greylisting"
+			      " but it is turned off",
+			      fnm2abs_err(abs_nm, wf->ascii_nm));
+	}
+
+	/* compute switch values from whiteclnt bits */
+
+	if (wf->wtbl_flags & DCC_WHITE_FG_NO_DISCARD)
+		sws |= FLTR_SW_NO_DISCARD;
+	else if (wf->wtbl_flags & DCC_WHITE_FG_DISCARD_OK)
+		sws &= ~FLTR_SW_NO_DISCARD;
+
+	if ((wf->wtbl_flags & DCC_WHITE_FG_DCC_OFF))
+		sws |= FLTR_SW_DCC_OFF;
+	else if (wf->wtbl_flags & DCC_WHITE_FG_DCC_ON)
+		sws &= ~FLTR_SW_DCC_OFF;
+
+	if (grey_on && (wf->wtbl_flags & DCC_WHITE_FG_GREY_ON)) {
+		sws &= ~FLTR_SW_GREY_OFF;
+	} else if (!grey_on || (wf->wtbl_flags & DCC_WHITE_FG_GREY_OFF)) {
+		sws |= FLTR_SW_GREY_OFF;
+	}
+
+	if (wf->wtbl_flags & DCC_WHITE_FG_LOG_ALL) {
+		sws |= FLTR_SW_LOG_ALL;
+	} else if (wf->wtbl_flags & DCC_WHITE_FG_LOG_NORMAL) {
+		sws &= ~FLTR_SW_LOG_ALL;
+	}
+
+	if (wf->wtbl_flags & DCC_WHITE_FG_GREY_LOG_ON) {
+		sws &= ~FLTR_SW_GREY_LOG_OFF;
+	} else if (wf->wtbl_flags & DCC_WHITE_FG_GREY_LOG_OFF) {
+		sws |= FLTR_SW_GREY_LOG_OFF;
+	}
+
+	if (wf->wtbl_flags & DCC_WHITE_FG_LOG_M) {
+		sws |= FLTR_SW_LOG_M;
+	} else if (wf->wtbl_flags & DCC_WHITE_FG_LOG_H) {
+		sws |= FLTR_SW_LOG_H;
+	} else if (wf->wtbl_flags & DCC_WHITE_FG_LOG_D) {
+		sws |= FLTR_SW_LOG_D;
+	}
+
+	if (wf->wtbl_flags & DCC_WHITE_FG_MTA_FIRST) {
+		sws |= FLTR_SW_MTA_FIRST;
+	} else if (wf->wtbl_flags & DCC_WHITE_FG_MTA_LAST) {
+		sws &= ~FLTR_SW_MTA_FIRST;
+	}
+
+	for (i = 0; i < MAX_DNSBL_GROUPS; ++i) {
+		if ((wf->wtbl_flags & DCC_WHITE_FG_DNSBL_ON(i))
+		    && dnsbls) {
+			sws |= FLTR_SW_DNSBL(i);
+		} else if (wf->wtbl_flags & DCC_WHITE_FG_DNSBL_OFF(i)) {
+			sws &= ~FLTR_SW_DNSBL(i);
+		}
+	}
+
+	if (wf->wtbl_flags & DCC_WHITE_FG_TRAP_ACC) {
+		sws |= FLTR_SW_TRAP_ACC;
+	} else if (wf->wtbl_flags & DCC_WHITE_FG_TRAP_REJ) {
+		sws |= FLTR_SW_TRAP_REJ;
+	}
+
+	return sws | FLTR_SW_SET;
+}
+
+
+
+#define LOG_ASK_ST_BLEN	    160
+#define LOG_ASK_ST_OFF	    "(off)"
+#define LOG_ASK_ST_OVF	    " ...\n\n"
+static int
+log_ask_st_sub(char *buf, int blen,
+	       const char *s, int slen,
+	       u_char off)
+{
+	int dlen, tlen;
+
+	/* quit if no room at all in the log */
+	if (blen >= LOG_ASK_ST_BLEN)
+		return LOG_ASK_ST_BLEN;
+
+	/* quit if nothing to say */
+	if (!s || !slen)
+		return blen;
+
+	dlen = LOG_ASK_ST_BLEN - blen;
+	tlen = LITZ(LOG_ASK_ST_OVF)+2+slen;
+	if (off)			/* notice if we need to say "(off)" */
+		tlen += LITZ(LOG_ASK_ST_OFF);
+	if (dlen <= tlen) {
+		/* show truncation of the message with "..." */
+		memcpy(&buf[blen], LOG_ASK_ST_OVF, LITZ(LOG_ASK_ST_OVF));
+		blen += LITZ(LOG_ASK_ST_OVF);
+		if (blen < LOG_ASK_ST_BLEN)
+			memset(&buf[blen], ' ', LOG_ASK_ST_BLEN-blen);
+		return LOG_ASK_ST_BLEN;
+	}
+
+	if (blen > 0 && buf[blen-1] != '\n') {
+		buf[blen++] = ' ';
+		buf[blen++] = ' ';
+	}
+	memcpy(buf+blen, s, slen);
+	blen += slen;
+	if (off) {
+		memcpy(buf+blen, LOG_ASK_ST_OFF, LITZ(LOG_ASK_ST_OFF));
+		blen += LITZ(LOG_ASK_ST_OFF);
+	}
+	return blen;
+}
+
+
+
+/* generate log file line of results */
+void
+log_ask_st(LOG_WRITE_FNC fnc, void *cp, ASK_ST ask_st, FLTR_SWS sws,
+	   u_char log_type,		/* 0="" 1="per-user" 2="global" */
+	   const DCC_HEADER_BUF *hdr)
+{
+	char buf[LOG_ASK_ST_BLEN+3];
+	char dnsbl_buf[24];
+	int blen, len, i;
+#define S(str,off) (blen = log_ask_st_sub(buf, blen, str, LITZ(str), off))
+#define S0(bit,off,str) if (ask_st & bit) S(str,off)
+#define S1(bit,s1off,str) S0(bit,(log_type != 2 && (s1off)),str)
+#define S2(bit,str) S0(bit,0,str)
+
+	blen = 0;
+	S2(ASK_ST_QUERY, "query");
+
+	/* the CGI scripts want to know why */
+	if (sws & FLTR_SW_MTA_FIRST) {
+		S2(ASK_ST_MTA_ISSPAM,	"MTA"DCC_XHDR_ISSPAM);
+		S2(ASK_ST_MTA_NOTSPAM,	"MTA"DCC_XHDR_ISOK);
+	}
+
+	S2(ASK_ST_WLIST_NOTSPAM,	    "wlist"DCC_XHDR_ISOK);
+	if (log_type != 2 && !(ask_st & ASK_ST_WLIST_NOTSPAM))
+		S2(ASK_ST_WLIST_ISSPAM,	    "wlist"DCC_XHDR_ISSPAM);
+
+	S1(ASK_ST_SRVR_ISSPAM,	(sws & FLTR_SW_DCC_OFF), "DCC"DCC_XHDR_ISSPAM);
+	S1(ASK_ST_SRVR_NOTSPAM,	(sws & FLTR_SW_DCC_OFF), "DCC"DCC_XHDR_ISOK);
+	S1(ASK_ST_REP_ISSPAM,  !(sws & FLTR_SW_REP_ON),	"Rep"DCC_XHDR_ISSPAM);
+
+	for (i = 0; i < MAX_DNSBL_GROUPS; ++i) {
+		if (ask_st & ASK_ST_DNSBL_HIT(i)) {
+			if (have_dnsbl_groups)
+				len = snprintf(dnsbl_buf, sizeof(dnsbl_buf),
+					       "DNSBL%d"DCC_XHDR_ISSPAM, i+1);
+			else
+				len = snprintf(dnsbl_buf, sizeof(dnsbl_buf),
+					       "DNSBL"DCC_XHDR_ISSPAM);
+			/* log "DNSBLx-->spam" or "DNSBLx-->spam(off)" */
+			blen = log_ask_st_sub(buf, blen,
+					      dnsbl_buf, len,
+					      log_type != 2
+					      && !(sws & FLTR_SW_DNSBL(i)));
+		} else if (ask_st & ASK_ST_DNSBL_TIMEO(i)) {
+			if (have_dnsbl_groups)
+				len = snprintf(dnsbl_buf, sizeof(dnsbl_buf),
+					       "DNSBL%d(timeout)", i+1);
+			else
+				len = snprintf(dnsbl_buf, sizeof(dnsbl_buf),
+					       "DNSBL(timeout)");
+			blen = log_ask_st_sub(buf, blen,
+					      dnsbl_buf, len, 0);
+		}
+	}
+
+	if (!(sws & FLTR_SW_MTA_FIRST)) {
+		S2(ASK_ST_MTA_ISSPAM,	"MTA"DCC_XHDR_ISSPAM);
+		S2(ASK_ST_MTA_NOTSPAM,  "MTA"DCC_XHDR_ISOK);
+	}
+	blen = log_ask_st_sub(buf, blen, dcc_progname, dcc_progname_len, 0);
+	if (log_type == 1) {
+		blen = log_ask_st_sub(buf, blen,
+				      "per-user", LITZ("per-user"), 0);
+	} else if (log_type == 2) {
+		blen = log_ask_st_sub(buf, blen,
+				      "global", LITZ("global"), 0);
+	}
+	blen = log_ask_st_sub(buf, blen, "\n\n", 2, 0);
+	fnc(cp, buf, blen);
+
+	if (hdr->used != 0)
+		xhdr_write(fnc, cp, hdr->buf, hdr->used, 0);
+#undef S
+#undef S0
+#undef S1
+#undef S2
+}
+
+
+
+/* parse -G options for DCC clients */
+u_char					/* 0=bad */
+dcc_parse_client_grey(const char *arg)
+{
+	int bits;
+	const char *p;
+
+	while (*arg != '\0') {
+		if (dcc_ck_word_comma(&arg, "on")) {
+			grey_on = 1;
+			continue;
+		}
+		if (dcc_ck_word_comma(&arg, "off")) {
+			grey_on = 0;
+			continue;
+		}
+		if (dcc_ck_word_comma(&arg, "query")) {
+			grey_query_only = 1;
+			continue;
+		}
+		if (dcc_ck_word_comma(&arg, "noIP")) {
+			grey_on = 1;
+			trim_grey_ip_addr = 1;
+			memset(&grey_ip_mask, 0, sizeof(grey_ip_mask));
+			continue;
+		}
+		if (!CLITCMP(arg, "IPmask/")) {
+			bits = 0;
+			for (p = arg+LITZ("IPmask/");
+			     *p >= '0' && *p <= '9';
+			     ++p)
+				bits = bits*10 + *p - '0';
+			if (bits > 0 && bits < 128
+			    && (*p == '\0' || *p == ',')) {
+				arg = p;
+				if (*p == ',')
+					++arg;
+				grey_on = 1;
+				trim_grey_ip_addr = 1;
+				/* assume giant blocks are really IPv4 */
+				if (bits <= 32)
+					bits += 128-32;
+				dcc_bits2mask(&grey_ip_mask, bits);
+				continue;
+			}
+		}
+		return 0;
+	}
+	return 1;
+}
+
+
+
+/* sanity check the DCC server's answer */
+u_char
+dcc_ck_grey_answer(DCC_EMSG emsg, const DCC_OP_RESP *resp)
+{
+	int recv_len;
+
+	recv_len = ntohs(resp->hdr.len);
+	if (resp->hdr.op != DCC_OP_ANSWER) {
+		dcc_pemsg(EX_UNAVAILABLE, emsg, "DCC %s: %s %*s",
+			  dcc_srvr_nm(1),
+			  dcc_hdr_op2str(0, 0, &resp->hdr),
+			  (resp->hdr.op == DCC_OP_ERROR
+			   ? (recv_len - (ISZ(resp->error)
+					  - ISZ(resp->error.msg)))
+			   : 0),
+			  resp->error.msg);
+		return 0;
+	}
+
+	if (recv_len != sizeof(DCC_GREY_ANSWER)) {
+		dcc_pemsg(EX_UNAVAILABLE, emsg,
+			  "greylist server %s answered with %d instead of"
+			  " %d bytes",
+			  dcc_srvr_nm(1), recv_len, ISZ(DCC_GREY_ANSWER));
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+ASK_GREY_RESULT
+ask_grey(DCC_EMSG emsg,
+	 DCC_CLNT_CTXT *ctxt,
+	 DCC_OPS op,			/* DCC_OP_GREY_{REPORT,QUERY,WHITE} */
+	 DCC_SUM msg_sum,		/* put msg+sender+target cksum here */
+	 DCC_SUM triple_sum,		/* put greylist triple checksum here */
+	 const DCC_GOT_CKS *cks,
+	 const DCC_SUM env_to_sum,
+	 DCC_TGTS *pembargo_num,
+	 DCC_TGTS *pearly_tgts,		/* ++ report to DCC even if embargoed */
+	 DCC_TGTS *plate_tgts)		/* ++ don't report to DCC */
+{
+	MD5_CTX ctx;
+	DCC_REPORT rpt;
+	DCC_OP_RESP resp;
+	DCC_CK *ck;
+	DCC_CK_TYPES type;
+	const DCC_GOT_SUM *g;
+	DCC_TGTS result_tgts;
+	int num_cks;
+
+	if (cks->sums[DCC_CK_IP].type != DCC_CK_IP) {
+		dcc_pemsg(EX_UNAVAILABLE, emsg,
+			  "IP address not available for greylisting");
+		memset(triple_sum, 0, sizeof(*triple_sum));
+		memset(msg_sum, 0, sizeof(*msg_sum));
+		return ASK_GREY_FAIL;
+	}
+	if (cks->sums[DCC_CK_ENV_FROM].type != DCC_CK_ENV_FROM) {
+		dcc_pemsg(EX_UNAVAILABLE, emsg,
+			  "env_From not available for greylisting");
+		memset(triple_sum, 0, sizeof(*triple_sum));
+		memset(msg_sum, 0, sizeof(*msg_sum));
+		return ASK_GREY_FAIL;
+	}
+
+	/* Check the common checksums for whitelisting at the greylist server.
+	 * This assumes DCC_CK_GREY_TRIPLE > DCC_CK_GREY_MSG > other types */
+	ck = rpt.cks;
+	num_cks = 0;
+	for (type = 0, g = cks->sums;
+	     type <= DCC_CK_TYPE_LAST;
+	     ++type, ++g) {
+		/* greylisting needs a body checksum, even if
+		 * it is the fake checksum for a missing body */
+		if (!g->rpt2srvr && type != DCC_CK_BODY)
+			continue;
+		ck->type = type;
+		ck->len = sizeof(*ck);
+		memcpy(ck->sum, g->sum, sizeof(ck->sum));
+		++ck;
+		++num_cks;
+	}
+
+	/* include in the request the grey message checksum as the checksum
+	 * of the body, the env_From sender, and env_To target checksums */
+	MD5Init(&ctx);
+	MD5Update(&ctx, cks->sums[DCC_CK_BODY].sum, sizeof(DCC_SUM));
+	MD5Update(&ctx, cks->sums[DCC_CK_ENV_FROM].sum, sizeof(DCC_SUM));
+	MD5Update(&ctx, env_to_sum, sizeof(DCC_SUM));
+	MD5Final(msg_sum, &ctx);
+	ck->type = DCC_CK_GREY_MSG;
+	ck->len = sizeof(*ck);
+	memcpy(ck->sum, msg_sum, sizeof(ck->sum));
+	++ck;
+	++num_cks;
+
+	/* include the triple checksum of the sender, the sender's IP
+	 * address, and the target */
+	MD5Init(&ctx);
+	if (trim_grey_ip_addr) {
+		struct in6_addr addr;
+		DCC_SUM sum;
+		int wno;
+
+		for (wno = 0; wno < 4; ++wno) {
+			addr.s6_addr32[wno] = (cks->ip_addr.s6_addr32[wno]
+					       & grey_ip_mask.s6_addr32[wno]);
+		}
+		dcc_ck_ipv6(sum, &addr);
+		MD5Update(&ctx, sum, sizeof(DCC_SUM));
+	} else {
+		MD5Update(&ctx, cks->sums[DCC_CK_IP].sum, sizeof(DCC_SUM));
+	}
+	MD5Update(&ctx, cks->sums[DCC_CK_ENV_FROM].sum, sizeof(DCC_SUM));
+	MD5Update(&ctx, env_to_sum, sizeof(DCC_SUM));
+	MD5Final(triple_sum, &ctx);
+	ck->type = DCC_CK_GREY3;
+	ck->len = sizeof(*ck);
+	memcpy(ck->sum, triple_sum, sizeof(ck->sum));
+	++num_cks;
+
+	if (!dcc_clnt_op(emsg, ctxt, DCC_CLNT_FG_GREY, 0, 0, 0,
+			 &rpt.hdr, (sizeof(rpt) - sizeof(rpt.cks)
+				    + num_cks*sizeof(rpt.cks[0])),
+			 op, &resp, sizeof(resp))) {
+		return ASK_GREY_FAIL;
+	}
+
+	if (!dcc_ck_grey_answer(emsg, &resp))
+		return ASK_GREY_FAIL;
+
+	/* see what the greylist server had to say */
+	result_tgts = ntohl(resp.gans.triple);
+	switch (result_tgts) {
+	case DCC_TGTS_OK:		/* embargo ended just now */
+		/* if we have previously included this target in a count of
+		 * targets sent to the DCC, then do not include it now */
+		if (resp.gans.msg != 0 && plate_tgts)
+			++*plate_tgts;
+		if (pembargo_num)
+			*pembargo_num = 0;
+		return ASK_GREY_EMBARGO_END;
+
+	case DCC_TGTS_TOO_MANY:		/* no current embargo */
+		if (pembargo_num)
+			*pembargo_num = 0;
+		return ((resp.gans.msg != 0)
+			? ASK_GREY_EMBARGO_END
+			: ASK_GREY_PASS);
+
+	case DCC_TGTS_GREY_WHITE:	/* whitelisted for greylisting */
+		if (pembargo_num)
+			*pembargo_num = 0;
+		return ASK_GREY_WHITE;
+
+	default:			/* embargoed */
+		/* if this is a brand new embargo,
+		 * then count this target in the DCC report */
+		if (resp.gans.msg == 0 && pearly_tgts)
+			++*pearly_tgts;
+		if (pembargo_num)
+			*pembargo_num = result_tgts+1;
+		return ASK_GREY_EMBARGO;
+	}
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/ck.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/ck.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,798 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * compute simple checksums
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.90 $Revision$
+ */
+
+#include "dcc_ck.h"
+#include "dcc_heap_debug.h"
+#include "dcc_xhdr.h"
+#ifndef DCC_WIN32
+#include <arpa/inet.h>
+#endif
+
+
+/* "substitute" or locally configured checksums */
+typedef struct {
+    u_int	nm_len;
+    const char	*nm;			/* name of the checksum */
+} DCC_SUB_CK;
+static DCC_SUB_CK sub_cks[DCC_MAX_SUB_CKS];
+static u_int num_sub_cks;
+
+
+/* get the checksum of an IPv6 address */
+void
+dcc_ck_ipv6(DCC_SUM sum, const struct in6_addr *addr)
+{
+	MD5_CTX ctx;
+
+	MD5Init(&ctx);
+	MD5Update(&ctx, (void *)addr, sizeof(*addr));
+	MD5Final(sum, &ctx);
+}
+
+
+
+/* add an IP address to the set of checksums */
+void
+dcc_get_ipv6_ck(DCC_GOT_CKS *cks, const struct in6_addr *addrp)
+{
+	cks->sums[DCC_CK_IP].type = DCC_CK_IP;
+	cks->sums[DCC_CK_IP].rpt2srvr = 1;
+	cks->sums[DCC_CK_IP].tgts = DCC_TGTS_INVALID;
+	dcc_ck_ipv6(cks->sums[DCC_CK_IP].sum, addrp);
+
+	if (&cks->ip_addr != addrp)
+		cks->ip_addr = *addrp;
+}
+
+
+
+void
+dcc_unget_ip_ck(DCC_GOT_CKS *cks)
+{
+	memset(&cks->ip_addr, 0, sizeof(cks->ip_addr));
+	CLR_GOT_SUM(&cks->sums[DCC_CK_IP]);
+	CLR_GOT_SUM(&cks->sums[DCC_CK_IP]);
+}
+
+
+
+/* Make DCC_CK_IP from a string containing an IPv4 or IPv6 address.
+ *	Because inet_pton() is picky, the string must be unambiguous and
+ *	fussy. */
+u_char
+dcc_get_str_ip_ck(DCC_GOT_CKS *cks,	/* put checksum here */
+		  const char *str)	/* from this IP address string */
+{
+	DCC_SOCKU su;
+
+	if (!dcc_str2ip(&su, str))
+		return 0;
+
+	if (su.sa.sa_family == AF_INET) {
+		/* treat IPv4 addresses as IPv6 so that everyone computes
+		 * the same checksum */
+		dcc_ipv4toipv6(&cks->ip_addr, su.ipv4.sin_addr);
+	} else {
+		cks->ip_addr = su.ipv6.sin6_addr;
+	}
+
+	dcc_get_ipv6_ck(cks, &cks->ip_addr);
+	return 1;
+}
+
+
+
+/* Compute a checksum from a string with matching but optional carets or
+ * quotes, after stripping the quotes or carets.
+ * Ignore case and white space */
+void
+dcc_str2ck(DCC_SUM sum,
+	   const char *hdr,		/* substitute header type */
+	   u_int hdr_len,
+	   const char *str)		/* string to checksum */
+{
+	MD5_CTX ctx;
+	u_int len;
+	char *p;
+	char c, cbuf[DCC_HDR_CK_MAX];
+
+	/* ignore whitespace, [<>'",] and case
+	 * do not ignore [.-_] to prevent confusing hostnames */
+	p = cbuf;
+	while ((c = *str++) != '\0' && p <= LAST(cbuf)) {
+		if (DCC_IS_WHITE(c)
+		    || c == '<' || c == '>'
+		    || c == '\'' || c == '"' || c == ',')
+			continue;
+		*p++ = DCC_TO_LOWER(c);
+	}
+	str = cbuf;
+	len = p - str;
+	/* strip trailing periods, mostly for mail_host */
+	while (len >= 1
+	       && *(p-1) == '.') {
+		--len;
+		--p;
+	}
+	MD5Init(&ctx);
+	if (hdr)
+		MD5Update(&ctx, hdr, hdr_len);
+	MD5Update(&ctx, str, len);
+	MD5Final(sum, &ctx);
+}
+
+
+
+/* make checksum from a string for headers and envelope */
+u_char					/* 1=ok 0=bad string */
+dcc_get_cks(DCC_GOT_CKS *cks,		/* put checksum here */
+	    DCC_CK_TYPES type,
+	    const char *str,		/* checksum of this string */
+	    u_char rpt2srvr)
+{
+	DCC_GOT_SUM *g;
+
+	g = &cks->sums[type];
+
+	switch (type) {
+	case DCC_CK_INVALID:
+	case DCC_CK_IP:
+	case DCC_CK_SUB:
+	case DCC_CK_SRVR_ID:
+	case DCC_CK_BODY:
+	case DCC_CK_FUZ1:
+	case DCC_CK_FUZ2:
+	case DCC_CK_G_MSG_R_TOTAL:
+	case DCC_CK_G_TRIPLE_R_BULK:
+		dcc_logbad(EX_SOFTWARE, "invalid checksum %s",
+			   dcc_type2str_err(type, 0, 0, 0));
+		return 0;
+
+	case DCC_CK_ENV_FROM:
+	case DCC_CK_FROM:
+	case DCC_CK_ENV_TO:
+	case DCC_CK_RECEIVED:
+	case DCC_CK_MESSAGE_ID:
+		dcc_str2ck(g->sum, 0, 0, str);
+		break;
+	}
+
+	g->type = type;
+	g->rpt2srvr = rpt2srvr;
+	g->tgts = DCC_TGTS_INVALID;
+	return 1;
+}
+
+
+
+/* make checksum for a locally configured header */
+u_char					/* 1=done 0=failed */
+dcc_ck_get_sub(DCC_GOT_CKS *cks,
+		 const char *hdr,	/* header name, not '\0' terminated */
+		 const char *str)	/* header value if not after hdr */
+{
+	DCC_GOT_SUM *g;
+	const DCC_SUB_CK *sck;
+	DCC_CK_TYPES type;
+	int i;
+
+	/* look for the header name in the list of locally configured headers */
+	sck = &sub_cks[0];
+	for (i = num_sub_cks; ; ++sck, --i) {
+		if (i <= 0)
+			return 0;	/* this header is not in the list */
+		if (!strncasecmp(hdr, sck->nm, sck->nm_len)
+		    && (hdr[sck->nm_len] == '\0'
+			|| hdr[sck->nm_len] == ':'))
+			break;
+	}
+
+	/* Get the header value if the caller did not separate it.
+	 * The colon is present if the header field was not separated */
+	if (!str)
+		str = hdr+sck->nm_len+1;
+
+	/* find a free checksum slot
+	 * or a slot already assigned to the header */
+	type = DCC_CK_SUB;
+	g = &cks->sums[type];
+	for (;;) {
+		if (type >= DIM(cks->sums))
+			return 0;	/* none free */
+
+		if (g->type == DCC_CK_INVALID
+		    && (type > DCC_CK_TYPE_LAST
+			|| type == DCC_CK_SUB))
+			break;		/* found a free slot */
+
+		if (g->type == DCC_CK_SUB
+		    && g->hdr_nm == sck->nm)
+			break;		/* found previously assigned slot */
+		++g;
+		++type;
+	}
+
+	dcc_str2ck(g->sum, sck->nm, sck->nm_len, str);
+	g->type = DCC_CK_SUB;
+	g->rpt2srvr = 1;
+	g->tgts = DCC_TGTS_INVALID;
+	g->hdr_nm = sck->nm;
+	return 1;
+}
+
+
+
+/* add to the list of locally configured or substitute headers */
+u_char
+dcc_add_sub_hdr(DCC_EMSG emsg, const char *hdr)
+{
+	const char *p;
+	char c, *q;
+	u_int n, len;
+
+	if (num_sub_cks >= DIM(sub_cks)) {
+		dcc_pemsg(EX_USAGE, emsg,
+			  "too many substitute headers with \"%s\"", hdr);
+		return 0;
+	}
+
+	p = hdr;
+	for (;;) {
+		if (*p == '\0')
+			break;
+		if (*p == ':' && p[1] == '\0') {
+			--p;
+			break;
+		}
+		if (*p <= ' ' || *p >= 0x7f || *p == ':') {
+			dcc_pemsg(EX_USAGE, emsg,
+				  "illegal SMTP field name character in \"%s\"",
+				  hdr);
+			return 0;
+		}
+		++p;
+	}
+
+	len = p - hdr;
+	if (len == 0) {
+		dcc_pemsg(EX_USAGE, emsg, "illegal empty field name");
+		return 0;
+	}
+
+	/* ignore duplicates */
+	for (n = 0; n < num_sub_cks; ++n) {
+		if (len == sub_cks[n].nm_len
+		    && !strncasecmp(hdr, sub_cks[n].nm, len))
+			return 1;
+	}
+
+	sub_cks[num_sub_cks].nm_len = len;
+	q = dcc_malloc(len+1);
+	sub_cks[num_sub_cks].nm = q;
+	do {
+		c = *hdr++;
+		*q++ = DCC_TO_LOWER(c);
+	} while (--len > 0);
+	*q = '\0';
+	++num_sub_cks;
+
+	return 1;
+}
+
+
+
+static int
+get_received_addr(char addr_buf[INET6_ADDRSTRLEN+2],
+      const char *hdr)			/* *hdr == '[' before the address */
+{
+	int a_len;
+
+
+	a_len = 1+strspn(hdr+1, ".:abcdefABCDEF0123456789");
+	if (a_len <= 6+1 || a_len >= INET6_ADDRSTRLEN+1)
+		return 0;
+	if (hdr[a_len] != ']')
+		return 0;
+
+	/* capture the address
+	 *	include leading '[' in case we later need a host name */
+	memcpy(addr_buf, hdr, a_len);
+	addr_buf[a_len] = '\0';
+
+	return a_len;
+}
+
+
+
+/* find IP address, client host name, and HELO string in a Received:
+ *	    header of forms:
+ *  #1	Received: from helo (hostname [addr] ...
+ *	Received: from helo ([addr] ...
+ *  #2	Received: from hostname [addr] ...
+ *	Received: from  [addr] ...
+ *  #3	Received: from qmailheloandhostname (addr) ...
+ *  #4	Received: from qmailhostname (HELO qmailhelo) (addr) ...
+ *   or	Received: from qmailhostname (HELO qmailhelo) ([addr]) ...
+ *
+ * ignore these forms:
+ *  #5	Received: from localhost by hostname with LMTP
+ *  #6	Received  (qmail 4824 invoked by uid 1000); 8 Nov 2005 12:13:33 -0000
+ *  #7	Received: (qmail 21530 invoked from network); 29 Aug 2005 16:05:04 -0000
+ *  #8	Received: (from user@localhost) by lochost (8.12.10/8.12.10/Submit) ...
+ *  #9  Received: by hostname (Postfix) id ...
+ *
+ * This should be called only with Received: headers that are known to
+ *	have been added by trustworthy code such as the local system
+ *	or an MX secondary.
+ * Return 0 for unknown header, "" if IP address found, or stupid type string
+ */
+const char *
+parse_received(const char *hdr,		/* the null terminated header */
+	       DCC_GOT_CKS *cks,	/* put address checksum here */
+	       char *helo,		/* optionally put HELO value here */
+	       int helo_len,
+	       char *clnt_str, int clnt_str_len,
+	       char *clnt_name, int clnt_name_len)
+{
+	char addr_buf[INET6_ADDRSTRLEN+2];
+	const char *h, *n;
+	int h_len, n_len, a_len;
+	int i;
+
+	/* make the field name optional */
+	if (!CLITCMP(hdr, "Received:"))
+		hdr += LITZ("Received");
+	hdr += strspn(hdr, " \t\r\n");
+
+/* #define DCC_DEBUG_PARSE_RECEIVED */
+#ifdef DCC_DEBUG_PARSE_RECEIVED
+	printf("\n\nReceived: %s\n", hdr);
+#endif
+#define SPAN_ADDR(l,p) (*(p) >= '0' && *(p) <= '9'			\
+			&& ((l) = strspn((p), "0123456789.")) >= 7	\
+			&& (l) < INET_ADDRSTRLEN)
+
+	if (CLITCMP(hdr, "from")) {
+		/* It does not match "Received: from" in #1, #2, #3, and #5
+		 * Recognize #6 and #7 */
+		if (!LITCMP(hdr, "(qmail ")) {
+			hdr += LITZ("(qmail ");
+			i = strspn(hdr, "0123456789");
+			if (i == 0)
+				return 0;
+			hdr += i;
+			if (!LITCMP(hdr, " invoked from network)")
+			    || !LITCMP(hdr, " invoked by uid "))
+				return "qmail";
+			return 0;
+		}
+		/* recognize #8 */
+		if (!LITCMP(hdr, "(from ")) {
+			hdr += LITZ("(from ");
+			hdr = strpbrk(hdr, DCC_WHITESPACE"@");
+			if (!hdr || *hdr != '@')
+				return 0;
+			hdr = strpbrk(hdr, DCC_WHITESPACE")");
+			if (!hdr || *hdr != ')')
+				return 0;
+			hdr += 1+strspn(hdr+1, DCC_WHITESPACE);
+			if (LITCMP(hdr, "by "))
+				return 0;
+			hdr += LITZ("by ");
+			if (strstr(hdr, "/Submit"))
+				return "sendmail Submit";
+			return 0;
+		}
+		/* recognize #9 */
+		if (!LITCMP(hdr, "by ")) {
+			hdr += LITZ("by ");
+			hdr = strpbrk(hdr, DCC_WHITESPACE);
+			if (!hdr)
+				return 0;
+			++hdr;
+			if (!LITCMP(hdr, "(Postfix)"))
+				return "postfix";
+			return 0;
+		}
+		/* unrecognized */
+		return 0;
+	}
+
+	hdr += LITZ("from");
+	i = strspn(hdr, DCC_WHITESPACE);
+	if (i == 0)
+		return 0;
+	hdr += i;
+
+	/* We have "Received: from "
+	 * get the host name or HELO value before '(' or '[' in
+	 * #1, #2, #3, and #5 */
+	h = hdr;
+	hdr = strpbrk(hdr, DCC_WHITESPACE"([");
+	if (!hdr)
+		return 0;		/* unrecognized */
+	h_len = hdr - h;
+	hdr += strspn(hdr, DCC_WHITESPACE);
+
+	if (*hdr == '(') {
+		/* look for client host name of #1
+		 * or IPv4 address of #3
+		 * or HELO value and IPv4 address of #4 */
+		++hdr;
+
+		if (SPAN_ADDR(a_len, hdr)
+		    && hdr[a_len] == ')') {
+			/* we seem to have the IPv4 address of #3 */
+			n = h;
+			n_len = h_len;
+			addr_buf[0] = '[';
+			memcpy(addr_buf+1, hdr, a_len);
+			addr_buf[a_len+1] = '\0';
+
+		} else if (!LITCMP(hdr, "HELO ")
+			   && hdr[LITZ("HELO ")] != '[') {
+			/* we have the #4 qmail HELO form when reverse DNS name
+			 * and helo value differ or unrecognizable */
+			n = h;
+			n_len = h_len;
+			h = hdr + LITZ("HELO ");
+			hdr = strpbrk(h, " \t'\"()[]");
+			if (!hdr)
+				return 0;
+			h_len = hdr - h;
+			if (!h_len
+			    || LITCMP(hdr, ") ("))
+			    return 0;
+			hdr += LITZ(") (");
+			if (SPAN_ADDR(a_len, hdr)
+			    && hdr[a_len] == ')') {
+				addr_buf[0] = '[';
+				memcpy(addr_buf+1, hdr, a_len);
+				addr_buf[a_len+1] = '\0';
+			} else if (hdr[0] == '['
+				   && SPAN_ADDR(a_len, hdr+1)
+				   && hdr[1+a_len] == ']'
+				   && hdr[2+a_len] == ')') {
+				memcpy(addr_buf, hdr, a_len+1);
+				addr_buf[a_len+1] = '\0';
+			} else {
+				return 0;
+			}
+
+		} else {
+			/* it is #1 or unrecognizable */
+			n = hdr;
+			hdr = strpbrk(hdr, DCC_WHITESPACE"[");
+			if (!hdr)
+				return 0;
+			n_len = hdr - n;
+			hdr += strspn(hdr, DCC_WHITESPACE);
+			if (*hdr != '[')
+				return 0;
+			a_len = get_received_addr(addr_buf, hdr);
+			if (!a_len)
+				return 0;
+		}
+
+	} else if (*hdr == '[') {
+		/* format #2; we have possibly null client name and no HELO */
+		n = h;
+		n_len = h_len;
+		h_len = 0;
+		a_len = get_received_addr(addr_buf, hdr);
+		if (!a_len)
+			return 0;
+
+	} else if (!CLITCMP(hdr, "by ")) {
+		/* recognize #5 */
+		hdr += LITZ("by ");
+		n = strchr(hdr, ' ');
+		if (!n || n > hdr+DCC_MAXDOMAINLEN)
+			return 0;
+		if (!CLITCMP(n, " with LMTP"))
+			return "LMTP";	/* stupid type string */
+		return 0;
+
+	} else {
+		return 0;
+	}
+
+	/* it looks ok so send out all the answers
+	 * if the IP address makes sense */
+	if (!dcc_get_str_ip_ck(cks, addr_buf+1))
+		return 0;
+
+	dcc_ipv6tostr(clnt_str, clnt_str_len, &cks->ip_addr);
+
+	if (clnt_name && clnt_name_len) {
+		if (n_len == 0) {
+			/* use address as the client host name */
+			addr_buf[a_len] = ']';
+			n_len = a_len+1;
+			n = addr_buf;
+		}
+		if (clnt_name_len > n_len+1)
+			clnt_name_len = n_len+1;
+		STRLCPY(clnt_name, n, clnt_name_len);
+	}
+
+	if (helo && helo_len) {
+		if (helo_len > h_len+1)
+			helo_len = h_len+1;
+		STRLCPY(helo, h, helo_len);
+	}
+
+#ifdef DCC_DEBUG_PARSE_RECEIVED
+	printf("helo=%s  clnt_str=%s  clnt_name=%s\n",
+	       helo, clnt_str, clnt_name);
+#endif
+
+	return "";
+
+#undef SPAN_ADDR
+}
+
+
+
+u_char					/* 1=found env_From value */
+parse_return_path(const char *hdr, char *buf, int buf_len)
+{
+	int i;
+
+	if (CLITCMP(hdr, "Return-Path:"))
+	    return 0;
+
+	hdr += LITZ("Return-Path:");
+	hdr += strspn(hdr, " \t");
+	i = strlen(hdr);
+	while (i > 0 && (hdr[i-1] == '\r' || hdr[i-1] == '\n'))
+		--i;
+	if (i >= 2 && *hdr == '<' && hdr[i-1] == '>') {
+		++hdr;
+		i -= 2;
+	}
+	if (i >= buf_len-1)
+		i = buf_len-1;
+	if (i <= 0)
+		return 0;
+	memcpy(buf, hdr, i);
+	buf[i] = '\0';
+
+	return 1;
+}
+
+
+
+u_char					/* 1=found env_From value */
+parse_unix_from(const char *hdr, char *buf, int buf_len)
+{
+	const char *p;
+	int i;
+
+	if (strncmp(hdr, "From ", LITZ("From ")))
+		return 0;
+
+	hdr += LITZ("From ");
+	hdr += strspn(hdr, " ");
+	p = strchr(hdr, ' ');
+	if (p == 0)
+		return 0;
+
+	i = p-hdr;
+	if (i >= buf_len)
+		i = buf_len-1;
+	if (i <= 0)
+		return 0;
+	memcpy(buf, hdr, i);
+	buf[i] = '\0';
+	return 1;
+}
+
+
+
+u_char
+parse_mail_host(const char *env_from, char *buf, int buf_len)
+{
+	const char *p, *p2, *p3;
+	int i;
+
+	p = strchr(env_from, '@');
+	if (!p)
+		return 0;
+	p2 = strchr(++p, '>');
+	if (!p2)
+		p2 = p+strlen(p);
+
+	/* do not try to figure out source routes */
+	p3 = strpbrk(p, ";@,");
+	if (p3 && p3 < p2)
+		return 0;
+
+	i = p2-p;
+	if (i >= buf_len)
+		i = buf_len-1;
+	if (i <= 0)
+		return 0;
+	memcpy(buf, p, i);
+	buf[i] = '\0';
+	return 1;
+}
+
+
+
+void
+dcc_print_cks(LOG_WRITE_FNC out, void *arg,
+	      u_char is_spam, DCC_TGTS local_tgts,
+	      const DCC_GOT_CKS *cks, DCC_CKS_WTGTS wtgts,
+	      u_char have_wlist)	/* 1=have whiteclnt result */
+{
+	char tgts_buf[16], type_buf[26], cbuf[DCC_CK2STR_LEN];
+#	define LINE_LEN 81
+	char buf[LINE_LEN*6];
+	const DCC_GOT_SUM *g;
+	u_char have_server, have_thold, headed;
+	DCC_TGTS tgts;
+	int cklen, buflen, inx, i;
+
+	/* decide which column headings are needed */
+	have_server = 0;
+	have_thold = 0;
+	for (g = cks->sums, inx = 0; g <= LAST(cks->sums); ++g, ++inx) {
+		if (g->type == DCC_CK_INVALID)
+			continue;
+		if (g->tgts != DCC_TGTS_INVALID)
+			have_server = 1;
+		if (wtgts[inx] != 0)
+			have_wlist = 1;
+		if (cks->tholds_rej[g->type] != DCC_THOLD_UNSET
+		    && g->type != DCC_CK_REP_TOTAL)
+			have_thold = 1;
+	}
+	if (have_wlist)
+		have_thold = 0;
+
+	headed = 0;
+	buflen = 0;
+	for (g = cks->sums, inx = 0; g <= LAST(cks->sums); ++g, ++inx) {
+		if (g->type == DCC_CK_INVALID)
+			continue;
+
+		if (!headed) {
+			headed = 1;
+			dcc_tgts2str(tgts_buf, sizeof(tgts_buf)-LITZ("spam"),
+				     local_tgts, 0);
+			if (is_spam)
+				STRLCAT(tgts_buf, " spam", sizeof(tgts_buf));
+			buflen += snprintf(buf+buflen, sizeof(buf)-buflen,
+					   "                            "
+					   DCC_XHDR_REPORTED" %-15s checksum",
+					   tgts_buf);
+
+			if (have_server || have_wlist || have_thold)
+				buflen += snprintf(buf+buflen,
+						   sizeof(buf)-buflen,
+						   PRINT_CK_PAT_SRVR,
+						   have_server ? "server" : "");
+			if (have_wlist)
+				buflen += snprintf(buf+buflen,
+						   sizeof(buf)-buflen,
+						   PRINT_CK_PAT_WLIST,
+						   "wlist");
+			else if (have_thold)
+				buflen += snprintf(buf+buflen,
+						   sizeof(buf)-buflen,
+						   PRINT_CK_PAT_THOLD,
+						   "thold");
+			if (ISZ(buf)-buflen > 1)
+				buf[buflen++] = '\n';
+
+		} else if (buflen >= ISZ(buf)-LINE_LEN) {
+			out(arg, buf, buflen);
+			buflen = 0;
+		}
+
+		cklen = snprintf(buf+buflen, sizeof(buf)-buflen,
+				 PRINT_CK_PAT_CK,
+				 dcc_type2str(type_buf, sizeof(type_buf),
+					      g->type, g->hdr_nm, 0, 0),
+				 dcc_ck2str(cbuf, sizeof(cbuf),
+					    g->type, g->sum, 0));
+		buflen += cklen;
+
+		if (g->rpt2srvr != 0 && g->tgts != DCC_TGTS_INVALID) {
+			if (buflen < ISZ(buf))
+				buflen += snprintf(buf+buflen,
+						   sizeof(buf)-buflen,
+						   PRINT_CK_PAT_SRVR,
+						   dcc_tgts2str(tgts_buf,
+							sizeof(tgts_buf),
+							g->tgts, 0));
+		} else if (wtgts[inx] != 0
+			   || (have_thold
+			       && cks->tholds_rej[g->type]!=DCC_THOLD_UNSET)) {
+			/* steal space from blank server count for
+			 * long substitute checksums */
+			i = (PRINT_CK_PAT_SRVR_LEN - (cklen - (PRINT_CK_TYPE_LEN
+							+2 +PRINT_CK_SUM_LEN)));
+			if (i > PRINT_CK_PAT_SRVR_LEN)
+				i = PRINT_CK_PAT_SRVR_LEN;
+			if (i > ISZ(buf)-buflen)
+				i = ISZ(buf)-buflen;
+			if (i < 0)
+				i = 0;
+			if (i > ISZ(buf))
+				i = ISZ(buf);
+			while (--i >= 0) {
+				buf[buflen++] = ' ';
+			}
+		}
+
+		if (buflen >= ISZ(buf)) {
+			;
+		} else if (wtgts[inx] != 0) {
+			buflen += snprintf(buf+buflen, sizeof(buf)-buflen,
+					   PRINT_CK_PAT_WLIST,
+					   wtgts[inx] == 0
+					   ? ""
+					   : dcc_tgts2str(tgts_buf,
+							sizeof(tgts_buf),
+							wtgts[inx], 0));
+		} else if (have_thold
+			   && (tgts = cks->tholds_rej[g->type]
+			       ) != DCC_THOLD_UNSET) {
+			buflen += snprintf(buf+buflen, sizeof(buf)-buflen,
+					   PRINT_CK_PAT_THOLD,
+					   dcc_thold2str(tgts_buf,
+							sizeof(tgts_buf),
+							g->type, tgts));
+		}
+
+		if (buflen >= ISZ(buf)-1)
+			buflen = sizeof(buf)-2;
+		buf[buflen] = '\n';
+		buf[++buflen] = '\0';
+	}
+
+	if (buflen != 0)
+		out(arg, buf, buflen);
+
+#undef LINE_LEN
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/ck2str.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/ck2str.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,121 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.25 $Revision$
+ */
+
+#include "dcc_defs.h"
+
+char *
+dcc_ck2str(char *buf, u_int buf_len,
+	   DCC_CK_TYPES type, const DCC_SUM sum, u_int32_t extra)
+{
+	u_int32_t n[sizeof(DCC_SUM)+3/4];
+	u_char c;
+	char *p;
+	const char *type_str;
+	char type_buf[32];
+	int i;
+
+	/* checksums other than server-ID declarations are easy */
+	if (type != DCC_CK_SRVR_ID) {
+		memcpy(n, sum, sizeof(n));
+		snprintf(buf, buf_len, "%08x %08x %08x %08x",
+			 (int)ntohl(n[0]), (int)ntohl(n[1]),
+			 (int)ntohl(n[2]), (int)ntohl(n[3]));
+		return buf;
+	}
+
+	/* decode server sanity declarations */
+	if (sum[0] == DCC_CK_SRVR_ID) {
+		if (extra == 0)
+			type_str = "server";
+		else
+			type_str = id2str(type_buf, sizeof(type_buf), extra);
+		snprintf(buf, buf_len, "%s at %d",
+			 type_str, (sum[1]<<8)+sum[2]);
+		return buf;
+	}
+
+	/* decode the string from server-ID declaration */
+	p = buf;
+	if (buf_len == 0)
+		return buf;
+	*p++ = '"';
+	--buf_len;
+	for (i = 0; i < ISZ(DCC_SUM); ++i) {
+		if (buf_len == 0)
+			return buf;
+		c = sum[i];
+		if (c == '\0')
+			break;
+		if (c >= ' ' && c < 0x7f) {
+			/* simple ASCII is easy */
+			*p++ = c;
+			--buf_len;
+			continue;
+		}
+
+		/* convert non-ASCII to an escape sequence */
+		*p++ = '\\';
+		if (--buf_len == 0)
+			return buf;
+		*p++ = '0'+(c>>6);
+		if (--buf_len == 0)
+			return buf;
+		*p++ = '0'+((c>>3) & 7);
+		if (--buf_len == 0)
+			return buf;
+		*p++ = '0'+(c & 7);
+		--buf_len;
+	}
+	*p++ = '"';
+	--buf_len;
+	if (--buf_len != 0)
+		*p = '\0';
+	return buf;
+}
+
+
+
+/* this is not thread safe */
+const char *
+dcc_ck2str_err(DCC_CK_TYPES type, const DCC_SUM sum, u_int32_t extra)
+{
+	static char ck_buf[DCC_CK2STR_LEN];
+
+	return dcc_ck2str(ck_buf, sizeof(ck_buf), type, sum, extra);
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/ckbody.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/ckbody.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,435 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * compute simple body checksum
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.56 $Revision$
+ */
+
+#include "dcc_ck.h"
+
+
+void
+dcc_ck_body0(DCC_GOT_CKS *cks, const char *bp, u_int bp_len)
+{
+#	define BUF_LEN 1024
+	char buf[BUF_LEN+5];
+	u_char flen;
+	int blen;
+	char c;
+
+	if (cks->sums[DCC_CK_BODY].type != DCC_CK_BODY)
+		return;
+
+	flen = cks->ctx_body.flen;
+	blen = 0;
+	for (;;) {
+		if (bp_len == 0) {
+			if (blen != 0) {
+				cks->ctx_body.total += blen;
+				MD5Update(&cks->ctx_body.md5, buf, blen);
+			}
+			cks->ctx_body.flen = flen;
+			return;
+		}
+		--bp_len;
+		c = *bp++;
+
+		/* Ignore the '>' in the sequence "\n>From" because
+		 * it is sometimes added for old UNIX MUAs.
+		 * As a side effect, ignore '\n' */
+		if (flen != 0) {
+			if (c == "\n>From"[flen]) {
+				if (++flen >= 6) {
+					memcpy(&buf[blen], "From", 4);
+					if ((blen += 4) >= BUF_LEN) {
+					    cks->ctx_body.total += blen;
+					    MD5Update(&cks->ctx_body.md5, buf,
+						      blen);
+					    blen = 0;
+					}
+					flen = 0;
+				}
+				continue;
+			}
+			if (--flen != 0) {
+				memcpy(&buf[blen], ">From", flen);
+				if ((blen += flen) >= BUF_LEN) {
+					cks->ctx_body.total += blen;
+					MD5Update(&cks->ctx_body.md5, buf,
+						  blen);
+					blen = 0;
+				}
+				flen = 0;
+			}
+		}
+		if (c == '\n') {
+			flen = 1;
+			continue;
+		}
+
+		/* Ignore whitespace to avoid being confused by
+		 * varying line endings added and removed by
+		 * various MUAs and MTAs.
+		 * As a side effect, ignore entirely blank messages. */
+		if (c == ' ' || c == '\t' || c == '\r')
+			continue;
+
+		/* Ignore '=' to minimize but not entirely avoid being
+		 * confused by some some sequences that look like
+		 * quoted-printable triples but that are not.
+		 */
+		if (c == '=')
+			continue;
+
+		buf[blen] = c;
+		if (++blen >= BUF_LEN) {
+			cks->ctx_body.total += blen;
+			MD5Update(&cks->ctx_body.md5, buf, blen);
+			blen = 0;
+		}
+	}
+}
+
+
+
+static void
+dcc_ck_body0_fin(DCC_GOT_CKS *cks)
+{
+	/* always generate the MD5 checksum so that grey listing has it */
+	MD5Final(cks->sums[DCC_CK_BODY].sum, &cks->ctx_body.md5);
+
+	if (cks->sums[DCC_CK_BODY].type != DCC_CK_BODY)
+		return;
+
+	if (cks->ctx_body.total < 30) {
+		cks->sums[DCC_CK_BODY].type = DCC_CK_INVALID;
+		return;
+	}
+
+	cks->sums[DCC_CK_BODY].rpt2srvr = 1;
+}
+
+
+
+static void
+decoders_init(DCC_GOT_CKS *cks)
+{
+	cks->mime_bnd_matches = 0;
+
+	cks->flags |= DCC_CKS_MIME_BOL;
+	cks->mime_ct = DCC_CK_CT_TEXT;
+	cks->mime_cset = dcc_cset_1;
+	cks->mime_ce = DCC_CK_CE_ASCII;
+	cks->qp.state = DCC_CK_QP_IDLE;
+	cks->b64.quantum_cnt = 0;
+}
+
+
+
+/* start all of the checksums */
+void
+dcc_cks_init(DCC_GOT_CKS *cks)
+{
+	DCC_GOT_SUM *g;
+
+	for (g = cks->sums; g <= LAST(cks->sums); ++g) {
+		CLR_GOT_SUM(g);
+	}
+
+	cks->flags = 0;
+	cks->mime_nest = 0;
+	cks->mhdr_st = CK_MHDR_ST_IDLE;
+	cks->mp_st = CK_MP_ST_TEXT;
+	decoders_init(cks);
+
+	cks->sums[DCC_CK_BODY].type = DCC_CK_BODY;
+	cks->ctx_body.total = 0;
+	cks->ctx_body.flen = 1;
+	MD5Init(&cks->ctx_body.md5);
+
+	dcc_ck_fuz1_init(cks);
+	dcc_ck_fuz2_init(cks);
+}
+
+
+
+/* decode quoted-printable and base64 and then compute the body checksums */
+static void
+decode_sum(DCC_GOT_CKS *cks, const char *bp, u_int bp_len)
+{
+	char tbuf[1024];
+	const char *tbufp;
+	int len;
+
+	/* Decode quoted-printable and base64 and make fuzzy sumes
+	 * only while in the body of a MIME entity.
+	 * Changing from the text, image, html, etc. requires a '\n'
+	 * to flush the URL and other decoders in the checksummers.
+	 * None of the checksums count whitespace. */
+	if (cks->mp_st != CK_MP_ST_TEXT) {
+		if (bp_len == 0)
+			return;
+#ifdef DCC_DEBUG_CKSUM
+		if (dcc_clnt_debug == 4)
+			write(1, bp, bp_len);
+#endif
+		dcc_ck_body0(cks, bp, bp_len);
+		dcc_ck_fuz1(cks, "\n", 1);
+		dcc_ck_fuz2(cks, "\n", 1);
+		return;
+	}
+
+	while (bp_len != 0) {
+		switch (cks->mime_ce) {
+		case DCC_CK_CE_ASCII:
+		default:
+			len = bp_len;
+			tbufp = bp;
+			bp_len = 0;
+			break;
+		case DCC_CK_CE_QP:
+			tbufp = tbuf;
+			len = dcc_ck_qp_decode(cks, &bp, &bp_len,
+					       tbuf, sizeof(tbuf));
+			break;
+		case DCC_CK_CE_B64:
+			tbufp = tbuf;
+			len = dcc_ck_b64_decode(cks, &bp, &bp_len,
+						tbuf, sizeof(tbuf));
+			break;
+		}
+
+		if (len != 0) {
+#ifdef DCC_DEBUG_CKSUM
+			if (dcc_clnt_debug == 4)
+				write(1, tbufp, len);
+#endif
+			dcc_ck_body0(cks, tbufp, len);
+			dcc_ck_fuz1(cks, tbufp, len);
+			if (cks->mime_ct != DCC_CK_CT_BINARY)
+				dcc_ck_fuz2(cks, tbufp, len);
+		}
+	}
+}
+
+
+
+/* compute all of the body checksums on a chunk of raw text */
+void
+dcc_ck_body(DCC_GOT_CKS *cks, const void *bp, u_int bp_len)
+{
+	DCC_CK_BND *bndp;
+	const char *sum;		/* 1st input byte not swallowed */
+	const char *cmp;		/* 1st not parsed for MIME */
+	const char *cp;
+	char c;
+	int len, matched_len, i, j;
+
+	sum = bp;
+	cmp = sum;
+	while (bp_len != 0) {
+		/* if we have no multipart hassles
+		 * then pass buffer to qp/base64 decoder and quit */
+		if (cks->mime_nest == 0) {
+			decode_sum(cks, sum, bp_len);
+			return;
+		}
+
+		/* look for start of next line to start matching boundaries */
+		if (cks->mime_bnd_matches == 0) {
+			cp = memchr(cmp, '\n', bp_len);
+			if (!cp) {
+				cp = cmp+bp_len;
+			} else {
+				++cp;
+			}
+
+			/* look for a MIME entity header in the text before
+			 * the next line and possible start of a boundary */
+			i = cp - cmp;
+			if (cks->mp_st == CK_MP_ST_HDRS) {
+				if (parse_mime_hdr(cks, cmp, i, 0)) {
+					/* blank header line ends the headers */
+					j = cp-sum;
+					if (j) {
+					    decode_sum(cks, sum, j);
+					    sum = cp;
+					}
+					cks->mp_st = CK_MP_ST_TEXT;
+				}
+			}
+			/* We found the end of a line.  Reset positions to
+			 * start looking for a MIME boundary after it */
+			if (*(cp-1) == '\n') {
+				cks->flags |= DCC_CKS_MIME_BOL;
+				cks->mime_bnd_matches = cks->mime_nest;
+				for (bndp = cks->mime_bnd;
+				     bndp <= LAST(cks->mime_bnd);
+				     ++bndp) {
+					bndp->cmp_len = 0;
+				}
+			}
+			cmp = cp;
+			if ((bp_len -= i) == 0)
+				break;
+		}
+
+		/* look for (rest of) one of the active MIME boundaries */
+		matched_len = 0;
+		for (bndp = cks->mime_bnd;
+		     bndp < &cks->mime_bnd[cks->mime_nest];
+		     ++bndp) {
+
+			if (bndp->cmp_len == DCC_CK_BND_MISS)
+				continue;   /* already mismatched boundary */
+
+			j = bndp->bnd_len - bndp->cmp_len;
+			len = bp_len;
+			if (j > len)
+				j = len;
+			cp = cmp;
+			if (j > 0) {
+				if (memcmp(cp, &bndp->bnd[bndp->cmp_len], j)) {
+					bndp->cmp_len = DCC_CK_BND_MISS;
+					--cks->mime_bnd_matches;
+					continue;
+				}
+				/* this boundary matches so far */
+				bndp->cmp_len += j;
+				cp += j;
+				if ((len -= j) <= 0) {
+					matched_len = bp_len;
+					continue;
+				}
+				/* since we did not exhaust len, we know
+				 * we matched the entire boundary */
+				j = 0;
+			}
+
+			/* look for 1st '-' of trailing "--" */
+			if (j == 0
+			    && *cp == '-') {
+				++bndp->cmp_len;
+				if (--len <= 0) {
+					matched_len = bp_len;
+					continue;
+				}
+				++cp;
+				j = -1;
+			}
+			/* look for 2nd '-' of trailing "--" */
+			if (j == -1) {
+				if (*cp == '-') {
+					++bndp->cmp_len;
+					if (--len <= 0) {
+					    matched_len = bp_len;
+					    continue;
+					}
+					++cp;
+				} else {
+					bndp->cmp_len = DCC_CK_BND_MISS;
+					--cks->mime_bnd_matches;
+					continue;
+				}
+			}
+			/* check for trailing whitespace & '\n' */
+			if ((c = *cp) == ' ' || c == '\t' || c == '\r') {
+				do {
+					++cp;
+				} while (--len > 0
+					 && ((c = *cp) == ' ' || c == '\t'
+					     || c == '\r'));
+				if (len <= 0) {
+					matched_len = bp_len;
+					continue;
+				}
+			}
+			if (*cp != '\n') {
+				/* mismatch after the end of the boundary */
+				bndp->cmp_len = DCC_CK_BND_MISS;
+				--cks->mime_bnd_matches;
+				continue;
+			}
+
+			/* We have found a MIME boundary.
+			 * Flush b64 & qp decoders and fuzzy checksummers */
+			j = cmp-sum;
+			if (j)
+				decode_sum(cks, sum, j);
+
+			/* pass the boundary in the buffer */
+			matched_len = ++cp - cmp;
+			cmp = sum = cp;
+
+			/* Body checksum the boundary */
+			cks->mp_st = CK_MP_ST_BND;
+			decode_sum(cks, bndp->bnd, bndp->bnd_len);
+			if (bndp->cmp_len != bndp->bnd_len) {
+				/* checksum trailing "--" of final boundary */
+				decode_sum(cks, "--", 2);
+				/* end the current & inner entities */
+				cks->mp_st = CK_MP_ST_EPILOGUE;
+			} else {
+				/* intermediate boundaries end inner entities */
+				cks->mp_st = CK_MP_ST_HDRS;
+				++bndp;
+			}
+			cks->mime_nest = bndp - cks->mime_bnd;
+			decoders_init(cks);
+			break;
+		}
+		bp_len -= matched_len;
+	}
+
+	j = cmp-sum;
+	if (j)
+		decode_sum(cks, sum, j);
+}
+
+
+
+/* finish all of the body checksums */
+void
+dcc_cks_fin(DCC_GOT_CKS *cks)
+{
+	dcc_ck_fuz1(cks, "\n", 1);	/* flush URL decoders & line buffers */
+	dcc_ck_fuz2(cks, "\n", 1);
+
+	dcc_ck_body0_fin(cks);
+	dcc_ck_fuz1_fin(cks);
+	dcc_ck_fuz2_fin(cks);
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/ckfuz1.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/ckfuz1.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,243 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * compute fuzzy body checksum #1
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.59 $Revision$
+ */
+
+#include "dcc_ck.h"
+
+#define FZ1  cks->fuz1
+
+#define MAX_FUZ1_LEN	(4*1024)
+
+
+void
+dcc_ck_fuz1_init(DCC_GOT_CKS *cks)
+{
+	cks->sums[DCC_CK_FUZ1].type = DCC_CK_FUZ1;
+	FZ1.total = 0;			/* bytes summed */
+	FZ1.eol = FZ1.cp = FZ1.buf;
+	FZ1.url.st = DCC_URL_ST_IDLE;
+
+	MD5Init(&FZ1.md5);
+}
+
+
+
+static inline u_char			/* 0=keep the line, 1=discard it */
+dear_sucker(const char *cp, u_int llen)
+{
+#define CK_WORD(w) (llen >= sizeof(w) && !strncmp(cp, w, LITZ(w)))
+
+	if (CK_WORD("dear"))
+		return 1;
+	if (CK_WORD("hello"))
+		return 1;
+	if (CK_WORD("greeting"))
+		return 1;
+	if (CK_WORD("date"))
+		return 1;
+
+	return 0;
+#undef CKWORD
+}
+
+
+
+static inline u_char
+add_sum(DCC_GOT_CKS *cks, int len)
+{
+	int i;
+
+	if (!len)
+		return 1;
+
+	/* ignore the end of very long spam, since
+	 * it is likely to make the checksum differ */
+	i = MAX_FUZ1_LEN - (FZ1.total + len);
+	if (i < 0)
+		len += i;
+	MD5Update(&FZ1.md5, FZ1.buf, len);
+	return (FZ1.total += len) < MAX_FUZ1_LEN;
+}
+
+
+
+void
+dcc_ck_fuz1(DCC_GOT_CKS *cks, const char *bp, u_int bp_len)
+{
+	char *cp;
+	DNSBL_WORK *blw;
+	int i, len, c;
+
+	if (cks->sums[DCC_CK_FUZ1].type != DCC_CK_FUZ1)
+		return;
+
+	if (FZ1.total >= MAX_FUZ1_LEN)
+		return;
+
+	cp = FZ1.cp;
+
+	for (;;) {
+		if (bp_len == 0) {
+			/* Sum the buffer if it ends with a line.  Note that
+			 * every message always ends with an artificial "\n". */
+			if (FZ1.eol == cp) {
+				add_sum(cks, cp - FZ1.buf);
+				FZ1.eol = cp = FZ1.buf;
+			}
+			FZ1.cp = cp;
+			return;
+		}
+		--bp_len;
+		c = *bp++;
+
+		i = dcc_ck_url(&FZ1.url, c, &cp);
+		c = i>>DCC_CK_URL_SHIFT;
+		switch ((DCC_CK_URL)(i & DCC_CK_URL_MASK)) {
+		case DCC_CK_URL_CHAR:
+			break;
+		case DCC_CK_URL_CK_LEN:
+			/* Make room before starting a URL
+			 * if we are too close to the end of
+			 * the buffer for a maximum size URL */
+			if (cp >= &FZ1.buf[sizeof(FZ1.buf)-DCC_URL_MAX]) {
+				if (!FZ1.eol
+				    || FZ1.eol < cp-DCC_FUZ1_MAX_LINE) {
+					if (!add_sum(cks, cp - FZ1.buf))
+					    return;
+					FZ1.eol = 0;
+					cp = FZ1.buf;
+				} else {
+					len = FZ1.eol - FZ1.buf;
+					if (!add_sum(cks, len))
+					    return;
+					memmove(FZ1.buf, FZ1.eol, cp - FZ1.eol);
+					FZ1.eol = FZ1.buf;
+					cp -= len;
+				}
+			}
+			blw = cks->dnsbl;
+			if (blw != 0) {
+				blw->tgt.dom.c[0] = '\0';
+				blw->tgt_dom_len = 0;
+			}
+			continue;
+		case DCC_CK_URL_HOST:
+		case DCC_CK_URL_DOT:
+			blw = cks->dnsbl;
+			if (blw != 0
+			    && blw->unhit.url != 0
+			    && blw->tgt_dom_len < ISZ(blw->tgt.dom))
+				blw->tgt.dom.c[blw->tgt_dom_len++] = c;
+			break;
+		case DCC_CK_URL_HOST_END:
+			blw = cks->dnsbl;
+			if (blw && blw->tgt_dom_len > 0
+			    && blw->tgt_dom_len < ISZ(blw->tgt.dom)) {
+				blw->tgt.dom.c[blw->tgt_dom_len] = '\0';
+				url_dnsbl(blw);
+			}
+			break;
+		case DCC_CK_URL_HOST_RESET:
+			blw = cks->dnsbl;
+			if (blw != 0) {
+				blw->tgt.dom.c[0] = '\0';
+				blw->tgt_dom_len = 0;
+			}
+			break;
+		case DCC_CK_URL_SKIP:
+			continue;
+		}
+
+		/* collect only ASCII letters */
+		if (c >= 'a' && c <= 'z') {
+			/* Collect more of a new line */
+			*cp = c;
+			if (++cp < &FZ1.buf[sizeof(FZ1.buf)])
+				continue;
+
+			/* We are at the end of the buffer,
+			 * so add it to the checksum */
+			if (!add_sum(cks, cp - FZ1.buf))
+				return;
+			cp = FZ1.buf;
+			FZ1.eol = 0;
+			continue;
+		}
+
+		if (c == '\n') {
+			/* Ignore short lines starting with some strings */
+			if (FZ1.eol
+			    && (len = cp - FZ1.eol) > 0
+			    && len <= DCC_FUZ1_MAX_LINE
+			    && dear_sucker(FZ1.eol, len)) {
+				cp = FZ1.eol;
+				continue;
+			}
+
+			/* Add the line to the checksum if we do not
+			 * have room in the buffer for another line */
+			if (cp >= &FZ1.buf[sizeof(FZ1.buf) - (DCC_FUZ1_MAX_LINE
+							+ DCC_HTTPS_LEN)]) {
+				if (!add_sum(cks, cp - FZ1.buf))
+					return;
+				cp = FZ1.buf;
+			}
+			FZ1.eol = cp;
+		}
+	}
+}
+
+
+
+void
+dcc_ck_fuz1_fin(DCC_GOT_CKS *cks)
+{
+	if (cks->sums[DCC_CK_FUZ1].type != DCC_CK_FUZ1)
+		return;
+
+	/* we cannot compute a checksum on an empty or nearly empty message */
+	if (FZ1.total < 30) {
+		cks->sums[DCC_CK_FUZ1].type = DCC_CK_INVALID;
+		return;
+	}
+
+	MD5Final(cks->sums[DCC_CK_FUZ1].sum, &FZ1.md5);
+	cks->sums[DCC_CK_FUZ1].rpt2srvr = 1;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/ckfuz2.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/ckfuz2.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,590 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * compute fuzzy body checksum #2
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.52 $Revision$
+ */
+
+#include "dcc_ck.h"
+
+#include "ckfuz2_tbl.h"
+struct {
+    const char **words;
+    u_int	len;
+    const u_char *cset;
+} tbls[FUZ2_LAN_NUM] = {
+    {word_tbl0, word_tbl0_LEN, 0},
+    {word_tbl1, word_tbl1_LEN, 0},
+    {word_tbl2, word_tbl2_LEN, dcc_cset_2},
+};
+
+
+#define FZ2 cks->fuz2
+
+#define BUF_LEN 1024
+typedef struct {
+    char buf[BUF_LEN+sizeof(DCC_FUZ2_WORD)+1];
+    int blen;
+} LBUF;
+
+
+#ifdef DCC_DEBUG_CKSUM
+#define FUZ2(lp, b, l) (dcc_clnt_debug == 5				\
+			? (write(1, b, l), MD5Update(&lp->md5, b, l))	\
+			: MD5Update(&lp->md5, b, l))
+#else
+#define FUZ2(lp, b, l) MD5Update(&lp->md5, b, l)
+#endif
+
+
+void
+dcc_ck_fuz2_init(DCC_GOT_CKS *cks)
+{
+	FUZ2_LANG *lp;
+
+	FZ2.wlen = 0;
+	DCC_FUZ2_WORD_CLEAR(&FZ2.w);
+	FZ2.st = DCC_FUZ2_ST_WORD;
+	FZ2.url_cp = FZ2.url_buf;
+	FZ2.urls = 0;
+
+	cks->sums[DCC_CK_FUZ2].type = DCC_CK_FUZ2;
+
+	FZ2.btotal = 0;
+	FZ2.xsummed = 0;
+	for (lp = FZ2.lang; lp <= LAST(FZ2.lang); ++lp) {
+		lp->wsummed = 0;
+		lp->wtotal = 0;
+		MD5Init(&lp->md5);
+	}
+}
+
+
+
+static inline u_char			/* 1=found it, 0=not a known word */
+lookup_word(const DCC_FUZ2_WORD *w, u_int wlen,
+	    const char **word_tbl, u_int word_tbl_len)
+{
+	const char *p;
+	u_int n;
+
+	p = word_tbl[fuz2_word_hash(w, word_tbl_len)];
+	if (!p)
+		return 0;
+	for (;;) {
+		n = *p++;
+		if (!n)
+			return 0;
+		if (n == wlen && !memcmp(w->b, p, n))
+			return 1;
+		p += n;
+	}
+}
+
+
+
+static void
+add_word(DCC_GOT_CKS *cks, LBUF *lbp)
+{
+	FUZ2_LANG *lp;
+	int tbl;
+
+	tbl = 0;
+	for (lp = FZ2.lang; lp < &FZ2.lang[FUZ2_LAN_NUM]; ++lp, ++lbp, ++tbl) {
+		if (tbls[tbl].cset != 0
+		    && tbls[tbl].cset != cks->mime_cset)
+			continue;
+		if (lookup_word(&FZ2.w, FZ2.wlen,
+				tbls[tbl].words, tbls[tbl].len)) {
+			++lp->wtotal;
+			memcpy(&lbp->buf[lbp->blen], &FZ2.w, FZ2.wlen);
+			if ((lbp->blen += FZ2.wlen) >= BUF_LEN) {
+				lp->wsummed += lbp->blen;
+				FUZ2(lp, lbp->buf, lbp->blen);
+				lbp->blen = 0;
+			}
+		}
+	}
+}
+
+
+
+/* resolve an HTML characater reference */
+static u_char				/* 0 or found value */
+lookup_cref(DCC_FUZ2_WORD *w, u_int clen)
+{
+	const char *p;
+	u_int n;
+
+	if (clen > sizeof(DCC_FUZ2_WORD)
+	    || clen == 0)
+		return 0;
+	p = cref_tbl[fuz2_word_hash(w, cref_tbl_LEN)];
+	if (!p)
+		return 0;
+	for (;;) {
+		n = *p++;
+		if (!n)
+			return 0;
+		if (n == clen && !memcmp(w->b, p, n))
+			return p[clen];
+		p += n+1;
+	}
+}
+
+
+
+static int
+adv_cref(DCC_GOT_CKS *cks, u_char c)
+{
+	switch (FZ2.cref_st) {
+	case DCC_CREF_ST_IDLE:
+		dcc_logbad(EX_SOFTWARE, "impossible fuz2 cref state");
+		break;
+
+	case DCC_CREF_ST_START:
+		/* start to get a possible HTML character reference
+		 * We have already passed the '&' */
+		if (c == '#') {
+			FZ2.cref_st = DCC_CREF_ST_NUM;
+		} else if ((c >= 'a' && c <= 'z')
+			   || (c >= 'A' && c <= 'Z')) {
+			DCC_FUZ2_WORD_CLEAR(&FZ2.cref_w);
+			FZ2.cref_w.b[0] = c;
+			FZ2.cref_cnt = 1;
+			FZ2.cref_st = DCC_CREF_ST_NAME;
+		} else {
+			FZ2.cref_st = DCC_CREF_ST_IDLE;
+			return -2;
+		}
+		break;
+
+	case DCC_CREF_ST_NUM:
+		/* look for 'x' or the first digit */
+		if (c == 'x' || c == 'X') {
+			FZ2.cref_st = DCC_CREF_ST_HEX;
+			FZ2.cref_cnt = 0;
+		} else if (c >= '0' && c <= '9') {
+			FZ2.cref_cnt = c - '0';
+			FZ2.cref_st = DCC_CREF_ST_DEC;
+		} else {
+			FZ2.cref_st = DCC_CREF_ST_IDLE;
+			return -2;
+		}
+		break;
+
+	case DCC_CREF_ST_DEC:
+		if (c >= '0' && c <= '9') {
+			FZ2.cref_cnt = FZ2.cref_cnt*10 + (c - '0');
+		} else if (cks->mime_cset[c] == FC_SP) {
+			FZ2.cref_st = DCC_CREF_ST_IDLE;
+			return c == ';';
+		} else {
+			FZ2.cref_st = DCC_CREF_ST_IDLE;
+			return -2;
+		}
+		if (FZ2.cref_cnt > 256)
+			FZ2.cref_cnt = 256;
+		++FZ2.btotal;
+		break;
+
+	case DCC_CREF_ST_HEX:
+		if ((c >= 'a' && c <= 'f')
+		    || (c >= 'A' && c <= 'F')) {
+			FZ2.cref_cnt = (FZ2.cref_cnt<<4) + (c & 0xf) + 9;
+		} else if (c >= '0' && c <= '9') {
+			FZ2.cref_cnt = (FZ2.cref_cnt<<4) + (c - '0');
+		} else if (cks->mime_cset[c] == FC_SP) {
+			FZ2.cref_st = DCC_CREF_ST_IDLE;
+			return c == ';';
+		} else {
+			FZ2.cref_st = DCC_CREF_ST_IDLE;
+			return -2;
+		}
+		if (FZ2.cref_cnt > 256)
+			FZ2.cref_cnt = 256;
+		++FZ2.btotal;
+		break;
+
+	case DCC_CREF_ST_NAME:
+		if ((c >= 'a' && c <= 'z')
+		    || (c >= 'A' && c <= 'Z')) {
+			/* If the word is too long, the final match will fail.
+			 * This will consume it */
+			if (FZ2.cref_cnt < sizeof(DCC_FUZ2_WORD))
+				FZ2.cref_w.b[FZ2.cref_cnt++] = c;
+		} else if (cks->mime_cset[c] == FC_SP) {
+			/* this character ends the cref */
+			FZ2.cref_cnt = lookup_cref(&FZ2.cref_w, FZ2.cref_cnt);
+			FZ2.cref_st = DCC_CREF_ST_IDLE;
+			return c == ';';
+		} else {
+			FZ2.cref_st = DCC_CREF_ST_IDLE;
+			return -2;
+		}
+		break;
+	}
+	return -1;
+}
+
+
+void
+dcc_ck_fuz2(DCC_GOT_CKS *cks, const char *bp, u_int bp_len)
+{
+#define SKIP_WORD() (FZ2.wlen = sizeof(DCC_FUZ2_WORD)+1)
+#define JUNK() (SKIP_WORD(), FZ2.st = DCC_FUZ2_ST_WORD)
+	LBUF *lbp, lbufs[FUZ2_LAN_NUM];
+	FUZ2_LANG *lp;
+	int i;
+	char *p;
+	u_char c;
+
+	if (cks->sums[DCC_CK_FUZ2].type != DCC_CK_FUZ2)
+		return;
+
+	for (lbp = lbufs; lbp <= LAST(lbufs); ++lbp)
+		lbp->blen = 0;
+
+	while (bp_len != 0) {
+		switch (FZ2.st) {
+		case DCC_FUZ2_ST_WORD:
+			/* gathering a word */
+			do {
+				if (FZ2.cref_st == DCC_CREF_ST_IDLE) {
+					--bp_len;
+					c = *bp++;
+					c = cks->mime_cset[c];
+					if (c == FC_CF) {
+					    if (cks->mime_ct == DCC_CK_CT_HTML){
+						FZ2.cref_st = DCC_CREF_ST_START;
+						break;
+					    }
+					    c = FC_SP;
+					}
+				} else {
+					i = adv_cref(cks, *bp);
+					if (i == -2) {
+					    JUNK(); /* bogus cref */
+					    continue;
+					}
+					if (i < 0) {	/* get more of cref */
+					    --bp_len;
+					    ++bp;
+					    continue;
+					}
+					bp += i;    /* use complete cref */
+					bp_len -= i;
+					c = cks->mime_cset[FZ2.cref_cnt];
+
+					if (c == FC_SK)
+					    continue;   /* ignore accent mark */
+				}
+
+				if (c >= FC_A) {
+					++FZ2.btotal;
+					if (FZ2.wlen < sizeof(DCC_FUZ2_WORD))
+					    FZ2.w.b[FZ2.wlen++] = c;
+					else
+					    SKIP_WORD();
+					continue;
+				}
+
+				if (c == FC_SP) {
+					if (FZ2.wlen >= MIN_WLEN
+					    && FZ2.wlen <=sizeof(DCC_FUZ2_WORD))
+					    add_word(cks, lbufs);
+					FZ2.wlen = 0;
+					DCC_FUZ2_WORD_CLEAR(&FZ2.w);
+					continue;
+				}
+				++FZ2.btotal;
+
+				if (c == FC_LT) {
+					FZ2.tag_len = 0;
+					DCC_FUZ2_WORD_CLEAR(&FZ2.tag);
+					FZ2.st = DCC_FUZ2_ST_START_TAG;
+					break;
+				}
+
+				JUNK();
+			} while (bp_len != 0);
+			break;
+
+		case DCC_FUZ2_ST_START_TAG:
+			/* collecting an HTML tag or comment
+			 * We've passed the '<' */
+			c = *bp;
+#define SAVE_TAG(_c) (FZ2.tag.b[FZ2.tag_len++] = _c, \
+		      ++FZ2.btotal, ++bp, --bp_len)
+			if (((c >= 'a' && c <= 'z')	/* tag */
+			     || (c >= '0' && c <= '9'))
+			    && FZ2.tag_len < sizeof(FZ2.tag)) {
+				SAVE_TAG(c);
+				break;
+			}
+			if (c >= 'A' && c <= 'Z'
+			    && FZ2.tag_len < sizeof(FZ2.tag)) {
+				SAVE_TAG(c - ('A'-'a'));
+				break;
+			}
+			if ((c == '/'	/* end-tag */
+			     || c == '!')   /* start of comment */
+			    && FZ2.tag_len == 0) {
+				SAVE_TAG(c);
+				break;
+			}
+			if (c == '-'    /* comment */
+			    && FZ2.tag_len >= 1 && FZ2.tag_len <= 2) {
+				SAVE_TAG(c);
+				break;
+			}
+#undef SAVE_TAG
+
+			/* notice an <html> tag while in text/plain
+			 * and switch to text/html */
+			if (FZ2.tag_len == 4
+			    && cks->mime_ct != DCC_CK_CT_HTML
+			    && !memcmp(FZ2.tag.b, "html", 4))
+				cks->mime_ct = DCC_CK_CT_HTML;
+
+			if (cks->mime_ct == DCC_CK_CT_HTML
+			    && FZ2.tag_len > 0) {
+				/* if we are in an HTML document and we
+				 * have at least one character after '<',
+				 * assume it is some kind of HTML tag */
+				FZ2.xsummed += FZ2.tag_len+1;	/* count '<' */
+				if (c == '>') {
+					/* optimize common simple tags */
+					++FZ2.xsummed;
+					++FZ2.btotal;
+					++bp, --bp_len;
+					FZ2.st = DCC_FUZ2_ST_WORD;
+					break;
+				}
+				if (FZ2.tag_len >= 3
+				    && !memcmp(FZ2.tag.b, "!--", 3)) {
+					FZ2.st = DCC_FUZ2_ST_SKIP_COMMENT;
+				} else {
+					FZ2.url.st = DCC_URL_ST_IDLE;
+					FZ2.st = DCC_FUZ2_ST_SKIP_TAG;
+				}
+			} else {
+				/* assume it is not an HTML tag and
+				 * mark the whole word as junk */
+				JUNK();
+			}
+			break;
+
+		case DCC_FUZ2_ST_SKIP_TAG:
+			/* Skip rest of boring HTML tag
+			 * We ought to ignore '>' in quotes */
+			do {
+				if (FZ2.cref_st == DCC_CREF_ST_IDLE) {
+					--bp_len;
+					c = *bp++;
+					if (c == FC_CF) {
+					    FZ2.cref_st = DCC_CREF_ST_START;
+					    continue;
+					}
+				} else {
+					i = adv_cref(cks, *bp);
+					if (i == -2)
+					    continue;
+					if (i < 0) {	/* get more of cref */
+					    --bp_len;
+					    ++bp;
+					    continue;
+					}
+					bp += i;    /* use complete cref */
+					bp_len -= i;
+					c = FZ2.cref_cnt;
+				}
+
+				/* capture URLs */
+				i = dcc_ck_url(&FZ2.url, c, &FZ2.url_cp);
+				c = i>>DCC_CK_URL_SHIFT;
+				switch ((DCC_CK_URL)(i & DCC_CK_URL_MASK)) {
+				case DCC_CK_URL_CHAR:
+					break;
+				case DCC_CK_URL_CK_LEN:
+					/* Make room before starting a URL
+					 * if we are too close to
+					 * end of buffer for a maximum size URL.
+					 * Discard the first URL in the buffer.
+					 * This relies on dcc_ck_url() limiting
+					 * the URL to DCC_URL_MAX bytes */
+					while (FZ2.url_cp
+					       >= &FZ2.url_buf[ISZ(FZ2.url_buf)
+							- DCC_FUZ2_URL_MAX]) {
+					    p = memchr(FZ2.url_buf, '\0',
+						       FZ2.url_cp-FZ2.url_buf);
+					    if (!p) {
+						/* if this was the first URL,
+						 * discard half of it */
+						p = &FZ2.url_buf[DCC_URL_MAX/2];
+					    } else {
+						++p;
+					    }
+					    memmove(FZ2.url_buf, p,
+						    FZ2.url_cp - p);
+					    FZ2.url_cp -= p - FZ2.url_buf;
+					}
+					if (FZ2.url_cp != FZ2.url_buf)
+					    *FZ2.url_cp++ = '\0';
+					++FZ2.urls;
+					break;
+				case DCC_CK_URL_HOST:
+				case DCC_CK_URL_DOT:
+					if (FZ2.url_cp
+					    < &FZ2.url_buf[ISZ(FZ2.url_buf)])
+					    *FZ2.url_cp++ = c;
+					continue;
+				case DCC_CK_URL_HOST_END:
+				case DCC_CK_URL_HOST_RESET:
+				case DCC_CK_URL_SKIP:
+					continue;
+				}
+
+				if (c == '>') {
+					++FZ2.xsummed;
+					++FZ2.btotal;
+					FZ2.st = DCC_FUZ2_ST_WORD;
+					break;
+				}
+				if (cks->mime_cset[c] != FC_SP) {
+					++FZ2.xsummed;
+					++FZ2.btotal;
+					/* don't let wild tags run forever */
+					if (++FZ2.tag_len > DCC_URL_FAILSAFE) {
+					    JUNK();
+					    break;
+					}
+				}
+			} while (bp_len != 0);
+			break;
+
+		case DCC_FUZ2_ST_SKIP_COMMENT:
+			/* HTML comments can include HTML tags,
+			 * but spammers don't understand HTML comment syntax
+			 * and Netscape and IE treat (and ignore) broken
+			 * comments like strange tags. */
+			do {
+				--bp_len;
+				c = *bp++;
+				if (c == '>') {
+					++FZ2.xsummed;
+					++FZ2.btotal;
+					FZ2.st = DCC_FUZ2_ST_WORD;
+					break;
+				}
+				if (cks->mime_cset[c] != FC_SP) {
+					++FZ2.xsummed;
+					++FZ2.btotal;
+					/* don't let wild tags run forever */
+					if (++FZ2.tag_len > DCC_URL_FAILSAFE) {
+					    JUNK();
+					    break;
+					}
+				}
+			} while (bp_len != 0);
+			break;
+		}
+	}
+	for (lbp = lbufs, lp = FZ2.lang; lbp <= LAST(lbufs); ++lbp, ++lp) {
+		if (lbp->blen != 0) {
+			lp->wsummed += lbp->blen;
+			FUZ2(lp, lbp->buf, lbp->blen);
+		}
+	}
+#undef SKIP_WORD
+#undef JUNK
+#undef BUF_LEN
+}
+
+
+
+void
+dcc_ck_fuz2_fin(DCC_GOT_CKS *cks)
+{
+	FUZ2_LANG *lp, *lp1;
+
+	if (cks->sums[DCC_CK_FUZ2].type != DCC_CK_FUZ2)
+		return;
+
+	/* pick the language checksum of the most words */
+	lp = FZ2.lang;
+	for (lp1 = lp+1; lp1 <= LAST(FZ2.lang); ++lp1) {
+		if (lp->wtotal < lp1->wtotal)
+			lp = lp1;
+	}
+
+#ifdef DCC_DEBUG_CKSUM
+	if (dcc_clnt_debug > 3)
+		printf("\n***fuz2: wtotal[%d]=%d summed=%d+%d btotal=%d\n",
+		       (int)(lp-FZ2.lang),
+		       lp->wtotal, lp->wsummed, FZ2.xsummed, FZ2.btotal);
+#endif
+	/* The FUZ2 checksum is not valid if it is on a few words and
+	 * less than 10% of a big, binary file */
+	if (lp->wtotal < 100
+	    && (lp->wsummed+FZ2.xsummed)*10 < FZ2.btotal) {
+		cks->sums[DCC_CK_FUZ2].type = DCC_CK_INVALID;
+		return;
+	}
+	/* We cannot compute a checksum on a nearly empty message */
+	if (lp->wtotal < 8) {
+		if (lp->wtotal + FZ2.urls*4 >= 8) {
+			/* use URLs if we lack words */
+			FUZ2(lp, FZ2.url_buf, FZ2.url_cp - FZ2.url_buf);
+		} else {
+			/* Compute a checksum for local blacklising on messages
+			 * that look empty to the FUZ2 checksum but are not and
+			 * are not too binary. The resulting checksum is zero.
+			 * Do not report it to the DCC server. */
+			if ((lp->wsummed+FZ2.xsummed) >= 120) {
+				memset(cks->sums[DCC_CK_FUZ2].sum, 0,
+				       sizeof(cks->sums[DCC_CK_FUZ2].sum));
+				return;
+			}
+			cks->sums[DCC_CK_FUZ2].type = DCC_CK_INVALID;
+			return;
+		}
+	}
+
+	MD5Final(cks->sums[DCC_CK_FUZ2].sum, &lp->md5);
+	cks->sums[DCC_CK_FUZ2].rpt2srvr = 1;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/ckfuz2_tbl.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/ckfuz2_tbl.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,5601 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Fuz2 words
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.18 $Revision$
+ */
+
+#define MIN_WLEN	3
+
+static inline u_int
+fuz2_word_hash(const DCC_FUZ2_WORD *w, u_int hash_len)
+{
+	static u_int32_t n;
+	n = w->w32[0] ^ w->w32[1] ^ w->w32[2] ^ w->w32[3];
+	return htonl(n) % hash_len;
+}
+
+/* decide which characters are parts of words */
+#define FC_A    'a'			/* letter in a word */
+#define FC_SP   ' '			/* white space */
+#define FC_JK   '#'			/* not part of a word */
+#define FC_LT   '<'			/* start of HTML tag */
+#define FC_CF   '&'			/* start of HTML character reference */
+#define FC_SK   0			/* ignored punctuation */
+
+/* 8859-1 */
+const DCC_CK_FC dcc_cset_1 = {
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 00-03 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 04-07 */
+    FC_JK,FC_SP,FC_SP,FC_JK,		/* 08-0b   \t and \n */
+    FC_SP,FC_SP,FC_JK,FC_JK,		/* 0c-0f   \f and \r */
+
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 10-13 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 14-17 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 18-1b */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 1c-1f */
+
+    FC_SP,FC_SP,			/* 20-21 sp !  */
+		FC_SP,			/* quot &#34; quotation mark */
+		      FC_JK,		/*    23	# */
+    FC_JK,FC_JK,FC_CF,FC_SP,		/* 24-27  $ % & ' */
+    FC_SP,FC_SP,FC_JK,FC_JK,		/* 28-2b  ( ) * + */
+    FC_SP,FC_JK,FC_SP,FC_JK,		/* 2c-2f  , - . / */
+
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 30-33  0 1 2 3 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 34-37  4 5 6 7 */
+    FC_JK,FC_JK,FC_SP,FC_SP,		/* 38-3b  8 9 : ; */
+    FC_LT,FC_SP,FC_SP,FC_SP,		/* 3c-3f  < = > ? */
+
+    FC_JK,'a',  'b',  'c',		/* 40-43  @ A B C */
+    'd',  'e',  'f',  'g',		/* 44-47  D E F G */
+    'h',  'i',  'j',  'k',		/* 48-4b  H I J K */
+    'l',  'm',  'n',  'o',		/* 4c-4f  L M N O */
+
+    'p',  'q',  'r',  's',		/* 50-53  P Q R S */
+    't',  'u',  'v',  'w',		/* 54-57  T U V W */
+    'x',  'y',  'z',  FC_JK,		/* 58-5b  X Y Z [ */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 5c-5f  \ ] ^ _ */
+
+    FC_JK,'a',  'b',  'c',		/* 60-63  ` a b c */
+    'd',  'e',  'f',  'g',		/* 64-67  d e f g */
+    'h',  'i',  'j',  'k',		/* 68-6b  h i j k */
+    'l',  'm',  'n',  'o',		/* 6c-6f  l m n o */
+
+    'p',  'q',  'r',  's',		/* 70-73  p q r s */
+    't',  'u',  'v',  'w',		/* 74-77  t u v w */
+    'x',  'y',  'z',  FC_JK,		/* 78-7b  x y z { */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 7c-7f  | } ~ del */
+
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 80-83 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 84-87 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 88-8b */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 8c-8f */
+
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 90-93 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 94-97 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 98-9b */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 9c-9f */
+
+    FC_SP,			/* nbsp   &#160; no-break space */
+    FC_SP,			/* iexcl  &#161; inverted ! */
+    FC_JK,			/* cent   &#162; cent sign */
+    FC_JK,			/* pound  &#163; pound sign */
+    FC_JK,			/* curren &#164; currency sign */
+    FC_JK,			/* yen    &#165; yen sign */
+    FC_JK,			/* brvbar &#166; broken bar */
+    FC_JK,			/* sect   &#167; section sign */
+
+    FC_SK,			/* uml    &#168; diaeresis */
+    FC_SP,			/* copy   &#169; copyright sign */
+    FC_JK,			/* ordf   &#170; feminine ordinal indicator */
+    FC_SP,			/* laquo  &#171; << quotation mark */
+    FC_JK,			/* not    &#172; not sign */
+    FC_SK,			/* shy    &#173; soft hyphen */
+    FC_SP,			/* reg    &#174; registered sign */
+    FC_SK,			/* macr   &#175; macron */
+
+    FC_JK,			/* deg    &#176; degree sign */
+    FC_JK,			/* plusmn &#177; plus-minus sign */
+    FC_JK,			/* sup2   &#178; superscript two */
+    FC_JK,			/* sup3   &#179; superscript three */
+    FC_SK,			/* acute  &#180; acute accent */
+    FC_JK,			/* micro  &#181; micro sign */
+    FC_JK,			/* para   &#182; pilcrow sign */
+    FC_JK,			/* middot &#183; middle dot */
+
+    FC_SK,			/* cedil  &#184; cedilla */
+    FC_JK,			/* sup1   &#185; superscript one */
+    FC_JK,			/* ordm   &#186; masculine ordinal indicator */
+    FC_SP,			/* raquo  &#187; >> quotation mark */
+    FC_JK,			/*	    188 */
+    FC_JK,			/*	    189 */
+    FC_JK,			/*	    190 */
+    FC_SP,			/* iquest &#191; inverted question mark */
+
+    224,			/* Agrave &#192; capital A with grave */
+    225,			/* Aacute &#193; capital A with acute */
+    226,			/* Acirc  &#194; capital A with circumflex */
+    227,			/* Atilde &#195; capital A with tilde */
+    228,			/* Auml   &#196; capital A with diaeresis */
+    229,			/* Aring  &#197; capital A with ring above */
+    230,			/* AElig  &#198; capital AE */
+    231,			/* Ccedil &#199; capital C with cedilla */
+
+    232,			/* Egrave &#200; capital E with grave */
+    233,			/* Eacute &#201; capital E with acute */
+    234,			/* Ecirc  &#202; capital E with circumflex */
+    235,			/* Euml   &#203; capital E with diaeresis */
+    236,			/* Igrave &#204; capital I with grave */
+    237,			/* Iacute &#205; capital I with acute */
+    238,			/* Icirc  &#206; capital I with circumflex */
+    239,			/* Iuml   &#207; capital I with diaeresis */
+
+    240,			/* ETH    &#208; capital ETH */
+    241,			/* Ntilde &#209; capital N with tilde */
+    242,			/* Ograve &#210; capital O with grave */
+    243,			/* Oacute &#211; capital O with acute */
+    244,			/* Ocirc  &#212; capital O with circumflex */
+    245,			/* Otilde &#213; capital O with tilde */
+    246,			/* Ouml   &#214; capital O with diaeresis */
+    FC_JK,			/* times  &#215; multiplication sign */
+
+    248,			/* Oslash &#216; capital O with stroke */
+    249,			/* Ugrave &#217; capital U with grave */
+    250,			/* Uacute &#218; capital U with acute */
+    251,			/* Ucirc  &#219; capital U with circumflex */
+    252,			/* Uuml   &#220; capital U with diaeresis */
+    253,			/* Yacute &#221; capital Y with acute */
+    254,			/* THORN  &#222; capital THORN */
+    223,			/* szlig  &#223; small sharp s */
+
+    224,			/* agrave &#224; small a with grave */
+    225,			/* aacute &#225; small a with acute */
+    226,			/* acirc  &#226; small a with circumflex */
+    227,			/* atilde &#227; small a with tilde */
+    228,			/* auml   &#228; small a with diaeresis */
+    229,			/* aring  &#229; small a with ring above */
+    230,			/* aelig  &#230; small ae */
+    231,			/* ccedil &#231; small c with cedilla */
+
+    232,			/* egrave &#232; small e with grave */
+    233,			/* eacute &#233; small e with acute */
+    234,			/* ecirc  &#234; small e with circumflex */
+    235,			/* euml   &#235; small e with diaeresis */
+    236,			/* igrave &#236; small i with grave */
+    237,			/* iacute &#237; small i with acute */
+    238,			/* icirc  &#238; small i with circumflex */
+    239,			/* iuml   &#239; small i with diaeresis */
+
+    240,			/* eth    &#240; small eth */
+    241,			/* ntilde &#241; small n with tilde */
+    242,			/* ograve &#242; small o with grave */
+    243,			/* oacute &#243; small o with acute */
+    244,			/* ocirc  &#244; small o with circumflex */
+    245,			/* otilde &#245; small o with tilde */
+    246,			/* ouml   &#246; small o with diaeresis */
+    FC_JK,			/* divide &#247; division sign */
+
+    248,			/* oslash &#248; small o with stroke */
+    249,			/* ugrave &#249; small u with grave */
+    250,			/* uacute &#250; small u with acute */
+    251,			/* ucirc  &#251; small u with circumflex */
+    252,			/* uuml   &#252; small u with diaeresis */
+    253,			/* yacute &#253; small y with acute */
+    254,			/* thorn  &#254; small thorn */
+    255				/* yuml   &#255; small y with diaeresis */
+};
+
+
+/* These character entities do not have 8-bit values.  They should be
+ * treated as if they were blanks.
+ *  FC_SP			    bull &#8226; bullet = black small circle
+ *  FC_SP			    hellip &#8230; horizontal ellipsis
+ *  FC_SP			    ensp  &#8194; en space
+ *  FC_SP			    emsp  &#8195; em space
+ *  FC_SP			    thinsp &#8201; thin space
+ *  FC_SP			    zwj   &#8205; zero width joiner
+ *  FC_SP			    lsquo &#8216; left single quotation mark
+ *  FC_SP			    rsquo &#8217; right single quotation mark
+ *  FC_SP			    sbquo &#8218; single low-9 quotation mark
+ *  FC_SP			    ldquo &#8220; left double quotation mark
+ *  FC_SP			    rdquo &#8221; right double quotation mark
+ *  FC_SP			    bdquo &#8222; double low-9 quotation mark
+ *  FC_SP			    dagger &#8224; dagger
+ *  FC_SP			    Dagger &#8225; double dagger
+ *  FC_SP			    lsaquo &#8249; single left-pointing angle "
+ *  FC_SP			    rsaquo &#8250; single right-pointing angle "
+ */
+
+
+/* 8859-2 */
+const DCC_CK_FC dcc_cset_2 = {
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 00-03 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 04-07 */
+    FC_JK,FC_SP,FC_SP,FC_JK,		/* 08-0b   \t and \n */
+    FC_SP,FC_SP,FC_JK,FC_JK,		/* 0c-0f   \f and \r */
+
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 10-13 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 14-17 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 18-1b */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 1c-1f */
+
+    FC_SP,FC_SP,			/* 20-21 sp !  */
+		FC_SP,			/* quot &#34; quotation mark */
+		      FC_JK,		/*    23	# */
+    FC_JK,FC_JK,FC_CF,FC_SP,		/* 24-27  $ % & ' */
+    FC_SP,FC_SP,FC_JK,FC_JK,		/* 28-2b  ( ) * + */
+    FC_SP,FC_JK,FC_SP,FC_JK,		/* 2c-2f  , - . / */
+
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 30-33  0 1 2 3 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 34-37  4 5 6 7 */
+    FC_JK,FC_JK,FC_SP,FC_SP,		/* 38-3b  8 9 : ; */
+    FC_LT,FC_SP,FC_SP,FC_SP,		/* 3c-3f  < = > ? */
+
+    FC_JK,'a',  'b',  'c',		/* 40-43  @ A B C */
+    'd',  'e',  'f',  'g',		/* 44-47  D E F G */
+    'h',  'i',  'j',  'k',		/* 48-4b  H I J K */
+    'l',  'm',  'n',  'o',		/* 4c-4f  L M N O */
+
+    'p',  'q',  'r',  's',		/* 50-53  P Q R S */
+    't',  'u',  'v',  'w',		/* 54-57  T U V W */
+    'x',  'y',  'z',  FC_JK,		/* 58-5b  X Y Z [ */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 5c-5f  \ ] ^ _ */
+
+    FC_JK,'a',  'b',  'c',		/* 60-63  ` a b c */
+    'd',  'e',  'f',  'g',		/* 64-67  d e f g */
+    'h',  'i',  'j',  'k',		/* 68-6b  h i j k */
+    'l',  'm',  'n',  'o',		/* 6c-6f  l m n o */
+
+    'p',  'q',  'r',  's',		/* 70-73  p q r s */
+    't',  'u',  'v',  'w',		/* 74-77  t u v w */
+    'x',  'y',  'z',  FC_JK,		/* 78-7b  x y z { */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 7c-7f  | } ~ del */
+
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 80-83 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 84-87 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 88-8b */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 8c-8f */
+
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 90-93 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 94-97 */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 98-9b */
+    FC_JK,FC_JK,FC_JK,FC_JK,		/* 9c-9f */
+
+    FC_SP,			/* nbsp   &#160; no-break space */
+    0xa1,			/* A'	LATIN CAPITAL A WITH OGONEK */
+    FC_SK,			/* '(	BREVE */
+    0xa3,			/* L/	LATIN CAPITAL L WITH STROKE */
+    FC_JK,			/* curren &#164; currency sign */
+    0xa5,			/* L< */
+    0xa6,			/* S'	LATIN CAPITAL S WITH ACUTE */
+    FC_JK,			/* sect   &#167; section sign */
+
+    FC_SK,			/* ':	DIAERESIS */
+    0xa9,			/* S<	LATIN CAPITAL S WITH CARON */
+    0xaa,			/* S,	LATIN CAPITAL S WITH CEDILLA */
+    0xab,			/* T<	LATIN CAPITAL T WITH CARON */
+    0xac,			/* Z'	LATIN CAPITAL Z WITH ACUTE */
+    FC_SK,			/* shy    &#173; soft hyphen */
+    0xae,			/* Z<	LATIN CAPITAL Z WITH CARON */
+    0xaf,			/* Z.	LATIN CAPITAL Z WITH DOT ABOVE */
+
+    FC_JK,			/* deg    &#176; degree sign */
+    0xb1,			/* a;	LATIN SMALL A WITH OGONEK */
+    FC_SK,			/* ';	OGONEK */
+    0xb3,			/* l/	LATIN SMALL L WITH STROKE */
+    FC_SK,			/* acute  &#180; acute accent */
+    0xb5,			/* l<	LATIN SMALL L WITH CARON */
+    0xb6,			/* s'	LATIN SMALL S WITH ACUTE */
+    FC_SK,			/* '<	CARON */
+
+    FC_SK,			/* cedil  &#184; cedilla */
+    0xb9,			/* s<	LATIN SMALL S WITH CARON */
+    0xba,			/* s,	LATIN SMALL S WITH CEDILLA */
+    0xbb,			/* t<	LATIN SMALL T WITH CARON */
+    0xbc,			/* z'	LATIN SMALL Z WITH ACUTE */
+    FC_SK,			/* '"	DOUBLE ACUTE ACCENT */
+    0xbe,			/* z<	LATIN SMALL Z WITH CARON */
+    0xbf,			/* z.	LATIN SMALL Z WITH DOT ABOVE */
+
+    0xc0,			/* R'	LATIN CAPITAL R WITH ACUTE */
+    0xc1,			/* A'	LATIN CAPITAL A WITH ACUTE */
+    0xc2,			/* A>	LATIN CAPITAL A WITH CIRCUMFLEX */
+    0xc3,			/* A(	LATIN CAPITAL A WITH BREVE */
+    0xc4,			/* A:	LATIN CAPITAL A WITH DIAERESIS */
+    0xc5,			/* L'	LATIN CAPITAL L WITH ACUTE */
+    0xc6,			/* C'	LATIN CAPITAL C WITH ACUTE */
+    0xc7,			/* C,	LATIN CAPITAL C WITH CEDILLA */
+
+    0xc8,			/* C<	LATIN CAPITAL C WITH CARON */
+    0xc9,			/* E'	LATIN CAPITAL E WITH ACUTE */
+    0xca,			/* E;	LATIN CAPITAL E WITH OGONEK */
+    0xcb,			/* E:   LATIN CAPITAL E WITH DIAERESIS */
+    0xcc,			/* E<	LATIN CAPITAL E WITH CARON */
+    0xcd,			/* I'	LATIN CAPITAL I WITH ACUTE */
+    0xce,			/* I>	LATIN CAPITAL I WITH CIRCUMFLEX*/
+    0xcf,			/* D<	LATIN CAPITAL D WITH CARON */
+
+    0xd0,			/* D/	LATIN CAPITAL D WITH STROKE */
+    0xd1,			/* N'	LATIN CAPITAL N WITH ACUTE */
+    0xd2,			/* N<	LATIN CAPITAL N WITH CARON */
+    0xd3,			/* O'	LATIN CAPITAL O WITH ACUTE */
+    0xd4,			/* O>	LATIN CAPITAL O WITH CIRCUMFLEX */
+    0xd5,			/* O"	LATIN CAPITAL O WITH DOUBLE ACUTE */
+    0xd6,			/* O:	LATIN CAPITAL O WITH DIAERESIS */
+    FC_JK,			/* times  &#215; multiplication sign */
+
+    0xd8,			/* R<	LATIN CAPITAL R WITH CARON */
+    0xd9,			/* U0	LATIN CAPITAL U WITH RING ABOVE */
+    0xda,			/* U'	LATIN CAPITAL U WITH ACUTE */
+    0xdb,			/* U"	LATIN CAPITAL U WITH DOUBLE ACUTE */
+    0xdc,			/* U:	LATIN CAPITAL U WITH DIAERESIS */
+    0xdd,			/* Y'	LATIN CAPITAL Y WITH ACUTE */
+    0xde,			/* T,	LATIN CAPITAL T WITH CEDILLA */
+    0xdf,			/* ss	LATIN SMALL LETTER SHARP S (German) */
+
+    0xe0,			/* r'	LATIN SMALL R WITH ACUTE */
+    0xe1,			/* a'	LATIN SMALL A WITH ACUTE */
+    0xe2,			/* a>	LATIN SMALL A WITH CIRCUMFLEX */
+    0xe3,			/* a(	LATIN SMALL A WITH BREVE */
+    0xe4,			/* a:	LATIN SMALL A WITH DIAERESIS */
+    0xe5,			/* l'	LATIN SMALL L WITH ACUTE */
+    0xe6,			/* c'	LATIN SMALL C WITH ACUTE */
+    0xe7,			/* c,	LATIN SMALL C WITH CEDILLA */
+
+    0xe8,			/* c<	LATIN SMALL C WITH CARON */
+    0xe9,			/* e'	LATIN SMALL E WITH ACUTE */
+    0xea,			/* e;	LATIN SMALL E WITH OGONEK */
+    0xeb,			/* e:	LATIN SMALL E WITH DIAERESIS */
+    0xec,			/* e<	LATIN SMALL E WITH CARON */
+    0xed,			/* i'	LATIN SMALL I WITH ACUTE */
+    0xee,			/* i>	LATIN SMALL I WITH CIRCUMFLEX */
+    0xef,			/* d<	LATIN SMALL D WITH CARON */
+
+    0xf0,			/* d/	LATIN SMALL D WITH STROKE */
+    0xf1,			/* n'	LATIN SMALL N WITH ACUTE */
+    0xf2,			/* n<	LATIN SMALL N WITH CARON */
+    0xf3,			/* o'	LATIN SMALL O WITH ACUTE */
+    0xf4,			/* o>	LATIN SMALL O WITH CIRCUMFLEX */
+    0xf5,			/* o"	LATIN SMALL O WITH DOUBLE ACUTE */
+    0xf6,			/* o:	LATIN SMALL O WITH DIAERESIS */
+    FC_JK,			/* divide &#247; division sign */
+
+    0xf8,			/* r<	LATIN SMALL R WITH CARON */
+    0xf9,			/* u0	LATIN SMALL U WITH RING ABOVE */
+    0xfa,			/* u'	LATIN SMALL U WITH ACUTE */
+    0xfb,			/* u"	LATIN SMALL U WITH DOUBLE ACUTE */
+    0xfc,			/* u:	LATIN SMALL U WITH DIAERESIS */
+    0xfd,			/* y'	LATIN SMALL Y WITH ACUTE */
+    0xfe,			/* t,	LATIN SMALL T WITH CEDILLA */
+    FC_SK,			/* '.	DOT ABOVE */
+};
+
+
+#define word_tbl0_LEN 9001
+static const char *word_tbl0[9001] = {
+ "\011collected", 0, "\007inspect", 0, "\004gift", "\004only\012phenomenon", 
+ "\004drop\004flit", "\007glamour", "\004jerk", 0, "\011belonging", 
+ "\007scenery", 0, 0, "\003err", "\005spray\006wholly", "\003phd\004syst", 
+ "\014cancellation\010tarragon", 0, "\006guilty", "\010deprived", 0, 
+ "\006planet", "\006latter\007hamster", 0, 
+ "\013accountable\012occasional\013luminescent", "\005scale\004vote", 
+ "\003tam\005flows", 0, "\012increments", 0, 0, 0, 0, "\006spruce", 
+ "\012economical\006caring", "\011protocols", 
+ "\005clear\007western\012whatsoever", "\006branch", 
+ "\014transactions\005elect\006unsafe", "\016investigations", 0, "\006sample", 
+ "\006strips\003rah\006cyborg", "\004envy", "\003wow\011shipments", 
+ "\013compilation", "\010response", "\005learn\004took", "\004tool", 0, 
+ "\014alternatives\004toon", "\007pouring", "\005asked\011gradually", 
+ "\010repeated", "\011reduction\005bless", 
+ "\004deck\010analyzed\012persuasive", 0, 0, "\007comfort\010radicals", 0, 0, 
+ 0, 0, 0, 0, "\003jaw", 0, "\006chicks\011beginners", 0, "\005idiot", 
+ "\006whiner\006waters", 0, "\012popularity", "\010feedback\007monitor", 0, 0, 
+ "\010exchange\007amateur\013drastically", "\013introducing", "\010inferred", 
+ "\005cedar\005races", "\005weird", "\005bored\012attraction", 0, 
+ "\006joined\007casinos", "\007portion", "\005bloke", "\011acclaimed", 0, 
+ "\011utilities\012skepticism", "\012thresholds\006teaser", 0, 0, 
+ "\011disorders\015misunderstood", 
+ "\006signed\005tiger\013embarrassed\011witnessed", "\006covers\011multitude", 
+ "\005cloud\011brotherly\010threaten", "\005right\005north\011realizing", 
+ "\007learned\007remains\011blackmail\006garnet", 0, 0, 
+ "\006fourth\007efforts\013productions", 0, 0, 0, 0, 0, "\012legitimate", 
+ "\005pleas\010injected", "\007disrupt", 0, "\007keyword\006septic", 
+ "\005sleep\004food", "\005pesky", 0, "\011fractured", "\006hoping", 
+ "\006scenic", "\006amazed\007restore\014sufficiently", 0, "\004fool", 0, 
+ "\007prayers", "\006hazard", "\007bottles\014modification\006insole", 
+ "\006trends", "\005local", "\005using\006smokes\004iffy", 
+ "\004foot\006unlike", 0, 0, 0, "\006joking\004vary", "\012missionary", 
+ "\005viola\012collectors\006folder", "\010neoprene", "\005socks", 
+ "\007sisters", "\007radical", 0, "\014circumstance", "\003lex", 
+ "\011notifying", 0, "\007keeping\003gig\011scheduled", "\006oracle", 0, 0, 
+ "\005being", "\005rebel\007leisure", 0, 0, 0, 
+ "\011worldwide\012tremendous\013recognition", 0, "\005input", 
+ "\006grande\011purchaser\007analyze", 
+ "\006normal\004wild\014advisability\013compulsions", "\012democratic", 
+ "\007instead\013negotiating", 
+ "\011afternoon\004pace\013achievement\010remedies", 0, 0, "\006sender", 
+ "\004true\010woodland", "\010checking", "\004pack", "\006auburn", 
+ "\011solicited", "\010formulas\006damage\010weddings", "\010francois", 
+ "\012properties", 0, "\003hen", 
+ "\011financing\011suggested\007causing\010concepts\007sorting", "\006aspect", 
+ "\013collections\011cocktails\016certifications", "\011amplifier", "\004some", 
+ 0, "\013prosecutors", 0, "\004hard\006sucked", "\004hare", "\011seemingly", 
+ "\010sequence", 0, "\007privacy", "\005aside", 
+ "\006secret\010analysts\007scoring", "\007empower\005swore", 
+ "\006second\004harm\007alimony", 0, "\005daddy", "\006cigars", 0, 
+ "\013practicable", "\003top", "\005radio\004hart\012customized", "\004fame", 
+ "\006apache", "\006fairly", 0, "\013definitions\010revenues", 
+ "\013commodities\007flowers", "\010bounding", "\006turtle", 0, "\006bigger", 
+ "\005could\003tie", 0, "\010rotation", "\010dominion\011solitaire\005hubba", 
+ "\011inception", "\005every", "\007parking\005tours", 0, "\012discretion", 
+ "\003gap\004vent", "\013blockbuster\013principally", "\011receivers", 0, 0, 0, 
+ "\005debit\012deliberate", "\007imaging\007defeats", "\012optimizing", 0, 0, 
+ 0, 0, "\006genius", "\005paste\003lit\006nibble", 0, "\013supercharge", 0, 
+ "\010together\012subjective\014subconscious", "\004back\005joint", 
+ "\007grinder", "\006access", "\010relation\003tho", "\011configure", 
+ "\010optional\011maximizes", "\015instructional", 0, 0, 
+ "\013percentages\007wedding\011bypassing", 0, 
+ "\007formula\005views\010indexing", "\004reds", "\010evidence", 0, 
+ "\012redirected", 0, 0, "\010disposal\011facsimile", 
+ "\014intelligence\010clicking\012internally", "\007concept\006unisex", 0, 0, 
+ "\006spaces\011dominance", 0, 0, "\010hospital\010trillion", 
+ "\004sake\006pronto", "\012duplicated", "\007analyst", 0, 
+ "\011impressed\003fop\005leeds", 0, "\010elephant\006firmly", 
+ "\007figured\007mystics", "\013exponential", "\004jest", "\006quanta", 
+ "\003lbs", 0, 0, "\011brilliant", "\010infringe\010preserve", 0, 
+ "\012particular\012contacting\011indicator", 
+ "\007revenue\011skeptical\006resets", "\003tan", "\004aunt", "\010credible", 
+ 0, "\006powder", 0, 0, 0, "\007excuses", "\006solely", "\005stock\005sheer", 
+ "\005tasks\014surveillance\013irrefutable", "\006visual", 
+ "\012classified\012indulgence\011speakeasy", 0, "\006attach\014metropolitan", 
+ "\011anonymity\006citrus", "\010protects", 0, 0, 0, "\006panned", 
+ "\011curiosity", "\007simpler", "\007emerald", 0, "\013interactive\004tied", 
+ "\013advertising\005pagan", "\012respective\013independent", "\004tops", 0, 0, 
+ "\003pad", "\005birch", "\010somebody", 
+ "\015inappropriate\010projects\012disclosure", "\004tien", 
+ "\011relations\011suspended\012rightfully", "\005gypsy", "\007coconut", 
+ "\007regimen", "\004ties\007cheated", "\011responded\012screenings", 
+ "\007expects\011groceries", 0, "\014satisfaction\011carpenter", 
+ "\005pains\003ivy", 0, 0, "\007certain\013publication", 
+ "\003son\005birth\004gaps\005pairs", "\005waist\005habit", "\007pennies", 0, 
+ 0, "\003has\006bureau", 0, "\006raving\011seventeen", "\004null\010exceeded", 
+ "\006marina", "\007dialers\011prohibits", 0, "\003sic", 
+ "\006theirs\013acceptances\010outlawed", 0, "\013diversified", 
+ "\013descriptive", "\012clinically", "\007numbers\004cafe", 0, 0, "\003fan", 
+ 0, 0, "\007objects", 0, 0, 0, "\005smile", 0, "\011regretted", "\007therapy", 
+ "\006banded\006lasted", "\004hour", "\006island", "\006banged", 0, 0, 
+ "\010initiate", 0, "\005valid\010bracelet", "\011necessary", 
+ "\010variable\006banned\007ecology", "\012privileges", "\003ute", "\005typed", 
+ "\005story", "\011entourage", "\006jersey\007carried", "\014distribution", 
+ "\004vast\007fatigue", "\006resent", "\007engaged\005cages", 
+ "\003bad\005modem\004tang", "\015unfortunately\010detector\006chubby", 
+ "\006allied", "\013financially\006survey\011detection", 
+ "\003ree\004tank\012passionate", "\012definitely", "\010variance", 
+ "\006fooled\010caffeine", 0, 0, 0, "\003ley", "\007pyramid", 
+ "\007project\007trimmed\004aria\004raid", "\006remark", "\005pecan", 0, 
+ "\010domestic\006gratis\010renowned\011valentine", "\003eon", 0, 
+ "\011foreigner", "\004rail", "\003cut\006incase", "\007protect\004rain", 0, 
+ "\012liquidated", "\006fields", "\005alias\003jet", "\006cancer\006fiends", 
+ "\014publications", "\011requested", "\005peeks", "\011insisting", 
+ "\006tricks", "\007falsely\013restrictive\007locking", 0, "\010grateful", 0, 
+ 0, "\006thinly\004wimp", "\015opportunities", 0, "\011sclerosis", "\005color", 
+ "\005wrong\004pads\005flirt", "\005split", "\011recipient\011incorrect", 
+ "\006matrix\011fingertip", 0, "\005cross", 
+ "\004over\007systems\004song\012subsidiary\010airlines", 0, "\006merely", 
+ "\011situation\005logic\010elements", "\006hombre\005wales", "\006worked", 
+ "\004hash", 0, "\005major", "\007headers\005serve\010incoming", "\005walks", 
+ "\005walls", "\004sons\006tokens", "\012resistance", "\005valor", 
+ "\010attacked", "\004sick", 0, "\004hast", "\010segments", 
+ "\010entering\006signal\005pager\011condensed", 
+ "\010postings\013instructing\004fang", 
+ "\010download\010identity\014coordination\006savage", "\003ebb\006patent", 
+ "\006saying", 0, 0, 0, "\006panama", 
+ "\010attacker\011sensation\012suggestion\010thrilled", 0, 0, "\006resume", 
+ "\004fans", 0, 0, "\005label", 
+ "\004plan\013environment\011postcards\006fleece", 0, "\007refined", 
+ "\011amazement", 0, "\006studio\007viruses", 0, 0, 
+ "\007fastest\015qualification\014housekeepers", 0, "\006lately\005gator", 
+ "\004play\006doubts\013programmers", "\012referenced\010chatting\006warmly", 
+ "\006toward", 0, "\004wave", "\006profit", 0, 0, "\004avec\004reef", 
+ "\007showing\011housewife", "\005alike\006boasts", 0, 
+ "\010thousand\006beauty\006canvas", "\012techniques\005polls\004reek", 
+ "\005chain\004reel", "\011discounts\013composition\012reflecting\006atomic", 
+ 0, "\013reliability\003nob\005merge\010dividend", 0, 0, 
+ "\010division\013acquisition\014multitasking", "\005alive\012procedures", 
+ "\006orphan", 0, "\007cancels", 
+ "\006called\005grove\011precision\004cute\010pursuant", "\011fashioned", 
+ "\012competitor", 0, "\007airline", "\011connected", 
+ "\012apparently\010openings", 
+ "\011desperado\010gorgeous\010barnyard\015strengthening", "\005since", 
+ "\007element", 0, "\013memberships", "\010decision", "\010urgently", 
+ "\004cuts\003vat", 0, "\007mercury", 0, 0, 0, "\011guarantee", 0, 0, 0, 0, 
+ "\007segment", "\004salt", "\006resort\010ordnance", 0, "\007mastery", 
+ "\013transferred\013accountants", 0, 0, "\013untraceable", "\005recap", 
+ "\010industry\006arrive", "\006unable\005kooky\010insisted", 0, 0, 
+ "\013meaningless", "\011available\006sierra", "\006deadly", "\005aides", 0, 0, 
+ 0, "\003raj\010waterloo", "\003bog", "\007miracle", 0, "\005sneak", 
+ "\006burial\006admits", "\007teenage\006borrow\007rebates", 0, 0, 
+ "\011startling", "\007marquee\006regret\003gem", "\007posting", 
+ "\010staffing", "\013immediately", "\003win", "\004role", 
+ "\007booster\010odometer", 0, "\004deep\007overdue\010browsers\011crouching", 
+ "\010distance\004pure\006appeal", "\004deer", 
+ "\004slim\007scarlet\011reprinted", "\004roll\010escalate", "\010castings", 
+ "\004were\004slip", 0, "\007harrier", "\010baseline\012regardless\006topped", 
+ "\006length", 0, 0, "\010supports", "\015manufacturers", 
+ "\006accept\005sissy\007trinity\014environments", "\004rial", 
+ "\015possibilities", "\010enjoying\013pathologist", "\005notch", 
+ "\012expandable", "\003hat", 0, "\007opening\010winnings", 0, 
+ "\012revitalize", "\007balloon", "\011instantly\013communicate", 0, 0, 
+ "\013spectacular", "\012instructed", 0, 0, 0, "\003mix\012projecting", 
+ "\005woods\005chips\014unresponsive", "\014representing", 
+ "\005third\010southern", 0, 0, "\014embarrassing\010generous", 0, 
+ "\007thereon\012obsessions", "\014acquisitions", "\012materially", 
+ "\011verifying", "\014consultation\006deluxe", 0, 
+ "\012newsgroups\010bullshit\005marry\007staples", "\005bundy\005dusty", 0, 
+ "\012exploiting", "\010released\011recollect", 0, 0, 
+ "\006couple\014distributing", 0, 0, "\006smooth", "\006spouse", 
+ "\012protecting\013respiratory", "\017representatives\010retailer", 
+ "\011copyright", 0, "\011materials\006speeds\012exhausting", 0, 
+ "\010feelings", "\011vandalism", 0, "\013registering\013probability", 
+ "\003got", "\007factual", "\003ref\010exhibits", "\006spends\010releases", 
+ "\013continually\004doll", "\012accurately\013concentrate", 0, 
+ "\011bulletins\010brochure", "\011reporting\007ability\004raja", 
+ "\012concealing", "\004burn", 
+ "\005files\013potentially\007expired\012allegiance", 
+ "\012beneficial\015complimentary", "\004spec\006hockey", 
+ "\010starting\005trail", 0, "\004dial", "\007sizable", "\005fills\003pea", 
+ "\013transmitted\005films\013cheerleader", 0, "\005fruit\004bury", 
+ "\005world", "\005floor", 0, "\004wind", "\004wine", "\006matter\005knock", 
+ "\006lovers\004wing", 0, 0, 0, "\004wink", 
+ "\007winning\004spew\010colleges\010plungers\010handsome", 
+ "\005china\010minerals", "\015traditionally", 
+ "\004gems\014individually\007buffalo", 0, 
+ "\012specialist\010payments\005reads\015unconditional", 0, "\004wins", 
+ "\003hep", "\007anytime\007sprints", 0, "\007browser\007jogging", 0, 
+ "\011practices\005maker\015profitability\005lakes", "\012background\005snows", 
+ "\010detailed\012recurrence", "\017confidentiality\012frequently\003vow", 
+ "\005reaps", "\007support\004hate", "\011overloads", 0, 
+ "\006bounce\012commentary", "\004soon\010scamming", "\005genes", 
+ "\012strategies\015consciousness", "\004side", "\012programmed\011seduction", 
+ "\005stiff\010unlocked", "\010seasonal\014coordinating", 0, 0, 
+ "\011lucrative", "\011utilizing\003tor", 0, 0, "\006lookup", 0, 0, 
+ "\010contract\013efficiently\010engaging", "\005genus", "\007feeling", 
+ "\007caption", "\003chi\006shifts", "\003eta", "\007encrypt", 0, 
+ "\006settle\005towns", 0, "\007exhibit", "\007release", 0, 
+ "\011inability\010freshest\005racer", "\013redirecting\003pus\005drain", 
+ "\003gar\006scopes", "\011brochures\006shirts", "\006stored\006scores", 
+ "\003bea", "\005asset", "\010windfall", 0, 
+ "\005along\003rib\005clone\006smells\005blond", 
+ "\005blood\006stands\007desired", "\004kiss", "\005trump", 
+ "\006oppose\007closing\007toolbox", "\005close\011perfectly\006erotic", 
+ "\005drawn\005pause", "\012productive", "\004twin\003nun\007inhaled", 
+ "\006kiddie", 0, "\006marked", 0, "\010enforced", "\006alerts\010finished", 
+ "\012integrates", 0, "\005bytes\007college", "\004goto", 0, 
+ "\005pitch\007mineral", "\007plunger", "\010organism", 
+ "\006newest\010snapshot", "\007payment\007notices", "\012irritation", 
+ "\007cluster", "\003how\007clutter", 0, "\003sei", "\010athletes", 
+ "\011foolproof\010wellness", "\004peak", "\004stay", "\011statement", 
+ "\007topical", 0, 0, "\012interfaces", "\004same\004pear", 0, 
+ "\007studies\011diligence\014cheerleaders", "\011illegally", 
+ "\003for\012definitive", 0, "\007deleted", 0, 
+ "\015manufacturing\006regain\006golfer", "\004knee", 0, 0, 
+ "\012accessible\005pound\006safari", "\006harbor\012snowflakes\010passages", 
+ 0, "\007whether", "\010requires\003fig", 0, 0, "\003tap\012accustomed", 
+ "\006lawyer", 0, 0, 0, "\003bus", "\012matchmaker\011truncated", 
+ "\007exactly\012acceptable", "\010required\004knew", "\007periods\006armada", 
+ 0, "\006unique", "\015uncertainties\010channels\012unreadable", "\004vows", 
+ "\007resized", "\004tore\007provoke", 0, 0, "\010republic\013trustworthy", 
+ "\004tori\011authorize\003ark", 0, 0, "\013incorrectly\011addiction", 
+ "\012initiation\004chic", "\004torn", "\006awhile", 0, 0, 0, 0, 
+ "\012terminated\007iridium\011mediation", "\012ceremonies", "\010believed", 
+ "\005bitch", "\007apology\004chin", 0, "\007discuss", 0, 
+ "\005mails\004anon\004push\013predominant\004bead", "\013supplements", 0, 0, 
+ "\006prince", 0, 0, "\010believes", "\010believer", 
+ "\004beam\011concerned\011efficient\006refers", "\006deemed\004bean", 0, 
+ "\004live\005whole\006miller\003sop", 0, 0, 0, "\004beat\011extractor", 
+ "\004west\007enforce\006digits\004beau", "\005whore", "\005whose", 
+ "\007kestrel\010envision", "\013discouraged", 0, "\003why", "\003sie", 0, 
+ "\005bound\010overlook\010invasive", "\011registrar\004nuns", "\011slightest", 
+ "\013unspecified", "\010incurred\011schedules", "\010shopping\005outer", 
+ "\011signature", 0, "\007tobacco", 0, 0, 0, "\007knowing", "\006employ", 0, 0, 
+ "\006assure", "\004glob", "\013discoveries\010instruct", "\007stapler", 0, 
+ "\007revenge", "\011reporters", "\005poker", "\011excessive", 
+ "\013bodybuilder", 0, "\007breasts\007offends", "\010informal", 
+ "\007channel\003sho", "\004form\010mirrored\011mountains", 0, 0, 0, 0, 
+ "\013participate", "\012frustrated\004asap\007sliding", 
+ "\004fort\004tape\007elapsed", 0, "\006bridge", "\003wax\007bishops", 
+ "\007growing", 0, "\010machines", "\007require\014masturbation", "\004bush", 
+ 0, "\005blunt\011expecting", "\005inset", "\013prohibiting\013commonplace", 
+ "\011replicate", 0, "\006suites", 0, "\005jaded", 0, 0, 
+ "\013punctuation\004bust\013foreclosure\010advisers\013fashionable", 0, 
+ "\006footer\011countless\007ensured\006yields", "\007believe\006strict", 
+ "\007watches", "\007typhoon\004busy\013merchandise\010drawings\012consisting", 
+ 0, "\005inner", 0, "\007loading", 0, 0, "\006lowers", 0, "\005bonds", 
+ "\012maintainer\005bones", "\005meets\010proteins", "\010lakewood", 
+ "\012invitation", 0, "\005based", 0, 0, "\007veranda", "\005deter", 
+ "\013fulfillment", "\005theft\004aqua", 0, 0, "\007holders", 
+ "\015inadvertently", "\005bonus", 0, 0, "\011simulated", 0, 
+ "\015professionals", "\005stood", 0, "\012determines", "\004jazz", 
+ "\010strongly\007scraped", "\006worker", 
+ "\005today\013limitations\010spelling", 0, 0, "\006exodus", 
+ "\010quantity\015subscriptions", 0, "\005claim\007signing\010finalist", 0, 
+ "\005wants\006afford", "\006growth", 0, "\011courtroom\012critically", 
+ "\007turning", 0, "\012redesigned", "\006waited", 0, 
+ "\005ideas\007screens\006waived", "\010purposes\006laughs", 0, "\007machine", 
+ "\015irreplaceable\005madam\005radar", "\006record\007covered\011delicious", 
+ "\005times\006rubber", 0, 0, "\003put\010template", "\003gas\011sprinting", 
+ "\004tele", "\005taboo\011endocrine", "\013penetrating", "\003wet\005amaze", 
+ "\011sacrifice", 0, 0, "\004tell\016inconveniences", 
+ "\004kits\016configurations\007adviser\010disclose\014entertaining", 
+ "\015environmental", 0, 0, 0, "\012successful\004shop", 
+ "\010creamery\007pulling", "\010bottomed", "\006inches\006talent\004moan", 
+ "\004shot\006masked", "\006intent", 0, "\004show\007auction\007freezer", 0, 
+ "\006racial\014participates", "\005older\011quarterly", 0, 0, 
+ "\010succeeds\003nod", "\010retiring", 0, 
+ "\010flexible\007senator\012absorption\011compiling", "\007wrapped", 
+ "\006clamps", 0, "\005picks\012interviews\005icons\006thread", 
+ "\013influencing", "\003ago\012exceptions", "\006canned", 
+ "\007surname\005balls", "\006bottle", "\006person\005humor\013advertisers", 0, 
+ "\012aggressive\007wearing", "\006amount", 0, 
+ "\004sand\006extras\010performs", "\003him\004sane\012percussion", "\004coke", 
+ 0, "\006headed", "\007drawing", "\010commerce", "\007statics", 0, 0, 
+ "\012initiative\007protein\010retrieve", "\015investigative", 0, 
+ "\007laundry\013assignments", "\003key\007checked\010operates\011finalists", 
+ "\004sans\014missionaries\013cooperative", "\012verifiable", "\012directions", 
+ 0, "\003elk", 0, "\010progress\007goodbye\007specify\005scene\011religious", 
+ "\005cabin", "\007commend", "\006tastes\014questionable", "\003but", 
+ "\010treasure", 0, "\010luscious", 
+ "\006within\013disposition\012adjustable\012accredited\010damaging", 
+ "\012harassment", 0, 0, 0, "\010multiply\011comprised\011salvation\004fell", 
+ "\007writing\010operated\005colds", "\005newly", 0, "\010multiple", 
+ "\006powers", 0, 0, "\004felt\010entirely\006ruined", "\010rendered", 
+ "\006hassle", "\013formulation", "\016transportation", 0, 0, 
+ "\005serif\010colonial\004toss", "\010disaster", "\003ala", 0, "\007uniform", 
+ 0, "\007purpose", "\003try", "\005angry", "\012scratching", 0, 
+ "\007happier\010bacteria", "\005spell", 0, "\004rice\007merrick", 
+ "\006valued\007landing\011youngster", "\007succeed\006resale\012collective", 
+ "\011countdown\006tagged\011subtitles", 0, "\010delights", "\004puts", 
+ "\006steaks", "\005false", "\015consideration", 0, "\007formats", 0, 
+ "\007correct", 0, 0, "\010opposite", 0, "\012absolutely\007perform\006slices", 
+ "\012configured\012capability\013destruction", "\010external\004node", 
+ "\007careful", 0, "\010existent", 0, "\003pro\010chambers", 0, 
+ "\003our\011emanating\005gates\006biking\005latex", "\005looks", 0, 
+ "\013legislation", "\004thru\006ruling", 0, "\005loops\007prefers", 
+ "\012intestinal", "\006thanks", 0, 0, "\011treadmill", 0, 
+ "\005spoof\005polar\007vending", "\006verbal", 0, "\011ironworks\003mum", 
+ "\006letter", 0, "\007melding", "\011integrity\010exposure\006yearly", 
+ "\013information", "\005width", "\007alumnus", 0, "\007winners", 0, 0, 
+ "\005discs", 0, 0, "\006forced", "\004done", "\003bag", 
+ "\003way\005terms\004dong", "\006forged", "\005drove\005disks", 
+ "\007operate\012restaurant", 0, "\006indigo\004keys\011horoscope", 
+ "\010bothered", "\006formed", 0, "\003ice", "\012previously\004dice", 
+ "\007invalid\005vista\005finds\005lindy", "\005fines\012suspicious", 
+ "\011corporate", "\011offensive", "\006feeder\005ethic", 
+ "\012modalities\012unbeatable\012identifier\006unseen", "\004butt", 
+ "\005maybe\010troubles", 0, "\011discovery\007quietly", "\010unlisted", 
+ "\013salespeople", "\007gourmet\010vitality\007delight\012collisions", 0, 0, 
+ "\007wealthy\006kindly", "\004wipe", "\007handled\006clicks\015granddaughter", 
+ "\004page\012displaying\010troubled", "\011continued", 0, 
+ "\003act\007willing\005teach", "\003col\011boyfriend", 0, "\011protector", 
+ "\011organizer", 0, 0, 0, 0, 0, "\003her\010behavior\013classifieds", 
+ "\012expressway", "\010wherever\006thames\005lambs", "\011automated", 0, 
+ "\005pearl\005heart\005boots", "\012terrifying", "\007euphony\010trainers", 
+ "\006server", "\006herbal", "\004have\011templates\006weapon", 
+ "\005sport\007pervert", "\012interwoven", 0, 0, 0, "\010teamwork", 0, 
+ "\007excited", "\006family", "\013unmoderated", 0, "\004prod\014informations", 
+ 0, "\006change\003tot\004prof", "\011addressee\004prog", 0, 0, 
+ "\006charge\006orbits", "\011accessory", 
+ "\006expect\010whenever\011assistant", "\010paycheck\006govern", 0, 0, 0, 
+ "\003etc\010appended", "\015cancellations\011toothless", "\004pros", 0, 
+ "\004ours\005truck\013philosopher", "\006shield", "\010lingerie\011importers", 
+ "\011repeating", "\005puffy\010stardust\010ensuring", 0, "\003gat", 
+ "\010floating", "\005spree", "\013downloading\010grinning", "\013unrepentant", 
+ "\004very", 0, "\003rid", "\007intense\012contractor", "\007advises", 0, 0, 0, 
+ "\014conventional\010fallback", 0, "\006knives\007enlarge", "\010punching", 0, 
+ 0, "\007secured\011realistic\006merger", "\011organized", 0, "\012comparable", 
+ "\005pools", "\010mistress", 0, 0, "\012persistent\004bags", 
+ "\012securities\004ways\013unconscious", 0, "\006liable", 0, 0, 
+ "\010scanners\012philosophy", "\010stations\003hoy", 
+ "\012difficulty\011secondary", 0, "\006wisest\010starlets", "\005vocal", 0, 
+ "\007bedroom", "\010training", "\007trouble", 
+ "\010children\011anonymous\007costing\004cola\013controlling", 0, 0, 
+ "\004cold", 0, 0, 0, "\013experiences", 0, "\011northeast", "\005weigh", 
+ "\010wagering", "\007females\010adoption", "\004here", 0, 
+ "\011reposting\005gowns", 0, "\004acts", "\010covering\005piled", "\004colt", 
+ "\004bldg", "\006linked\003ell", "\003tar\006karate", 
+ "\013fortunately\005yacht\004hero", "\010findings\011portfolio\007exposed", 0, 
+ 0, "\004hers", "\012faithfully", "\011frequency", "\011depending", 
+ "\005grasp", "\012multilevel", "\007patriot\004vine", 0, 
+ "\003did\005erupt\005combs", "\005worse", "\005fight", 
+ "\005comes\010investor", 0, 0, "\005march\005stamp\003arm\012prudential", 
+ "\005sigma", 0, "\007killing", 0, 0, 0, 0, 0, "\007nemesis", "\007against", 
+ "\006highly", "\005makes\003alb\006clause", "\007stealth\004roof\005seams", 
+ "\010epidemic\010humanity", "\006nurses\010futility", 0, 
+ "\006organs\004gate\007swallow", 0, "\012correction\010keyboard", 
+ "\004room\016specifications\005seats\010scanning", "\006gaming", 0, 0, 
+ "\007monthly\004ride\011defective", 0, 0, "\004root\011recording", 
+ "\011demeaning", "\011household\013willingness\007delayed", "\007scanner", 
+ "\010reliance", "\007without\010applying", 0, "\005sting", "\010closeout", 
+ "\003haw\005tower\011plutonium", "\007dispose\007arsenal", 
+ "\006safety\013storefronts", "\012automation", 0, "\015exponentially", 0, 
+ "\013specializes", "\007capital", 0, "\007heavily", 0, "\012negotiator", 
+ "\010pamphlet\005spawn", "\006angels", "\005words\012punishable", "\003far", 
+ "\006stages\006wooden", 0, 0, "\007replies\011ambitious\012scheduling", 
+ "\006people", "\005works", "\007station", "\007finding\005worms\007kidneys", 
+ "\006barbed", "\014overwhelming", 0, "\010turbines\013preferences", 
+ "\017standardization\005gauss", "\006states", "\005liens", "\006middle", 0, 
+ "\012foundation", 0, "\003ten\005vomit", "\006nobler", "\005vigor", 
+ "\012guaranteed\010activity", "\005comma", "\011principal", "\004hint", 
+ "\011practical", 0, 0, "\006creams", "\005cakes", "\010everyday\003bah", 
+ "\007invites", "\010moderate\014intermediary", 0, "\012attachment", 
+ "\007creator", "\006living", 0, "\005tests", "\005renew\011behaviors", 0, 0, 
+ "\004door\010titanium", "\011germanium", "\006lesson", 0, 
+ "\010choosing\007factors", 0, 0, 0, "\011headaches", 0, 0, 0, "\005would", 
+ "\004alba\004ramp\006enjoys", "\005wound\006outfit", 0, 0, "\010painting", 
+ "\006career\010manifest", "\004arms", 0, "\003sap", "\003com\007looking", 
+ "\011prevalent", "\006givers", "\005wheel\004army", "\010steroids", 
+ "\013alterations", "\006dealer\006reform\011paychecks", 
+ "\005upper\007rescued", 0, "\007founded", "\011occasions", 
+ "\013maintenance\012recruiting", "\011dangerous\007panties", "\006marine", 0, 
+ 0, "\007harness", "\006troops", "\005cause\010specials", 
+ "\007drunken\006hydros", "\012wealthiest", 0, 0, 
+ "\010binaries\006custom\007farmers", 0, "\003fen", 0, "\014unregistered", 0, 
+ "\004sort\016professionally", "\011supporter", "\007payable", 0, "\005brien", 
+ "\011traveling\011appealing", "\004sign\006treats", "\005faked\007fullest", 
+ "\005daily", "\005small", "\010approval", 0, 0, "\010wondered\011stupidity", 
+ "\004farm\011equipoise", 0, "\005coach", 
+ "\012conducting\011obstacles\005hades\005choir", 0, 0, "\016respectability", 
+ "\012confirming", "\010separate\013matrimonial", "\005snarf", 
+ "\005badly\005faxes", "\004tend\004vest", 0, "\005those", 
+ "\003bed\007debtors\012stimulates\012assortment", "\006closer", 
+ "\014specifically", "\004damn\012enthusiasm\010flushing", 
+ "\007inbound\005tweed\006clover", 0, "\004plex", "\005zoned\013compounding", 
+ 0, "\007columns", 0, 0, "\004tens\005mouth\005webby", 
+ "\011impartial\006versed\004tent", 0, 0, "\006invent\007observe\004avid", 
+ "\006enroll", 0, 0, "\006school", "\012monitoring", 0, 0, 0, 
+ "\007authors\013compromised", 0, "\005handy\005panda\005bands", 
+ "\006agency\010removing\012maintained", 0, "\005bangs", 
+ "\012settlement\004gill", "\004drug", 0, "\011something\005banks\006viewed", 
+ "\010profited\003rho", 0, "\006hunger", "\007grammar\004drum", 
+ "\011authentic", "\010triangle", "\007expands", 0, "\010inclined", 
+ "\004come\007special\004flow\003jug", "\006center", "\007humanly", 
+ "\007tablets\006chorus\003fou", "\012disclaimer", 0, 0, "\005slash", 0, 0, 
+ "\013involvement", "\006pueblo", "\012congregate", "\004saps", 
+ "\012extraction", "\011rationale\014authenticate", "\003hub", 
+ "\006timing\003elm\005wells", "\011approvals", "\005needs", "\011responses", 
+ 0, "\005buyer\010regional", "\005taxes\011targeting", "\011disregard", 0, 0, 
+ "\004inch\007damaged\010carrying\007thyroid", "\011incurable", 
+ "\015presentations", 0, "\003die", "\010reckoned\010showcase", 0, 
+ "\010contests\011strategic\003ran\011compliant", "\010tutoring", 
+ "\011suspected", 0, "\006owners", "\006delays", "\007eastern", 
+ "\007curious\006deeply", 0, "\010listings", 
+ "\006passed\012incoherent\007inflict", "\015participating", 
+ "\007chances\004tour", 0, 0, "\005males", "\006cannot", "\010dropping", 
+ "\011pamphlets", "\005stole", 0, "\005stone", "\005malls", 0, 0, 
+ "\005store\007logical", 0, "\006nicely", 0, 
+ "\011deficient\006womens\007visions", "\011etiquette", "\004poke", 
+ "\006follow", "\012functioned", "\006refund\003sos", 0, "\014jurisdiction", 0, 
+ "\006trains", 0, 0, "\013condominium", 0, "\013unblemished\007elderly", 
+ "\006margin\011duplicate\005flask", "\014unproductive\013transplants", 
+ "\005rains", "\011warranted", "\006public", "\011perimeter", 0, "\005timer", 
+ "\004cake", "\007schemas", "\003out", 0, 0, "\004self\007unearth\010chairman", 
+ "\010sometime", "\003vet", 0, "\006wander", "\004load\007exploit", 
+ "\004sell\007publish", "\005favor\005octet\011implanted", 
+ "\005query\006fiscal", "\005turbo", "\015installations", 0, "\013aficionados", 
+ 0, 0, "\004loan\005dolly", "\012generalist", 0, "\012requesting\010facility", 
+ "\005fifty", 0, "\006cities", "\005annum", "\006wicked", 
+ "\015communication\004jugs", 0, 
+ "\004four\006notice\016pharmaceutical\012surrounded", 
+ "\007allowed\015informational\007branded\010ferocity", 0, "\011increased", 
+ "\011elsewhere", 0, 0, "\005diary\011simulates", "\004task", 0, "\005calls", 
+ "\006forger", 0, "\005going\005micro\010insiders\005jumps", 
+ "\004died\003ons\004hubs", "\006horses", 0, "\006former\007plastic", 0, 0, 0, 
+ "\010products", 0, "\006failed", "\004rank", 0, 
+ "\007contest\007preview\006brains", 0, "\013inexpensive\011checksums", 
+ "\004dies\011withdrawn", "\004spin\004diet\007frankly", 0, "\004wire", 
+ "\004paid", "\011strenuous", "\007listing", "\007express\007capable\004spit", 
+ 0, "\003con\010valuable\012volatility", 0, "\011investing\005lucky\005fucks", 
+ 0, "\005haven", "\004pain", "\011branching", 0, "\011grandkids", 
+ "\004pair\006ticker", "\007painful\012toothpaste", "\006caster", 0, 0, 
+ "\010recently", "\004paix\012presenting", 0, 0, 0, "\006bikini", 
+ "\012preventing\012regulation\011sentences\011northwest", "\010seasoned", 
+ "\005mercy", 0, "\005clips", 0, "\011lotteries", 0, "\006forgot", 0, 0, 
+ "\014condominiums", "\011injection", "\006walked", 0, "\006define", 
+ "\013accusations", "\005merry", "\005about\006plants", "\010coverage", 0, 0, 
+ 0, 0, 0, 0, "\005sperm", "\011convicted", "\004outs\010invoiced", 
+ "\011bracelets", "\004fast\010formally", "\007patrons", "\006ripped\004epic", 
+ "\010syndrome\007manning\004dang\004kiwi", 
+ "\007insider\011recursive\005noble", "\010position\012relaxation", 0, 
+ "\003bee\004dank", 0, "\010accuracy\006recall", "\007product", 
+ "\011complaint", "\004mode\010rollover", "\012magnifying", "\014successfully", 
+ "\006please\003pol\011textbooks\005goofy\010invoices", 0, 0, 
+ "\016accommodations\010studying", 0, 0, "\005scent", "\005midas", 0, 0, 
+ "\010hijacked", "\011developer", "\013circulation", "\010potatoes", 
+ "\003old\011diversity", 0, 0, "\010recovery\005solar\004bait\004stem", 
+ "\011localized\005vixen", "\013engineering\006quince", "\004step", 
+ "\013originating\005roles", 0, 0, "\012challenges\011unleashed", 
+ "\007outlook", 0, 0, "\005rolls", 0, 0, "\007anthrax\005aroma", "\006drinks", 
+ "\007thicker", 0, "\004peer\003hip", 0, 
+ "\012consulting\010maintain\006rising\013operational", 0, "\007heading", 0, 
+ "\015hydroelectric", 0, 0, 0, "\012postmaster\016inconvenienced", 0, 
+ "\006rancho\007healing\013accumulates", "\010woodruff", "\007lowered", 
+ "\004auto", 0, 0, "\006flowed\007hearing", 0, "\007catcall", 
+ "\010redirect\010managers", 0, "\011navigator\012profession", 
+ "\005entry\011numerical", "\005thigh", "\010parasite", 0, 0, 
+ "\012bankruptcy\005stuck", "\006guests", "\012regulators", "\006energy", 0, 
+ "\011estimates", "\005lying", "\005heads", "\005whisk", 
+ "\005match\004deja\005quake", "\011potential\006mature", "\005water", 0, 
+ "\011institute\007blocked\005cools", "\006eating\006worthy", 0, 
+ "\012advertises", 0, "\011developed\006bellow\015interrelation", 
+ "\006bought\007invoice\010eighteen", "\006cycles", 
+ "\005hears\005lamer\014experimental", 0, 0, 0, 0, "\004gave\014conferencing", 
+ "\012responsive\013personality", 0, "\007fingers", "\007closely", 
+ "\006boards", 0, "\006madman", "\010answered\013emphasizing", 0, 
+ "\010attached", "\004pole\017unconditionally", 
+ "\004been\006wealth\011deposited", "\007cowgirl", "\005sixty", 
+ "\011addressed\011landscape", "\007recover\004beer\006impala", 
+ "\011thousands\011southwest", "\015comprehensive\004poll", 
+ "\003hay\010alerting", 0, "\010straight\006drives\011delimited\004polo", 
+ "\006submit\012inadequate", 0, "\005twice", 0, "\010features", "\007domains", 
+ "\013interaction", "\006accent", 0, 0, "\012facilitate", 0, "\007custody", 
+ "\003fat\005hotel\010featured\010lecithin", "\004thus", 
+ "\006allows\013calculation", 0, "\004call\010pinewood", 
+ "\007smoking\004semi\010drilling\004calm", "\005smoke\006agenda", 
+ "\007decided\004lump", 0, "\005worth\005forty", "\007mailman", 
+ "\005study\005busby\005ninth", 0, "\007decoded", "\010brunette", 0, 
+ "\007filling", 0, "\012extracting\010fracture", "\010eligible\012corruption", 
+ 0, 0, "\006enable", 0, "\006loaded\006eighty\010admitted", 
+ "\011volunteer\006plunge", 0, 0, 0, 0, "\010attained\005goose", "\007poultry", 
+ "\007dialing", "\011somewhere", "\007editors\014unparalleled\004huck", 0, 
+ "\006comply", "\011affecting", 0, "\006minors", 0, "\010pathetic", "\004upon", 
+ "\011daughters", "\011obtaining", 0, "\004bold\003gin\007coolest", 0, 
+ "\012presumably", "\007analogy", "\010gamblers", 0, 0, 0, 
+ "\004exam\011fiduciary", "\006packet", "\004clip", "\010software\006minded", 
+ "\010creation", "\012additional\005float\006devote\012ridiculous", 
+ "\005ultra\004wise\006gently", "\012performing", "\004bolt\006things", 
+ "\004wish", "\012unverified\007battery\004germ", "\005bores", 
+ "\007powered\005audit", 0, "\010stroking", 0, "\011promoting", 
+ "\007painted\006decker", "\011presented", "\007gagging\005cigar", 
+ "\005horny\006diesel", 0, 0, 0, 0, 0, "\005eight", "\006bodies", 
+ "\007vanilla", 0, "\012functional", "\005wards", "\005wares\005ships", 
+ "\011workhorse", "\006golden", "\010matrixes", "\013consequence", 0, 0, 
+ "\006stress\012overweight\007standby", "\006versus", 
+ "\007fantasy\010executed", "\006fusion\007witness", 0, 
+ "\011requiring\010cleanest\011dimension", "\005mambo", "\005given\007feature", 
+ "\006regard\004fate", 0, 0, 0, 0, 0, "\012conditions\011teenagers", 0, 
+ "\011newsagent", "\012facilities\011powerless\006queued", "\003til", 
+ "\010deadline", "\003ant\011anchorage", "\011lightning\010missions", 0, 
+ "\005junta", "\006bonnet", "\010zirconia", 0, 0, "\013origination", 0, 
+ "\006taught", 0, "\011tasteless", 0, "\003rig", "\010patented", 
+ "\004away\005crack", "\005loper", "\007gambler", "\010examples", 
+ "\014interconnect", "\005force", 0, "\003ear", 0, "\005forge", 0, 
+ "\010attitude\005crank", "\010equipped", "\011inclusion\007coupled", 
+ "\005alone\010assorted", "\010subjects", 0, "\005moron", 0, 0, "\010squirrel", 
+ "\011customers\005worst", "\005forte\005ports", 0, "\004onto\012irrelevant", 
+ 0, 0, "\003sen", "\006adjust\012travelling", "\011workshops", 
+ "\011authority\011crosswind\012substances", "\007voyeurs", "\005reset", 
+ "\005happy", "\007dynamic\015incorporation", "\011ambulance\007solicit", 
+ "\007maximum", "\006freaks", "\010outcomes", "\007copying", 
+ "\014consequences\007descent", 0, "\010skeptics\006insult", "\007gateway", 
+ "\004cook", "\004cool\010entities", "\006theory\010respects", 0, 0, 
+ "\007lithium", "\015sophisticated\010stealing", 0, 
+ "\010computer\007encoded\006blocks\006hacker", "\004near", 
+ "\011blacklist\003hud", "\014completeness\004neat", "\003tau", 0, 
+ "\012continuing\007execute", 0, "\010enhanced", "\006permit", 0, 
+ "\011headlines", 0, 0, 0, 0, 0, "\003dig", "\007hobbies", "\012exchanging", 
+ "\010transfer\007beliefs\006billed\006escrow", "\010enhances", 0, 0, 0, 
+ "\010supplier", "\010supplies\010composed\006robbed", 
+ "\011eliminate\010suddenly\006italic\011engravers", 
+ "\004town\013eliminating\017vulnerabilities", 0, "\007example\007bounced", 
+ "\004tile", "\006hourly", "\003bib\004ante", "\010compared\003wit", 
+ "\007effects\007toolkit", "\006vender", "\004anti\006veneer\006mortal", 
+ "\006starts\004till\006tennis", 0, 0, 
+ "\003psi\004wrap\007honored\007moments\012healthiest", 
+ "\011otherwise\006robots", "\010supplied", "\007subject", "\011parachute", 0, 
+ "\004ants", "\004slot\007mission\012refundable", 0, "\006ultima", "\004slow", 
+ "\010monotype", 0, "\010daylight\003sou", "\007settled", "\006tripod", 
+ "\006occurs", "\010upgrades", 0, 0, 0, 0, 0, 0, 
+ "\004earn\010updating\010lowering", "\006scroll", "\007replica", "\005bells", 
+ "\007outcome", "\011bookstore\004ears", "\012dependable", "\004came", 0, 0, 
+ "\004send", "\003hmm\012achievable", 0, "\007hangers", "\010upgraded", 
+ "\011attendant", "\006afraid", "\011terrorist", "\011including\004camp", 0, 
+ "\005spans\007manages", "\004cams", 0, "\006engine\010annoying", 
+ "\004lock\006fering", "\010mourning", "\004sent", 0, "\004loco", 0, 0, 
+ "\007generic", 0, "\013fascinating", "\007skeptic", "\003two\007enhance", 
+ "\010mobility", "\004file", "\007respect", 
+ "\014compensation\012scientific\013suggestions\012redirector\014internetwork", 
+ "\005waste", 0, "\007commute\004crud", 0, "\004fill", "\005field\004film", 0, 
+ 0, "\011standards\012ultimately", "\004dorm", 0, 0, "\007playboy", 
+ "\011basically\007compose", "\007primary\007redding\004bomb", "\005fires", 
+ "\004rape\006phones", 0, "\010handbook\016organizational\007enquiry", 
+ "\010creating\007fairway", "\005steal", "\007compare\010lighters", 
+ "\010resident\003peg\004crux", "\010resulted\005firms\005infra", 
+ "\005steel\005abide\013supervision", "\006squads", "\015consolidators", 0, 
+ "\013benefitting", "\007daytime", 0, "\006modify\004alee", "\005faith", 
+ "\004with", "\013aphrodisiac", 0, "\003cop", "\010dispatch", 0, 0, 0, 
+ "\007scratch\007clinics\011believers", "\010punished", 0, "\005swear", 0, 0, 
+ "\006rigors", "\007hurdles", 0, 0, 0, "\005teeth", "\010whatever\006theses", 
+ "\007quality\011lifestyle", 0, "\011repairing", 
+ "\011contracts\004soul\014disconnected\010commence", 0, "\012highlights", 0, 
+ 0, "\007shocked\006lawful", "\007between\004sour", "\007granted\007hustler", 
+ 0, "\007upgrade", "\010whistles", "\011different\010thinking\010cropping", 
+ "\014investigator", "\011collapsed", "\006obtain", "\006donkey", 
+ "\010blasting", "\005mango", "\012permission\006trivia\007carioca", 
+ "\005salon", "\012possession", "\005three\006wanted\013respondents", 0, 
+ "\007elastic\011discusses", 0, "\015compatibility", "\007filings\003mad", 
+ "\006spirit", "\006sylvan", "\006offset\005tires", 
+ "\013authorities\012negotiated", 0, "\007airfare\011inoculate", 
+ "\010breaking\010dreaming", "\007summary\003imp\006shorts", 0, 
+ "\011tribesman\003beg", "\005alarm\006builds", "\006corona", 
+ "\006simple\004byte\006equals", "\014broadcasting", "\007lighter", 
+ "\010creditor", "\011accepting", "\012characters\003pon", "\005aimed", 0, 
+ "\011seriously", "\003nut", "\010programs\013convictions\007episode", 
+ "\013obligations\007reorder", "\010appendix\011thickness\010abundant", 0, 0, 
+ 0, "\010interpol\013understands", "\003pic", 0, "\012medication\006freely", 0, 
+ "\010cleverly\013humiliation", "\010shortage\007welding", 0, 0, "\005posts", 
+ "\003can\006tanner", "\007grabbed", 0, 0, 0, "\004rely\011violating", 
+ "\010sciences", 0, 0, 0, "\007forgery\010smartest", 
+ "\014interviewing\014unscrupulous", 0, "\011correlate", "\007snipped", 
+ "\004cope", "\010property", "\007durable", "\003fox\007excerpt\006spikes", 0, 
+ 0, 0, "\006remain\007cleaner\011discarded", "\013furnishings", "\005under", 0, 
+ "\012copyrights\013elimination", "\007shortly\005threw", "\006pulses", 
+ "\006begins\004cops", "\006cookie", "\006inputs\003hue", 0, 0, "\010absorbed", 
+ "\004copy\011suffering\005envoy", "\013fingerprint\007thieves", 
+ "\005throw\006gained", "\003buy", "\012businesses\012collecting", 0, 
+ "\006prater", "\005crest\010utilized\005dress", 0, 0, "\006incest", 
+ "\007missing\007obscure\006prayer", 0, 0, "\007numeric\013invitations", 
+ "\003bon", "\006nobody\014photographic", "\010absorber\011predicted", 0, 
+ "\007cologne", 0, "\012deductible", "\011creditors", "\010shoppers", 
+ "\003get\012pharmacist", "\004time\011companies", "\006coward\007whittle", 
+ "\004made\005exact", "\013unconfirmed\007usually\004dell\005alien\005seeks", 
+ "\010attempts\007modesty", 
+ "\005align\003pal\005seems\010utilizes\010headland", 0, 
+ "\007program\010headwall", "\006encore", "\012remembered", 0, 0, 0, 0, 
+ "\006upload", 0, 0, "\006defame", "\015somatotrophin", 
+ "\004pond\012graduating", 0, "\003nag", 0, "\006talked", 0, 
+ "\007filters\007science\004ease\005crown", "\013flexibility\010meridian", 
+ "\006flames", "\010proceeds", 0, "\007leather\006ladies", "\010encoding", 0, 
+ "\011regarding\011whitening\011attracted\007adhered", "\007sparkle", 
+ "\005dream\005break", "\006blonde", "\006magnet\003lab", "\004pick\004nuts", 
+ 0, "\004east\012counseling\014discouraging", "\013individuals", 
+ "\010shipping", 0, 0, "\004easy\012practicing", 0, 0, "\005motor", 
+ "\006estate\012propagator", 0, "\012discovered", "\007futures", "\006basics", 
+ "\005lotus", "\010assuming\007drilled\004hire", 
+ "\007easiest\007hacking\004cant", 0, "\012references\011necessity\003kiz", 0, 
+ "\012complained", 0, 0, 0, 0, 0, 0, "\010priority", "\010supposed\006viable", 
+ "\006author", "\011gibberish", 0, 0, "\012uncensored\007incomes", 
+ "\010solution", "\010forwards\006locked\004dose", "\006better\006relief", 0, 
+ "\003ado\012opposition", 0, "\010thankful", "\006kitten", 
+ "\007utilize\005climb", "\007attempt\011officials", "\007bidding", 
+ "\006around\006expand", "\012supersedes", "\006houses", "\011radiation", 
+ "\006expend", "\010describe\004bond\007earlier", "\004bone", "\006herein", 
+ "\007rhodium\004buys\005derma", "\010networks\011inquiries", 0, 
+ "\003dry\012adventures\006deploy", 
+ "\016advertisements\010tailspin\010swindled", "\012researched\007dustbin", 
+ "\007diverse", 0, "\005inter\007startup", "\007reprint", 0, 0, 0, 0, 
+ "\007orgasms\007shopper\010declared", 0, "\003sat", 0, "\007proceed", 0, 
+ "\004pall", "\004gets\004palm\010downtime", 0, "\007rejects\010swindles", 0, 
+ "\006sector", "\003hew", "\006deeper", 0, 0, 0, "\005lares", 
+ "\005reach\010enticing", 0, 0, 0, 0, 0, 0, 0, 
+ "\006dating\010diskette\013unnecessary", "\010deleting", 0, 0, 0, 0, 0, 
+ "\005names\010stirring", "\010approved", 0, "\003toy", 
+ "\005mixed\014garnishments", "\010judgment\013recursively", "\006import", 0, 
+ "\004uucp\005smell", "\010constant", "\007readily", 0, 
+ "\004labs\015knowledgeable", 0, "\011remaining\011sceptical", "\003tin", 0, 0, 
+ 0, "\010thievery\003rot", "\007suppose\010optimize\014headquarters", 
+ "\006single\005spear", 0, "\006casino", "\003gay", 0, "\007another\004murk", 
+ "\006ratios", 0, 0, "\011occurring", "\006pillow\005album", 0, "\004term", 0, 
+ "\007reached", "\004bald\004shun", "\012submission\006refill", 
+ "\011apartment\006modest\010gasoline", 0, "\003eat", 0, "\004shut", 0, 
+ "\004ball", "\006salmon", 0, 0, "\014continuously", "\010calories\007seniors", 
+ "\007forward", 0, 0, 0, 0, "\011providing\011estimated\005lotto", 0, 
+ "\010portable\005targe", "\011assisting\007letting", 0, "\011activated", 
+ "\012admissions\006anonym\010unmanned", 0, 0, "\005rooms", 0, 
+ "\014institutions\005quart", "\007declare\006dialer", "\005punch", 
+ "\007network\012concentric", "\003his", "\005heard\005roots\005camel", 
+ "\007extends", "\005death", "\006cranky\006bubble", "\006number\011doctorate", 
+ "\014capabilities\006sister", "\003ash", 0, "\004neck", "\007scripts", 
+ "\007trailer", "\011screening\010beginner", "\010critical\011affidavit", 
+ "\011reception", "\012naturalist", "\003fin", "\005alter", 0, 
+ "\007jackets\003taw", "\003dot\007pressed", "\007seeking", 0, 
+ "\005built\006osprey\007approve", 0, 0, "\004visa", "\006listed\007worried", 
+ "\007touched", 0, "\013registrants\011optimizes", "\005suite", "\006whines", 
+ 0, "\006dobson", "\013enlargement\005cores", "\003boo", 0, "\003mea", 
+ "\013specialists", 0, "\006optics", "\012originates", 
+ "\006sprint\005refer\006adults\006coffee\013threatening\012discretely", 
+ "\010flooding\012excellence\004tinc", 0, "\005drunk\004demi", 0, 
+ "\004toys\003bid\004ting\005silva", "\007strings", 0, "\005digit\005blows", 
+ "\010consumer\004demo\010earliest\011minimizes", 0, 0, 
+ "\010turnpike\006carrot", "\011essential\013grandfather", 
+ "\006wishes\013programming\006analog", "\007located\005largo", "\010consoles", 
+ "\006endure", "\005clean", "\015administrator\011executive", "\006marrow", 
+ "\010parallel", "\015hybridization", "\007picking\004tiny", 
+ "\010follicle\010harvests", 0, "\004pooh\005bunch\003sow", "\005lived", 
+ "\005parts", 0, "\004pool\012physically", 0, "\012nominating\005bends", 
+ "\016administration", 0, "\010headache", "\004poor\012thereafter", 
+ "\005sunny", "\011endurance", 0, 0, "\015circumstances", "\013unstoppable", 0, 
+ "\006holder", "\006smoker\010emerging", "\012personally", 
+ "\007caravan\006suffer\006severe", 0, "\012searchable\010downturn", 0, 0, 
+ "\012exercising\006holler", "\014disappointed", 0, 0, "\006header", 
+ "\011negatives", 0, 0, 0, "\007smarter", "\006warmer", "\004sept\006warner", 
+ 0, "\012appearance\010molecule", 0, "\005asses", 0, 
+ "\005fault\011happening\005pilot\005traps", 0, 
+ "\010reckless\015inconvenience", "\010assisted", 
+ "\004find\014universities\013credibility", "\004fine", 
+ "\012challenged\010directed\016correspondence\005brave", "\005yield", 0, 0, 
+ "\013recruitment", 0, 0, "\007traders", "\005comic", 0, 0, 0, "\005trial", 
+ "\007prosper", 0, 0, 0, 0, "\004rare\011melatonin", 
+ "\007fitness\014neighborhood", "\010virility", 0, "\006mystic", 
+ "\010location", 0, "\004book", 0, 0, "\007courier\004meal\004buzz\004boon", 0, 
+ "\004mean", "\007pointer", "\013consistency", "\005penis", 
+ "\006cactus\004boot", "\006rental", "\014functionally\004meat", 0, 
+ "\006strike", "\007returns", 0, "\010military\010reposted\007avoided", 0, 
+ "\005shown\006period\006melody", "\006stroke\005coast", 0, "\010registry", 
+ "\003hex", "\013termination\007consume", "\013workstation\005ative", 
+ "\003its\014complemented", 0, 
+ "\010minister\005youth\015bidirectional\011incognito", 
+ "\007brokers\006cobalt", 0, 0, "\007console", "\011prominent", "\010backbone", 
+ "\011magazines\006leases", "\005laugh\013educational\013moneymaking", 
+ "\007qualify\007schemes", "\007harvest\006leaves", 0, 0, 
+ "\011stiffness\006snatch", "\007labeled", "\005lasts", "\010maximize", 
+ "\003oak", "\007flavors\004silk", "\010deletion\007combats", 
+ "\004lack\007restart\013corrections", "\006status\011wonderful", 
+ "\005paper\011omissions", 
+ "\014fraudulently\011disappear\011numbering\007existed", "\007unknown", 
+ "\011committee", 0, "\003den", "\006daemon", "\014unemployment", 
+ "\010youthful\007anyones", "\011keystroke", 
+ "\007honesty\004scam\011announced", "\004scan\010impaired", 
+ "\004lacy\007proving", 0, 0, "\004dare", "\010sections", 
+ "\012threatened\010hesitate\006citing", "\012electrical", 
+ "\011regularly\007gestalt", 0, "\006vanish\004dark\006denial\005delft", 0, 0, 
+ 0, "\007chooses", "\012colleagues\013implication\003tub\010brighter", 
+ "\005cells", "\005loser", 
+ "\006should\004must\003pop\012categories\011resellers\012simulation", 
+ "\004dart\012conforming", "\007awesome", "\006dental\007begging", 
+ "\011decreased", "\004test", "\010suitable", "\006waxing", "\012indicative", 
+ "\006retire\007extract", 0, "\003thy", "\003pie", 0, 0, "\013identifying", 
+ "\010champion", 0, "\012pagination\010approach\010marriage", 0, "\005bases", 
+ "\003cap\004rent", 0, "\006tapper", "\005basis", 0, "\005agent", "\006pseudo", 
+ "\011certainty\014grandparents", 0, "\011arbitrary", "\006caused", 
+ "\010improper\005canal", "\007animals", 0, "\003hit\005taste", "\004core", 0, 
+ 0, "\010citizens", "\011recognize\007reveals", 
+ "\006stable\010decisive\006sitter\013preliminary\006robust\012hallelujah", 0, 
+ "\011confirmed", 0, "\013corporation\012managerial\004corn", "\006effort", 
+ "\007richest", 0, "\010failures", 0, 0, "\011certainly", 0, "\003tax", 
+ "\005steam", "\006knocks", "\005girls\011searching\003ups", 
+ "\007minimal\010nonsense\010exporter", 0, 0, "\004vita", 
+ "\003owe\013partnership\010smallpox\012connecting", 0, 0, "\011improving", 0, 
+ 0, "\016representative", "\012interested\010premiere\007theater", 0, 
+ "\012scavenging", 0, "\010cellular", "\005legal", 0, 
+ "\013certificate\010screwing", 0, "\006humble", 
+ "\010psychics\010juvenile\005heels", 0, "\005guess\004oaks\010advocate", 
+ "\013dimensional", "\005queue", "\006system", "\005costs", 
+ "\003pan\010inherent", 0, "\011licensing", 0, "\004gaze", "\011nighthawk", 
+ "\010readings", "\007natures", "\013constraints\013compliments\007pyrosis", 
+ "\007abusing\010diamonds", "\007section", 
+ "\011virtually\004deny\011recycling\013homeworkers\010ornament", 
+ "\004tube\006plains", 0, "\010coaching\010traveled", "\004pope\006lonely", 
+ "\010flawless", "\007monarch", "\011fulfilled\012formidable", 
+ "\007weather\005lasso\005dance", "\011allergies", 0, 0, 0, 
+ "\013homeopathic\006teller", 0, "\006screen", "\013considering\005ranks", 0, 
+ "\007classic\004pops", "\007recipes\011minefield", 0, 
+ "\007citizen\007nuclear\007surfing\006hollow\003lad", 
+ "\005loves\013frustrating", 0, 0, "\012investment\004cape", "\012skyscraper", 
+ "\007erotica\007wanting\006hooked", "\003fax\010messages", 
+ "\010everyone\007failure\010symphony", "\006garden", "\011gladiator", 
+ "\010bankrupt\010asteroid", "\007dialups\007yielded", "\005likes", 
+ "\012revolution\010mistakes", "\003awe", 0, 0, "\004caps", 0, "\006maroon", 
+ "\013exceedingly", "\007nipples", "\005poser", "\011locations", 
+ "\010courtesy", 0, "\010concrete", 0, "\007sincere", "\010declined", 
+ "\011borrowers", "\004hits\005holds", "\005holes", "\007bizarre", 
+ "\004huge\014certificates", "\005sized\007spanked", "\012capitalize", 
+ "\005costa", "\003ban", 0, "\013competitors\005songs\013commandment", 
+ "\014corporations\006castle", "\007telling\006locker", 
+ "\004owed\006forest\007psychic", 0, "\013residential", "\010mailings", 
+ "\012engineered", "\010locating", 0, "\007reading\004exec", 
+ "\007premier\013terminating", "\005thunk\010declines", 
+ "\007nothing\007trapped\011workplace", "\003flu\005scold", 0, 0, 
+ "\006colony\016implementation", "\007readers\013collectible\004owes", 0, 
+ "\003ohm", "\012remodeling", "\012eliminated", "\005dumps", 
+ "\015interestingly", "\006freeze", "\007assumes", "\006forums", 0, 
+ "\006dotted", "\010continue\007laughed", 
+ "\010targeted\005cover\015certification\007craving", "\003cos", "\007circles", 
+ "\003nee", "\007healthy", 0, "\005magma", 0, 0, "\010licensed", 
+ "\003hey\010barriers", 0, 0, "\005watch", 0, 0, "\011novelties", 
+ "\012wholesaler\015matriculation", 0, 0, "\004nail", "\012terminator", 
+ "\011confident\007exceeds", "\007clicked", 0, 
+ "\006excess\010platinum\005fewer", "\011playmates", "\005twain", 0, 0, 
+ "\004cell\010licenses", "\011valuation", "\011recurring", "\007humidor", 
+ "\011sometimes\007speller\007flannel", 0, "\006snakes\006puddle", 0, 0, 0, 
+ "\006images", "\007alleged", "\005colon\006burden", 
+ "\014installation\010stupidly", "\010proposed", "\012arbitrator", "\003tip", 
+ 0, 0, "\004lady\007rapidly\005chart", "\007message\005nurse", 
+ "\006format\007mistake", "\007mailing", "\007mileage\011sponsored", 0, 
+ "\006sturdy", "\011malicious", 0, 
+ "\007leaders\007dealing\007decline\012explicitly", 
+ "\007current\005opens\006florid", "\013speculation\006purity", "\006supply", 
+ 0, 0, 0, 0, "\004mutt\005creek\007culprit", "\004band", "\003ort", 0, 
+ "\004bang", 0, "\013testimonial", 0, "\004bank", "\010reaction", 
+ "\007asshole\010organize", "\007complex\013enhancement\010acoustic", 
+ "\007storage\007putting\007apparel", "\011equipment\011continues", 0, 
+ "\011negligent", "\014subsequently\011publishes", 0, 
+ "\006church\010sizzling\014commandments", "\012reputation", 
+ "\007noticed\005cater\007essence\007crucial", 0, 
+ "\006tested\010wildlife\007regents", "\004girl\006active", 0, "\003ser", 
+ "\013communities", "\011digestive", 0, "\007derived", 
+ "\011featuring\005jewel", "\010invented", "\003log\011forgotten", 0, 
+ "\006thumbs", "\004save", 0, "\004need", "\007driving\013restaurants", 0, 
+ "\007grounds\011partnered", 0, 0, "\007license", 0, "\010assigned", 
+ "\006object", "\010migrated\011scrolling\013liquidation", "\003job", 0, 
+ "\005elite\003ire", "\004cost\007helping", "\007cleared\003huh", "\003els", 
+ "\012calculator\007elected\011brokerage", 0, "\011contained\012substitute", 
+ "\003cry", 0, 0, "\011unwilling\006laurel\010centered", "\007implies", 
+ "\010explains", "\006breast", "\010awaiting", "\005misty", "\010perfecto", 0, 
+ "\005opera", 0, "\003rat\007blessed", 0, "\005kinky", 0, "\011galleries", 
+ "\007vampire", "\011overnight", 0, 0, "\003pyx", 0, 0, 0, "\013backgrounds", 
+ "\007methods", "\014testimonials\007dropped\003yup\006cliffs", "\010interest", 
+ "\007tiffany", "\012forwarding\007grossed", "\007satisfy", "\006pulled", 
+ "\015photographers", "\011everyones", "\007defined", "\013contracting", 
+ "\004tips\007neither\012positioned\006yelled\007undergo", "\005knows", 
+ "\005meant\011townhouse\005jeans", "\007mailbox\006denied\010indirect", 
+ "\010mandated", 0, "\006gentle", 0, 0, "\007finally\003soy", 
+ "\011impostors\007discern", "\011knowledge\007tonight\011whirlpool", 
+ "\010branches", "\006layers", 0, 0, "\015determination", 0, "\010compiles", 
+ "\012encouraged\011converged", 0, "\003sin", 0, 0, "\010generate", 0, 0, 
+ "\004sera", "\007pleased\012transcript", "\005embed\011warehouse\005specs", 
+ "\010followed", "\014bankruptcies\014enhancements", 
+ "\012committing\011astronomy\006babies\010refunded", "\007getaway\003scc", 
+ "\013unpublished\010captures", 
+ "\006shills\010compiled\006savers\013contractors", 
+ "\013exhibitions\011selecting\006advise", "\006thinks", "\007biggest", 
+ "\014intervention", "\007nations", "\006pacing\010activism", "\012cancelling", 
+ "\010captured", "\007explain", "\006dining", "\010infusion", 0, 
+ "\004logo\016authentication", "\013explanatory", 0, "\010offering\005glory", 
+ "\004logs", "\004jump", "\006acting", 0, "\012management\005homes", 
+ "\007medical\005truth", "\013interpreted", "\004else", "\005cards", 
+ "\006nudity\005cares", "\010practice\007bondage\011parasites", 
+ "\005ready\010scalable", "\007perfect\010securely", 
+ "\007friends\003une\012discomfort\013interracial", "\012completing\003rep", 0, 
+ "\004jobs", 0, 0, "\010journals", "\007editing", 0, "\005sides", 0, 
+ "\004rate\007rewrite\005carts", "\005flyer\006hiding", "\010prestige", 
+ "\007furnish\004arte\006lounge", "\004fuck\010especial\010probable\006column", 
+ "\013comfortable\003ape", "\006cursor\011direction", 
+ "\007sniffer\011accessing", "\005widow\010patience\007watched", 0, 
+ "\006seeing\006beaten\007placard", "\007traffic", "\010engineer", 0, 0, 
+ "\005still\011effective", "\011canceller", "\004arts\010grasping", 
+ "\004spot\005howdy", "\012recipients\003saw", 
+ "\011delivered\012designated\003cot\011scrambler", "\005menus", 
+ "\013universally", 0, "\010probably", "\007victims", "\006cancel\006missed", 
+ "\005altos", "\007peering", "\011intrigued", "\011treatment\006toilet", 
+ "\006videos\005coral", 0, 0, 0, "\015consolidation", 0, 0, 
+ "\005bills\013compensated\003lea", 0, 0, 0, "\010judicial", "\011criticism", 
+ "\004open\006static", 0, "\012networking", 0, "\003aid\004sing", 
+ "\007armored", "\005among", "\007compile", "\010dinosaur\004sink\010disburse", 
+ "\006equity\010sapphire", "\010circular", 0, "\006warned\015clearinghouse", 
+ "\015unprecedented", 0, "\013arbitration\005stars", "\004sins", 0, 
+ "\010capacity", "\006narrow", "\011brunettes", 0, "\005stays", 
+ "\003any\004data\007markets\007capture\006humans", "\010crosscut", "\003row", 
+ 0, "\004date\007counter\010shepherd", 0, "\007parties\010wheelers", 0, 
+ "\011videotape", "\010secreted\010quickest", 0, "\014illuminating", 
+ "\006rebate", 0, "\012moderation\007catered", 0, "\012violations", 0, 
+ "\006harass", 0, "\011resolving", 0, "\007lexicon", 0, 
+ "\005cents\013dermatology", "\013lightweight", "\005young", 0, "\006future", 
+ "\011rejecting", "\003pig\006solves\007possess", 
+ "\007revisit\014recommending", 0, "\007snoring\014subsidiaries", "\005globe", 
+ "\012guidelines\005piles\005moral", "\010entrance", 
+ "\011maintains\007buddies", "\013foundations\004reps", "\003car", 0, 
+ "\007journal", "\005pills\010anterior", "\005brand\005crane\006clears", 
+ "\007initial", "\005media", "\013proposition\007centrum", 
+ "\013propagation\005flood", "\006soviet", "\004onyx\005token", 
+ "\011streaming\004eine", "\006posing\005crave\010accessed", 
+ "\007uncover\010memories", "\011passenger\006invite", 
+ "\012diplomatic\010shutdown\006jammer\004cote", "\005trust\005craze", 0, 
+ "\012contention", "\004apex", "\007cuckold\012convention", 
+ "\003ask\011precisely", 0, "\011substring\017authoritatively", 
+ "\005scaup\007collage", 0, 0, "\006prefer", "\014implementing\007trolley", 
+ "\012paragraphs", "\007devoted\013replicating", "\015authenticated\005delay", 
+ "\004lead\013unknowingly", "\006needed\007shallow", "\004leaf", 
+ "\015automatically", 0, "\006idiots", 0, "\012housewives", "\004viva", 
+ "\004know", "\004lean\010innocent", "\004aide", 0, "\005genie", 
+ "\005doubt\014photographed\013maintainers", "\010versions\011wallpaper", 
+ "\006nights\006social", 0, 0, 0, "\010criminal\003med\005thoth", 
+ "\010reported", "\012approaches\005genre", "\006felony", 0, 
+ "\004aids\010airborne", "\011knowingly", "\005heigh", "\006medium", 
+ "\005topaz", "\003big\010genetics", "\006sprays", "\005laser\006steamy", 
+ "\006simply\003pap", "\007happens", "\010icehouse", 0, 0, "\007halfway", 
+ "\006remind\010reporter\012explaining\006garage", "\007eternal", 0, 
+ "\012roadrunner\013butterflies", 0, "\006always", "\007stomach", 
+ "\006easily\004rows", "\007ancient\015reproductions", 
+ "\013consolidate\005panel\005apple", "\006tender", 0, "\012mechanical", 
+ "\010doubtful", 0, "\011molecular", 0, "\007violent", 
+ "\005atlas\004porn\011committed", "\007earners", "\007illicit", 0, 
+ "\007chattel", "\007history\007walking", "\004port\006cinema", 0, "\005brick", 
+ 0, "\010avoiding", "\004card", "\004care", "\010deserved", 0, "\010northern", 
+ "\006piracy", "\007conform", "\011promising", "\013regulations\007counsel", 0, 
+ "\005mimer\006strait\007smiling", "\010addicted", "\003sub", "\012responders", 
+ 0, "\004cars\004lust", "\004cart\005privy", "\007boggles", 0, 0, 0, 
+ "\006awaits", "\004junk", "\010bouncing\010deserves", 0, "\011psychotic", 
+ "\003shy", "\012complaints\012prosperity", "\011referrals\006flower", 
+ "\005found\006absent", 0, "\011quotation\006logged", 
+ "\011criminals\007tracked", 0, 0, "\005cases\012federation", 
+ "\014solicitation\004asks\010implants", "\013streamlined", 0, 
+ "\006cheese\010pretends", "\003req", "\006temple\007genetic\006voyeur", 
+ "\012developers", "\004grab", "\004down\006costly", 
+ "\011transfers\007seizure", "\011exchanged", "\012compliance\004bora", 0, 
+ "\012retirement\006extend", "\007ordered\010economic\007legibly\003git", 0, 
+ "\007rollers", "\010estrogen", 
+ "\006losing\010redesign\004gram\004dill\011charities\011unrelated", 
+ "\011animation\005joins", 0, "\010obsessed", "\006behind\005slack", 0, 
+ "\004born", 0, "\007version", 0, 0, "\007wishing\005wired", "\007becomes", 0, 
+ 0, "\005boxes\006trucks", 0, "\010apparent\006fucked\010validate", 
+ "\011southeast\012separately", 0, "\007goddess", 0, 0, "\010swingers", 
+ "\006depart", "\011inflation\012injections\005shoes", 0, "\006whiten", 0, 0, 
+ "\010vehicles\011transform", "\011assurance", 0, 0, "\014consistently", 0, 
+ "\005shops", 0, "\010overcome", 0, "\003ink\006pursue\005shots", 0, 
+ "\005guest", "\005shows\006female\007abusers\006soccer", 0, "\007tickets", 0, 
+ 0, "\010campaign\007deserve\005nasal", 0, 0, "\004sion\006domino", 0, 
+ "\004cent\011centuries", "\007furious", 0, "\013statistical", 
+ "\006titles\010browsing", "\006wasted\011incidents", 
+ "\007legally\014motivational", 0, "\005cycle\006mostly\006excuse\006tailor", 
+ 0, "\012eventually\011invisible\016characteristic", "\004subj\012paranormal", 
+ "\006surely", 0, "\005sands\011virginity", "\013competitive\011undertake", 0, 
+ 0, "\010wrangler", "\007compete", "\003bel\006bitter\004subs", 0, 0, 
+ "\006remedy\011economics\010migraine", "\003rim", "\011mechanism\007thereby", 
+ "\007threats", "\005lover\005moves", "\005lodge\005flail", 
+ "\006crafts\011retention\007uniting", 0, 0, 0, 0, 0, 0, 
+ "\007fifteen\010catalogs", 0, "\010commands", "\013integration", 
+ "\011documents\007extreme", "\006barrio", "\006darken", 0, "\003non", 
+ "\005virus", 0, "\007rockets\010leukemia", 0, "\005roses", "\010pictures", 
+ "\010interval", "\010premiums\007nightly", "\003set", 0, "\012automobile", 
+ "\007vehicle", 0, 0, 0, "\004snap\005quest", 0, 0, 0, 0, "\007studied", 0, 
+ "\006moving", 0, 0, 0, 0, 0, 0, 0, 
+ "\011campaigns\003zag\012preferably\006consul", 0, "\012tightening", 
+ "\010thoughts\006raises\006hereby", "\003fir", 0, 0, "\013represented", 0, 
+ "\005semen", "\005scope", 0, "\005score", 0, "\011agreement\006turkey", 
+ "\011territory", "\011bandwagon", "\011negotiate", "\005frees", 0, 
+ "\011correctly\007quicker\003dim", "\010personal", "\006return", "\006rushed", 
+ "\010numbered\006desist", 0, "\014occasionally", 0, 0, 
+ "\006barrel\013desperately", "\007burning", 0, 0, "\010carriers", "\004tire", 
+ "\005biker\010stuffing", 0, "\003wiz", "\005later\012restricted\003alk", 
+ "\013embroidered", "\007modules", "\011associate", "\007command\007gathers", 
+ 0, 0, "\004main\005spoil", "\007rewards\011homeowner\012songwriter", 0, 0, 0, 
+ "\006dancer", "\006faster\004slut\005spool\007queries", 0, 0, 
+ "\011community\006danger\004pose", "\005holed", "\007premium\011generally", 
+ "\015biotechnology", "\010develops", "\006stocks", "\006widely", 
+ "\014unauthorized\004belt", 0, "\010pressure", "\011unsecured\004rims", 
+ "\005shake\007gelling", "\005fifth", "\012multimedia\010frequent\005shame", 0, 
+ "\004post", "\004none\005shape", 
+ "\004casa\003lag\010pancreas\011liquidity\011falsified", 
+ "\005share\006topics", 0, "\006browse\007genuine", "\004case\003pry", 0, 
+ "\007sustain", "\004cash\007council\012compressed", "\010accepted", 
+ "\011operators", "\012structural", "\004nice\007hostile", 
+ "\007minutes\013congressman", "\005lines\007picture", 0, "\007hanging", 
+ "\003jab", "\005notes\015customization", "\005trees\010navigate\005creed", 
+ "\011butterfly", 0, "\013emotionally", "\010province\004sets", 
+ "\006virtue\005minor\011enrolling", "\012fictitious\011projected", 0, 
+ "\012recognized\011protected\012addictions", "\010dateline", 0, 0, 0, 
+ "\015enzymatically", 0, 0, "\004fire\007persona", 0, 0, 0, "\005cates", 
+ "\012professors", 0, "\010congress\010disclaim\012domination", 
+ "\004firm\011placement\012processors", "\007thought", "\005hoops", 0, 
+ "\010brackets", 0, 0, 0, "\004dime", "\014guaranteeing\007reclaim", 
+ "\012oceanfront\007floored\005fixes", "\011naturally", 0, "\011genealogy", 
+ "\010resource", "\007springs", "\004fuel", 0, 0, 0, 0, 0, 0, 0, 
+ "\007luckily\007develop\010playback", "\014encyclopedia", 
+ "\004boss\006quotes", 0, "\010discreet\005perks\006newton", "\004meet", 
+ "\003say", "\012continuity", "\012friendship", "\007whoever\007carrier", 
+ "\012unattended", "\004http", "\014blacklisting", "\005above", "\007arbiter", 
+ "\011wholesale", 0, "\006crying", "\005until", 0, "\013coincidence", 0, 0, 0, 
+ 0, 0, 0, "\013accommodate", 0, 0, "\013disclaimers", "\011bartender", 
+ "\003few", 0, "\013initiatives", "\005stuff\007cutting\011cooperate", 
+ "\012substation\007rallied", "\007justify\007notions", "\006issues\005peach", 
+ "\013intelligent\006toxins", "\007warfare", "\013prestigious", 
+ "\006nature\016infrastructure\007garland", "\007digital", 0, 
+ "\010changing\012eliminates", "\006chains", 0, "\006genome", "\004idea", 0, 0, 
+ 0, "\004such", "\005least\011restoring", "\007applies", 
+ "\004suck\015discrepancies\011cleansing", 
+ "\010charging\007gadgets\014duplications", 0, 0, "\006belief\006splits", 0, 
+ "\006gifted", "\010irritate", "\010strength\007surfers\006dianne", 0, 0, 
+ "\011solutions", "\003rin", "\010attorney\004mole", 0, 
+ "\005lower\006random\013provisional", 
+ "\006coming\006chance\007bonuses\006chosen\003pot", 
+ "\010obtained\006safely\005depot\007fuchsia", 0, 0, 0, 
+ "\010residual\013schoolgirls\006donors", 0, "\013destination", 0, 0, 
+ "\006cables", "\005miles", "\013pornography\007dummies", 
+ "\011secretion\007demands", 0, "\013anticipates", "\004what", "\005pines", 
+ "\012beforehand\003yea\007penalty\005mills\014deliberately", 0, 0, "\004icon", 
+ 0, 0, "\010bringing\010patently", 0, "\006differ", 0, "\007posture", 0, 
+ "\006accord\007perjury", "\013furthermore", 0, "\011spiritual", "\007started", 
+ "\006salida", "\004cove", "\007postfix\005beach\011merchants", 
+ "\010advanced\007desires\007resides", "\011outlining\013necessarily", 
+ "\006battle", "\011confusion\006culled", 0, 0, 0, 0, "\011botanical", 
+ "\012constitute\005guard", "\011priceless", "\004says", "\005gives\006helped", 
+ "\010advances\005garth", "\005years\007tuition\011ourselves\010colossus", 0, 
+ "\012impression", "\006brandy\012autonomous", "\006lumber\012camouflage", 
+ "\006nugget", "\011combating", "\011magnitude", 0, "\006coated", 0, 
+ "\006insure", "\012phenomenal", "\007library\007surpass", "\011judgments", 
+ "\006winner\003din\010prurient", 0, 0, "\003raw\013beneficiary\006monies", 
+ "\003bot\012ambassador", 0, "\006winter\011allowance\015contributions", 
+ "\013challenging", 0, 0, "\013endorsement\010examined\007blacker", 
+ "\010twenties", 0, 0, 0, "\005quite\012altogether", 0, "\006origin\006digest", 
+ "\003all\012difference\005wordy\014encapsulated\005heirs", 
+ "\003par\006father", "\006thirds", 0, 0, 0, "\010salaries", 0, 
+ "\011misplaced", "\010fountain\011favorable\005bloom", 
+ "\005truly\010replying\010reducing", "\010subspace", "\005swiss\005twist", 
+ "\011newsgroup\010hormones\007muscles\006slogan", 
+ "\005worry\007selfish\003ftp\004ring\005combo\007firstly", 0, 
+ "\005forth\011membranes", 0, "\012aggravates", 0, 0, "\010cashiers", 0, 0, 
+ "\011remediate", "\011conscious\012terminally", "\010explicit", 
+ "\013arbitrators", 0, "\007lasting", "\010horrible", 
+ "\010overseas\007meadows\011strongest\010transmit", "\005taken\010entirety", 
+ "\010uploaded", 0, "\012principles", 0, "\006inside\004yeah\014destinations", 
+ "\014testosterone\010cracking", 0, 0, "\004noon", 0, 
+ "\012indication\006cherry\010inquired", "\006approx", "\007advance", 0, 
+ "\004year", "\006hosted", "\007entries", 0, "\007edition\004cats\010guidance", 
+ 0, 0, 0, "\005lions", "\005power\007bangkok", "\011exposures", "\006floors", 
+ "\006mingle\012refreshing", "\005build\007jewelry", 0, "\005equal\007twisted", 
+ 0, "\012applicable", "\012horoscopes", "\005hopes", "\003mom", 
+ "\005filed\007fooling", "\011graduated", "\014dramatically", "\006filing", 
+ "\003bar", "\014conveniently", 0, "\005drama", "\003res", "\011blemishes", 0, 
+ "\014accomplished", "\012prophecies", "\007monkeys", "\005jokes\004fist", 
+ "\004dine", 0, "\015understanding\004ding\007trimmer", 
+ "\010powerful\007players\011throbbing", "\005sorts\005tight\012officially", 
+ "\003fly\007factory", "\006gamble\007creates", "\004both", "\010complete", 
+ "\007hormone\003pen\007reduces\010annotate", "\005signs", 0, "\006visits", 
+ "\007nullify", "\007arrived\011supported\004alla", 
+ "\004para\011reinforce\007cashier", 0, 0, "\004exit\004bots", "\006vessel", 
+ "\012divination", "\010enormous\007insulin", 0, "\003cow", 
+ "\005pesos\004dual", "\004park\007carries", "\011hazardous", "\005snail", 
+ "\011notarized", "\007stamina", 0, 0, 0, "\005pulse", "\004part", "\004name", 
+ 0, "\007relayed", "\004ally", "\013practically", 
+ "\010vacation\011encrypted\011nominated", "\003led", "\011supplying", 0, 0, 
+ "\006clones\006redeem", 0, "\006caught", "\006eleven\010leverage", 
+ "\006method\014expectations\010commonly", "\006motion\007ensures\006closes", 
+ "\006resins", "\007hitting", 0, "\014independence\005cheat", 0, 
+ "\011honeymoon", 0, "\010collapse\011ownership", "\013complaining", 
+ "\006raptor\007inquire", "\006gotten\005gauge\007oversea", "\010stocking", 
+ "\011generator\006totals\011challenge", "\006vision", 0, 
+ "\012scrollbars\005vault", 0, "\005hands\003tit", 0, 0, 
+ "\005tango\011excepting", "\005chest", 0, "\010proposal", "\004trap\005lapel", 
+ "\010adequate", 0, 0, 0, "\007amazing", "\007utility\010aircraft", 0, 
+ "\007talking", "\006fetish\007revised", "\006runner", "\006wright", 0, 
+ "\012evaluation\012formatting", 0, "\011specified\013blacklisted\004bare", 0, 
+ "\011dishonest\005bulge", "\004text\012evacuation", 0, 
+ "\010recorded\005drill", 0, "\011dedicated\014introductory\006boosts", 
+ "\010classify", "\004moms\004barn", "\006nation", "\003rub\011addictive", 
+ "\011prosecute", 0, "\004give\004bars", 0, 0, "\005began", "\006across", 
+ "\004rest\010addition\012developing\013huckleberry", 0, 0, "\006filter", 
+ "\010recorder", 0, "\005begin\010instinct\013centralized", 0, 0, 0, 0, 
+ "\011vacations\012publishers\010adhering\014refrigerator", 0, 0, 
+ "\006domain\006demise", "\004pens", "\010converts", 0, 
+ "\010language\013experienced\005begun", "\007writers\013informative", 
+ "\005honor\007quizzes", 0, "\010universe\006retain\012portfolios", 0, 0, 0, 
+ "\014connectivity\010elevated", "\011servicing", "\010integral", 
+ "\011albatross", "\003fit", "\015controversial", "\006gender", 0, 
+ "\010millions", "\007written", 0, 0, 0, "\010wireless", 0, 
+ "\005canon\004frat", "\006valley", 0, 0, 0, 
+ "\007falling\013inhibitions\010tempting\010monoxide\010geometry", 
+ "\011investors\004blow\012litigation\005blues", "\006mentor\012mysterious", 0, 
+ "\006whores", 0, "\012sustaining", "\015advertisement", "\007massage", 
+ "\007relates", "\010password\006editor\007choices", 0, 
+ "\013attachments\012girlfriend", "\005first", "\011elevators", "\005timed", 
+ "\004make\004desk", 0, "\005sells\006judges\005ravel", "\003pas\011oxidation", 
+ 0, 0, "\005style", "\013remediation\013undertaking", 0, "\004bend", 0, 0, 
+ "\004tits\014continuation", 0, "\010pleasure", 0, 0, 0, "\005condo", 0, 0, 0, 
+ 0, 0, "\010borrowed", 0, "\010ordained", 0, "\005basic", "\006anyone", 
+ "\013essentially", "\004pour", "\017procrastination", "\003sir\010crossing", 
+ "\007nervous\011venturing\004nope", 0, 0, 0, "\011notorious", "\011counselor", 
+ "\007opposed", "\007waiting\007decades", 0, "\014anticipation", "\005talon", 
+ 0, 0, 0, "\007concert", "\007million\007retired\007decides\012resolution", 
+ "\010checksum\014intimidation\010election", "\004ruby\007judging", 
+ "\006eczema", "\006paging", "\007fishing\005wires", 0, 0, 0, "\005blank", 0, 
+ "\006sealed", "\013billionaire", 0, "\011expressly", 0, 0, 
+ "\007immoral\012overriding", "\010services\007convert\013acknowledge", 
+ "\011forecasts", 0, 0, "\011boulevard", 0, 0, "\003bas\010swimming", 0, 0, 
+ "\004hull", "\003ret", 0, 0, 0, "\004iris", "\006debtor\004fits\010breakers", 
+ 0, "\007docking", 0, "\007invoked\006sirens\011acquiring", 
+ "\010redheads\007concise", "\011embassies", "\007predict\013electricity", 0, 
+ "\010director\006loving", "\013application\005troll", "\004dion", 
+ "\006relays", 0, "\011thumbnail", "\007proxies", "\006kicked\004alma", 0, 
+ "\006mutual", "\006claims", 0, "\004bout", "\004spss", 0, 
+ "\007relying\006eerily", "\006defend\003cox", 0, 0, "\010doctrine", 
+ "\005other\011filthiest", "\005units\005lobby\007bonanza\006climax", 
+ "\011permanent\013settlements\010closeups", "\005setup\012imposition", 
+ "\005rogue", "\007married\011descended", "\004pass\014difficulties", 
+ "\004past\010birthday\006muller", "\006desert\007tourism", 0, "\006cracks", 
+ "\007attract\011referring\006kiosks", "\011digitized\012expression", 
+ "\006speech\010arranged", 0, "\007joining\011posterior", "\006notary", 
+ "\012widespread", "\011recession\011packaging\006tablet\007cypress", 
+ "\006cartel\007phrases", "\003inn\005mayor\012homosexual", "\003fey", 
+ "\004sire", "\004laid\012stationery", 0, "\006happen\007pricing\003viz", 
+ "\012indicating\006riches\010worrying\014configurable", "\017extraordinarily", 
+ "\010thanking\007harmful", 0, "\005bucks\005ratio", "\007gallant", "\003oso", 
+ "\005steps", 0, "\010intercom", "\004sirs", 0, "\004sued", "\010diagnose", 
+ "\006really\004jade", 0, "\007hilltop", "\003mal", "\010suburban", 
+ "\007despite", 0, "\012flashlight", 0, "\007trident", 0, "\005anger", 
+ "\003end", "\013plagiarized", "\011shortcuts", "\014acknowledged", 
+ "\005royal\003ure", "\003rip", 0, 0, "\010opinions\013unrewarding", 
+ "\005phase", "\007assured", "\004base", "\014applications", 0, "\006closet", 
+ 0, "\007service\004mono\005scoot", 0, "\006moment\010memorial", 
+ "\006trying\005minds", "\003ate", "\012statistics\005bread", 0, 
+ "\012containing\007bearing\013prospecting", "\011answering", "\007beating", 0, 
+ 0, 0, "\004stop\004unit", "\010estimate", "\006binary", 0, 0, 
+ "\005offer\012officiates", "\006uptime\007beavers", "\011employing", 0, 0, 0, 
+ 0, 0, 0, "\010needless\010dizzying", 0, 0, "\010chemical\013replacement", 0, 
+ 0, 0, 0, 0, 0, "\007arrange\007enjoyed", "\006possum", 0, "\006aboard", 
+ "\007useless", "\007sending\006floppy\010distinct", 
+ "\012atmosphere\012subsequent", "\012delinquent\011comprises", 
+ "\007flights\003hum", 0, 0, "\006primer", 0, "\006serial", "\007outdoor", 0, 
+ 0, "\005owner", 0, "\014presentation", "\015announcements", 0, 0, "\003dip", 
+ "\011travelers", "\011succeeded", "\005boyer\010counties", 0, "\005thats", 0, 
+ 0, 0, "\007recycle", 0, "\005aches", "\011performed", 
+ "\007opinion\015inspirational", 0, "\011corrected", "\004male", 
+ "\014confidential", 0, 0, "\010verbiage", "\007robbing", "\005tempt", 
+ "\004mall", 0, 0, 0, "\010noticing", "\013contaminant\011mailboxes", 
+ "\010billings", "\004ripe\006rotary", 0, "\005exist", "\012experiment", 0, 
+ "\006pickup", 0, "\006canton", "\007precise", "\004ends", 
+ "\005rates\005snore\015manifestation", "\005early\010crusader", 0, 
+ "\007grocery", "\005deals\010bachelor", "\013development", "\004pike\003woe", 
+ "\006fellow\006clinic", "\007updates", "\006velvet\003sis", "\011chemicals", 
+ 0, 0, "\010daughter", 0, "\006crappy\004cave", "\010movement\010reminded", 
+ "\011migration", 0, "\010clearing", "\006nearly", 0, "\006neatly", 0, 
+ "\005emery", "\006scheme\006summer", "\012calculates", "\014rejuvenation", 
+ "\013coordinator", "\007totally\006misuse", "\010reminder\005wises", 0, 
+ "\010ancestry\006escape", "\010blessing", "\003day\011tiredness", 0, 
+ "\012censorship", "\014announcement", "\006voices", 0, "\005visor", 0, 
+ "\003qua\007divider", "\006plenty\006subset\014replacements", 0, 
+ "\012invaluable\006racing\007liberal\014intellectual\003moo", 0, 
+ "\010employed\007ragtime", "\010employee\013negotiation", 0, 
+ "\015extraordinary\003bat\004jury", "\004liar\007blaster\010awarding", 0, 
+ "\010printing\011variables", "\007vendors", 0, "\010audience\005woman", 
+ "\003mid", 0, "\005highs", 
+ "\005women\010pharmacy\005rotor\010nicotine\012misleading", "\005chili", 0, 0, 
+ 0, 0, "\007billing", "\011teachable", "\011perverted", 
+ "\010employer\007helpful", "\014governmental\003pep", "\006script", "\003ohs", 
+ "\007further\010directly\006impact", "\005sleek\007electro", 0, "\003yah", 
+ "\004bike", 0, "\005funds", "\012documented\004grey", 0, 
+ "\005lease\007treated", "\004path", "\012prosperous\004duck", 
+ "\005leave\010designed\012associated\011encourage", 
+ "\010sleeping\010defeated\014supplemental", "\010standout", "\006orgies", 0, 
+ 0, "\014unacceptable\010irrigate", 0, "\006losses\006tinker", "\005juicy", 0, 
+ 0, 0, "\011returning", "\010designer", "\005tenth", 0, "\006sweets", 
+ "\015unpredictable", "\007someone", "\013enthusiasts", 0, 0, 
+ "\005focus\006ethics", "\012messengers", "\006string", "\006advice", 
+ "\007musical\007ecstasy", 0, "\010starring", "\006gather\003oar", 
+ "\010problems\006strong\011bluegrass\010encoders\011federally", 
+ "\012telephones\013exclamation\012temptation", "\003ppm", 0, 
+ "\010customer\006placed\007spooler\014developments", 0, 0, 
+ "\013traditional\007marshal", "\006aurora", "\014negotiations\007grandma", 
+ "\005aging\011paperwork", "\006quoted", "\011employees\010workshop", 
+ "\005horse", 0, "\003sly", "\012conclusion", "\007teaches", 0, 0, 
+ "\010contrary\016malfunctioning", "\006plated", 0, "\011energized", 0, 
+ "\011parasitic", "\006played", "\010bisexual", "\013improvement", "\004mood", 
+ "\005tarot", "\007advised", 0, 
+ "\011employers\004days\005pussy\006coping\003pow", "\005harsh", 0, 
+ "\011sidelines", "\012considered", "\007degrees\010decrease\004bath", 
+ "\004moon\007javelin", 0, "\006skills\006finder", "\007focused", 0, 
+ "\006finger\015concentration", "\007extrema", 
+ "\005touch\010headline\004midi\010conclude", 0, 0, "\003nor\006upward", 
+ "\005tough", "\005quiet", 0, "\007padding\003lux", "\007abusive\003caw", 
+ "\010wrapping\011reputable", "\011engineers", 0, "\003sex\005spank", 0, 0, 0, 
+ 0, 0, "\010mechanic\012interlinks", "\013privatizing", 
+ "\011unhealthy\011divisions", 0, 0, "\011primarily", "\011breathing", 0, 0, 
+ "\013investments\015establishment", 0, "\003asp\007figures\007catcher", 0, 
+ "\003lib\016accountability", "\007mailers", "\006sorrel", "\005joker", 
+ "\007problem", "\010survival", "\013scholarship\011decisions\003irk\005slots", 
+ 0, 0, "\005slows", 0, 0, 0, 0, 0, 0, 0, 0, "\006envies", 
+ "\005price\014mathematical", "\005pride\007seekers\007intends", 
+ "\011literally", 0, "\006dreams\006breaks", "\010bonehead", "\004left", 
+ "\011important", "\006greens", "\014improvements\007bicycle", 
+ "\005prime\014preparedness", "\012acquainted", 0, 0, "\005sends\005acids", 0, 
+ "\005prise\011graphical\005belly", "\011realities\012circulated", "\005kitty", 
+ "\003era\012sequential", "\005helps\012republican", 0, 
+ "\006lights\007relaxed", "\005prize\005goals", "\012assessment", 0, 0, 
+ "\017distributorship", "\007changed\006factor", "\011advantage", 0, 
+ "\011preferred", "\007charged\006liquid", 0, "\010coloring", "\007serious", 
+ "\007roaming", 0, "\011generates", "\006filled\003nap", 
+ "\006filmed\011reminders", 0, "\005oasis\006giggle", 0, 0, "\011superstar", 0, 
+ "\007cruiser", 0, "\006summit", "\004pile", "\013investigate\004rude", 
+ "\006stamps\005ranch\006crazed", "\003sit", "\010recruits\007affects", 
+ "\006tester", "\005debts", "\004pill\013retractable", 0, 
+ "\012discussion\005isles", "\011adhesives", 0, 0, 0, "\013contingency", 
+ "\007destroy\011offerings", "\006anemia", "\007planned", "\010politely", 0, 
+ "\014scholarships\007worship\003awl", "\014regenerating\011prettiest", 
+ "\005bride", "\005tries\011unmatched", 0, "\006photos", "\010holdings", 
+ "\011cartridge", "\015accessibility", "\006making\007erasers", "\007confirm", 
+ 0, 0, "\010grumbled\004sexy\006police", "\011prospects\005trips", 
+ "\006proven\013marketplace\005queen", 0, "\011hurricane\013masterpiece", 0, 
+ "\004just\004five\003mop", 0, 0, "\006wheels\004hung\010heavenly", 0, 
+ "\010teachers\011possesses", "\007invited\006buried", "\010hardwood", 
+ "\006actual\005helve\013celebrating", "\003rev", 0, 
+ "\014conservative\005board\006burned", "\011retailers\012announcing", 
+ "\007reality", "\005hosts\007heights", "\012intrusions", 
+ "\007reasons\004hunt\014guardianship", "\010incident", "\012centennial", 0, 0, 
+ "\006reveal", "\012starvation\011endowment", 
+ "\005check\010presence\011hilarious", "\010managing\015investigators", 
+ "\007antenna", 0, "\012accordance\004bowl\006behave", 
+ "\011prototype\007acrobat", "\012handspring\013unavailable", "\011increases", 
+ "\006common\004spun", "\010rankings\011avalanche\011porcelain", 
+ "\015experimenting", 0, "\004spur\012applicants\013impressions", 
+ "\010lighting\003gul", 0, "\005voila", "\012transfered\003obi", 0, 0, 0, 0, 0, 
+ "\005noisy", "\007showers", 0, 0, "\005clubs\005birds", "\010greeting", 
+ "\012marketeers", "\010document", 0, 0, 
+ "\005image\012enterprise\015notifications", 
+ "\006broken\011expansion\011filtering", "\003leg", "\007gallons", "\005blast", 
+ "\005slate", 0, "\014investigated\005slave\014championship", 0, "\007holding", 
+ "\004site\007endorse\010climbing", "\013differences", "\004lake\010lawsuits", 
+ 0, "\005naves", "\011moderated", "\013appropriate", "\010inactive", 
+ "\012presidents", 0, "\006trench\014genealogical", "\010canceled", 
+ "\006upshot", "\007recruit\007regency", "\007mention", 0, 0, "\011bandwidth", 
+ "\005keeps", 0, 0, "\006awards\010infamous\011unnatural", "\011hopefully", 
+ "\007teacher", 0, "\004daze", 0, "\013intoxicated", 0, 0, 0, 
+ "\007cabaret\014plagiarizing", "\006mobile", 0, 0, "\010moreover", 
+ "\010expected\005niche", "\007windows", "\010charters", 0, "\010chapters", 0, 
+ "\006effect\012consultant", "\007crawled", 0, 
+ "\005index\010sponsors\006corpus", "\007baggage\007ranging\006begets", 
+ "\007refuses\012credential", "\012attractive\007conduct", 
+ "\004when\015international", "\011converter", "\007victory", 
+ "\005eager\003rue", "\005coded", 0, "\005urges", "\006guides", 
+ "\012reschedule", "\004whew", 0, "\004whey\010epilogue", 0, "\011checklist", 
+ 0, "\012scientists\010recalled", 0, "\007packing", 0, 0, "\007coaches", 0, 0, 
+ 0, "\011astounded\004gulf", "\007pockets\006python", 
+ "\010positive\011macintosh\003jut", "\005beech", "\006ranges", 
+ "\010gambling\010duration\012blacklists", "\004gull\012leadership\007lightly", 
+ "\013transcripts", 0, 0, "\005mucus\010volatile\004wade", 0, "\006weight", 
+ "\010contains\011moderator", "\011computers\012appliances", "\011privately", 
+ 0, "\010converse", "\004goal\015acceptability", "\006manage\005surge", 
+ "\010terminal\015intelligently", "\012everything\013installment", 
+ "\007layoffs\004cozy", "\007selling", "\012supervisor", 0, "\005drops", 
+ "\005elder", "\004zone", 0, 0, "\005group\005burst", 0, "\007failing", 
+ "\004legs", "\006circle", "\011supposing", "\003box", "\010reflects", 
+ "\007century", 0, "\010discover\011establish\007village\005metro\006ladder", 
+ 0, "\005great", "\007clothes\006trader\007lawsuit", 0, 0, "\005creep", 
+ "\010electric", "\011carefully\005tempo", "\003due", 
+ "\011residence\005layer\003alp", 0, "\007sponsor\010shutting", 
+ "\010financed\006resend", 0, "\010protocol\014contemporary", "\007arrival", 
+ "\013discontinue\010skipping", "\007totaled\010referred\007locates", 0, 
+ "\006abuser\013quarantined", "\004urge", "\011detective", 0, 0, 0, 
+ "\006levels\010finances\006lovely", 0, 
+ "\004many\015premeditation\011surcharge", "\011denigrate", 0, 0, 
+ "\006entity\005saver\011demanding", 0, 0, "\007chapter", "\005raton", 
+ "\007charter\011nutrition", "\006likely", "\012everywhere\005spoon", 
+ "\010referrer\010bathroom", "\010official\011newspaper\010disputes\006trials", 
+ "\004nose", "\012prosecuted", 0, "\010spouting\010officers", "\012alcoholism", 
+ "\010research\007surface\007interne", "\010sporting", "\006sounds\006counts", 
+ 0, "\013instability\013booksellers", 0, "\006courts", 0, 
+ "\006stokes\006routes", "\011addresses", "\006saints", 
+ "\006stones\010disputed\007peridot", "\005human\012highlander\003jag", 
+ "\004long\012attracting", "\015authorization\010sneaking", 
+ "\006stores\011profanity", "\005grown", "\005proxy", "\013speculative", 
+ "\006flavor", "\007seventh\006combat\011candidacy\010untapped", 0, 0, 0, 
+ "\005lists", 0, 0, "\007reflect", 0, "\004lick", "\006layout", "\005piper", 
+ "\013sweepstakes", 0, "\007contain", 0, "\004join\010dramatic", 
+ "\006comsat\020responsibilities", "\005robot", "\012affiliates\003rew", 0, 
+ "\007reviews", 0, "\005array", "\010overture", "\004club", "\004dire", 
+ "\006busted", "\007printed\004clue", 0, 0, "\012blackthorn", "\010although", 
+ "\011residents\005occur", 0, "\003per", "\011operation", 
+ "\005video\006yellow", "\006switch\007finance", 0, 0, "\004dirt", 0, 
+ "\007defense\007pending\013recommended", "\007clients", "\007unclear", 
+ "\003gum", "\011reselling\011dissolved", 0, 0, "\014discontinued", 
+ "\010extended\005midst", 0, "\006shaped", "\010delivery", "\006shared", 
+ "\007wizards", "\007officer\004keen", "\003gob", "\004keep\013attentively", 
+ "\006almost", "\010ventures", "\005angle", "\006crisis", "\010delivers", 
+ "\006animal\010tolerant\005chase\011scarecrow", 0, 0, 0, 0, 
+ "\011tolerated\007thinner", "\007citadel", "\005enter", 0, 0, 
+ "\013comfortably\003zoo", "\010grieving", 0, 0, "\012millennial", "\003oat", 
+ 0, 0, "\007dispute", "\012incredible", 0, 0, "\010merchant", 0, 0, 
+ "\003dew\004tree\013professions\010inviting", "\011cigarette", 
+ "\010comments\007unhappy", "\010graphics\011recommend", 
+ "\014subscription\014administered", "\013preparation\011accidents\006owning", 
+ 0, 0, "\012department\010donation\012trademarks", 0, 0, "\005dated", 0, 0, 
+ "\007pencils", "\013unsurpassed", 0, 0, 
+ "\013misdemeanor\011simmering\011commuting", 0, "\010received\006tutors", 0, 
+ "\005beast", "\014quantitative", "\006carney", "\007overall", 0, 0, 
+ "\012delusional", "\012disruption", 0, 0, "\006measly", 
+ "\013resolutions\010patterns", "\017recommendations", 0, "\003pin", 0, 
+ "\011resources\012coordinate", 0, "\003not\007prairie", 
+ "\010receives\012afterwards", "\010receiver", "\010brothers", 0, 
+ "\011computing\003cay", "\011producers\004pere", "\006sperms\007smaller", 
+ "\005track\006ethnic", 0, "\003off", 0, "\004perk", 
+ "\007deliver\005faces\013combination", 0, 0, "\003loo\010enrolled", 0, 
+ "\011publicize\011timeshare", "\005rings", "\011involving\010deadbeat", 0, 0, 
+ "\011believing\006exacta\011whirlwind\007rampant", "\010grinding", 
+ "\010criteria\011dentistry", "\011visionary", 0, "\005facts\003lid", 
+ "\012television\012propaganda\013maliciously", "\005night", 
+ "\004free\011awareness", "\006dialup\011foolishly", 0, "\007starved", 
+ "\003fix\007courage", "\012eliminator", 0, "\007already", "\011intention", 
+ "\007venture", "\007comment\006filthy", "\006double", 0, 0, "\006goalie", 
+ "\003own", 0, "\003hoe", 0, "\010visitors", "\005earth\012translates", 
+ "\010diseases\011recovered", 0, "\004zoom", "\003boy\006impose", 0, 0, 
+ "\011wrinkling", 0, "\004zoos", "\007regular", "\010properly", 0, 
+ "\007neutron", 0, 0, "\003bin", "\013expressions", 0, 
+ "\010offended\006bumper\011arguments\005bowel", 0, "\007graphic", 
+ "\015alternatively", 0, "\006roller", "\010returned\007roofing", 0, 0, 
+ "\005trace\006reader", "\007receive\007process\005trade", "\004rise", 
+ "\005flock", "\010policies", 0, "\012originator\006spread", "\005essay", 
+ "\004risk", 0, "\006trance\004engr\006whilst", "\007removes", "\006points", 
+ "\010offender\014combinations", "\006poised", 0, "\007martial", 
+ "\014constructive", "\004pine\004nota\012comparison\005filth", 0, 
+ "\006market\006reject\004ping\014highlighting", 0, "\004note", 
+ "\011presently\003lam\007pattern", "\004pink\014dissertation", "\007evident", 
+ 0, "\011penthouse\011provinces", 0, "\007concord", 
+ "\007besides\010erasable\012linguistic", "\007slicker", "\004pins", 
+ "\007brother\004pint", "\007fortune", "\015incorporating", 
+ "\007ongoing\005miter", "\012conversion\013procurement", "\003awn", 0, 
+ "\011joysticks", 0, "\003kpc\005wives\005gusto\012constantly", "\005tuner", 
+ "\004look", "\006diving", "\004weak\005hence\004offs", 0, 0, 
+ "\004loop\006seemed", "\007visitor", "\010shocking", "\007disease", 
+ "\004wear\003oil", "\005uncle", "\003hac", "\013conjunction", 0, 
+ "\012animations", "\007krypton\006detect", 0, "\006tissue", "\007clearly", 0, 
+ "\003adz", 0, 0, 0, 0, "\005hours\003mig", 0, "\005south\006leader\004disc", 
+ 0, "\006lacked", "\007testing\011describes", "\011excluding\012worthwhile", 
+ "\004dish", "\006agrees", "\010lifetime\014appreciation", "\004disk", 
+ "\012consumable\011utterance", "\013submissions\005frame\005revel", 
+ "\005grand\004owns\006magnum", "\012ammunition\011publicity\011enclosing", 0, 
+ 0, 0, "\010striving", "\006campus\004bind\010angstrom", 0, 0, "\004boys", 
+ "\005ayers\011immensely\007veteran", "\006senior\003gun", "\005scams", 
+ "\006superb", "\011fingering", 0, "\007conveys\011condition", 
+ "\005abuse\012convenient\006darned", 0, "\006recent\007backups\004pawn", 0, 
+ "\006sensor", "\007offices\006lingua", "\007physics", 0, 0, 0, "\005naked", 
+ "\010circuits", "\011donations", "\007boulder\012accelerate\007turnkey", 0, 
+ "\005title\003lei\007abysmal", "\005grins", "\007expires\010contents", 
+ "\007legible", "\012agreements", 0, 0, 0, "\011deduction", "\013illustrator", 
+ "\003ail\012validation\007labeler\004lame", "\003cud", "\007lengthy", 
+ "\007sanders", "\015significantly\010lesbians\011syndicate", 0, 
+ "\017interconnection", "\006cogent", 0, "\005legit", "\006higher", "\004lamp", 
+ "\006during", "\007largest\012candidates\006solved", 
+ "\006psycho\013transparent", 0, 0, "\005house\007belongs", 0, 
+ "\005added\003map", "\007answers", "\007soaring\014insufficient", 
+ "\015transmissions\006ending", 0, "\007potency", "\006earner", 0, 
+ "\007mapping\007locally", 0, "\006formal\003bes", 
+ "\004suit\010misprint\005shelf\010mornings\007varying", 0, 
+ "\011satisfied\012motorcycle", "\007ethical\003rit", "\004more\006useful", 
+ "\010helpless", "\007romance", 0, "\006asking\004hack", "\013encountered", 
+ "\011asterisks", 0, "\010notified\011academics\006module", "\012calculated", 
+ 0, 0, 0, 0, 0, "\004mort", "\012discipline\003rug", "\007enabled", 0, 
+ "\010offshore\011completes", 0, 0, "\010confused", 0, 0, 0, "\012nationwide", 
+ "\005crime\007loyalty", 0, "\010amethyst", "\005raise", 0, "\003pub", 
+ "\012deliveries", 0, "\007circuit\004peso\003lop", "\010pointing", "\003egg", 
+ "\011embarrass", 0, 0, 0, "\003eye", "\012compatible", 0, 
+ "\003ass\010remotely", 0, "\003lie\010disabled", 
+ "\017technologically\013cornerstone", "\013methodology\011rendering", 
+ "\004guns", "\007lesbian\007seasons", "\006roving\010enclosed", 0, 0, 
+ "\011encounter", "\007coupons\013voluntarily\007cottage\013researching", 
+ "\010reliable", 0, 0, "\005sheet", "\007artists", 0, "\011fantastic", 
+ "\007stalker", "\006assume\010function", 
+ "\012processing\007consent\010ceremony", "\007content\005after", 
+ "\012associates\011messaging\004drag", "\011subjected\007convent", 0, 
+ "\004ugly", "\012herbaceous\011instances", "\007accused", "\004dram", 
+ "\005storm", "\007visible", "\007morning\006listen\007silicon", "\005place", 
+ "\007credits", 0, "\006friend\004ails", "\005flags", 0, 
+ "\007husband\004draw\006juices", "\005quote\005tired", "\003bio", 
+ "\012executives\003dug", "\011consumers", 
+ "\006carbon\005plane\014artificially\012infidelity", 
+ "\007justice\010forecast", 0, 0, 0, 0, "\005plate", 0, 
+ "\014confirmation\006breath\006rooted", 0, 0, 
+ "\004maps\006polite\011happiness", 0, 
+ "\005items\006repeat\011recruiter\013remorseless\007deepest", 0, 0, 
+ "\005mimic\003axe", "\005deeds\012instructor\005needy\006truths", 
+ "\012themselves\006carton", 0, "\007remover", "\007patches", 
+ "\004best\012physicians\010truthful", "\010querying\013exaggerated", 0, 
+ "\010outgoing", 0, "\013accelerator\007optical", 0, 
+ "\011custodial\014productivity", 0, "\012selections", 
+ "\006amazon\007briefly\014geographical", "\011backstage\007confuse", 0, 0, 0, 
+ 0, "\011violation", "\011respected\012classmates", 0, 0, 0, 0, "\007crammed", 
+ "\006threat\006relies\011influence", 0, 0, 0, "\010thinning\007goodies", 
+ "\007disable", "\011enlarging", "\007metrics\010aborting", "\006wafers", 0, 
+ "\011competent\010speaking\006throat", "\012perfection", "\007asserts", 
+ "\004pubs\004lied", "\011personnel\007flagged", "\006helper", 
+ "\007expound\011lacquered\005blade", 0, 
+ "\004joke\004webs\012sufficient\007enclose", "\003had", 0, "\006raging", 
+ "\006inject\005fired\006nailed", "\004eyes", 0, "\006looked\005blame", 0, 0, 
+ "\004lies", "\006snooze", "\003rey", "\012integrated\012corrective", 
+ "\011prejudice", "\014appreciating", 0, "\004grid", 0, "\012commission", 
+ "\005blaze", "\010receipts\015inexpensively", 0, "\007aliased", 
+ "\011passwords", "\015independently\007glasses", 0, "\004full\010nowadays", 
+ "\014unrecognized", "\004grip", "\005slick\012proportion", "\010material", 
+ "\006ignite", "\010searcher\011surviving", 
+ "\010searches\011clearance\007repairs", 0, "\011coalition", 0, "\005solid", 
+ "\013medications", "\004melt", "\005dolls", 0, "\012micrograms", 
+ "\003neo\011pleasures", "\005metal", "\005proof", "\005blitz\011grandsons", 
+ "\006intend\007obvious", "\005amino", "\003wag", 
+ "\013competition\013atmospheric", "\005purse", "\005dials\007costume\004axed", 
+ "\010clements\011ministers", "\007stating\010searched", "\007gaining", 
+ "\011currently", "\010particle", "\007pendant", "\007staying", 
+ "\011merciless", 0, 0, "\005areas\013redirection\006treble", "\005jumpy", 
+ "\003ins\013conventions", "\015encouragement", 0, 
+ "\007quickly\010normally\004land\011starlight", "\003aim\012curriculum", 0, 
+ "\006adding", "\010erectile", 0, 0, "\004lank", 0, 0, "\012instrument", 0, 0, 
+ "\014unsuccessful\010nastiest", "\006brands", "\010annually", 
+ "\005ideal\012navigation\007utterly", 0, "\010secretly\010redeemed", 
+ "\004plug\005hates", "\007outputs", "\006easier", 0, "\011typically", 
+ "\004jail", "\005cable\005saves", "\012directives", 0, "\006having", 0, 
+ "\003bet", "\004plus\007receipt\012enrollment", "\006easter", 0, 
+ "\012components\010secondly", 0, "\012letterhead", 0, "\012sponsoring", 0, 
+ "\005cured", "\011forefront\011companion", 0, "\006sizzle\011disproved", 0, 0, 
+ "\012increasing", 0, "\005drool", 
+ "\006forget\005turns\004moss\007embassy\005burne", "\004most\005speak\003pip", 
+ "\014personalized\005olive\003ruh\010lawfully", 0, "\005jumbo\011directors", 
+ "\012discounted\012algorithms", "\011container\006firmer", "\011franchise", 0, 
+ 0, "\011languages\011egregious", "\003ems", 0, 0, 0, "\014provisioning", 
+ "\010collects", "\010survives", 0, "\003gab\006quartz", 
+ "\010appetite\004kick", "\004snip", 0, "\011notebooks\007manuals", 0, 
+ "\011surprised\004pets", "\013cholesterol", "\014astonishment", 0, 
+ "\010fetishes", "\005ocean\006bundle", "\011character", "\010watering", 
+ "\004wage", "\010matching", 0, "\010filtered\007proudly", "\012preserving", 0, 
+ "\006dozens", 0, "\006spring", "\005kooks", "\003ami", 
+ "\013outstanding\011invention\010salesmen", "\011automatic", 0, 
+ "\011consuming", "\005error\014competitions\011registers", 0, 
+ "\005gecko\012simplified\005strap", 0, "\007results\004blur", 
+ "\003hog\013penetration", 0, "\011underwear", "\012remarkable", 
+ "\006glossy\003fum", "\005again\005strip", "\011formatted", "\011blackjack", 
+ "\006income", "\003spy", "\013nutritional\010retained", 0, 0, 0, 0, 0, 
+ "\003fob", "\011designing\004jeep", "\003ere", "\007fucking", 0, 
+ "\011fireworks", "\003pay\006copies", 0, 
+ "\010retainer\015intentionally\011hamburger", "\004beta\006budget", 0, 
+ "\006tables", "\013accessories", "\013effectively", "\010stashing", 
+ "\006reduce\010affluent", "\005finch", "\005windy", 0, 0, "\005cuffs", 0, 0, 
+ "\013bloodstream", "\007collect\010packaged", 0, 
+ "\010business\006orgasm\006prison\006wildly", 
+ "\004able\004bets\016conservatively", "\011celebrate", 0, "\011apologies", 
+ "\011everybody", "\007washing", "\010packages\004pipe", 
+ "\006senate\007biology", "\010building\011elections\006affirm\011tradition", 
+ "\003six", 0, "\011canonical\005peace", 0, "\007getting\007instant", 0, 
+ "\010validity", "\007jasmine", "\007wasting\006telnet\006screed", 
+ "\006surfer", "\011attempted\006mighty", "\012provisions", 0, "\007replied", 
+ "\005carry", "\005lives\010relaying", "\007tunnels", "\005booth\005fears", 
+ "\011worthiest", "\006bloody", "\006boring\006luxury", 0, 0, "\011oceanside", 
+ 0, "\007survive\005spots", 0, "\010outlined", "\007organic", 
+ "\004life\015chiropractors", "\010currency", "\006wished", 0, "\010schedule", 
+ "\011construed", 0, "\003mot", "\005loyal", "\007options\006hungry", 
+ "\010graduate\011qualified", 0, 
+ "\003bay\005visit\006longer\011difficult\014legitimately", 0, 0, 
+ "\003uno\004iron\004lift", "\007cameras", "\004than\011proceeded\010cultured", 
+ 0, "\012compelling", "\014conversation\012sweetheart", "\014metaphysical", 0, 
+ "\004that\004hurt", "\011refinance", "\005hills", "\011departure", 0, 0, 0, 
+ "\005chick", "\010swapping", 0, "\005intra", 0, "\003kid", "\004expo", 
+ "\005might", "\003yam", "\004memo", "\011execution\010tickling\005sinus", 
+ "\006insert\006unsent", "\006arrest\010clothing\006belive", 0, 0, "\005cargo", 
+ 0, 0, 0, "\013opportunity\006murder\007grouper", "\011buildings", 
+ "\004also\007package", "\010suggests", "\014psychologist", "\005teams\003wah", 
+ "\012receivable", "\004pays", "\005lusty\015reimbursement", 0, 
+ "\011extension\005named\013combustible", "\006orally\011valuables", 0, 
+ "\011bachelors\013proprietary", 0, "\010nominate", 0, "\006debate", 0, 0, 0, 
+ "\006buying\011offending\014undetectable", 0, 0, 0, 0, "\010holsters", 
+ "\010guzzling", 0, "\010animated", 0, 0, "\011receptive", 0, "\006jacket", 
+ "\003zig\013transformer\010erection", "\007sitting", "\010affinity\005bilbo", 
+ "\013appreciated", "\006months\011forwarded", 
+ "\011described\005draft\013fundamental", "\007cabling", "\010defender", 
+ "\005judge", "\003mar\006thrall\007posters", "\005cache\004aero", 0, 
+ "\010medicine\006strata", "\007counted", 
+ "\006cruise\007outline\012disconnect", "\005craps", "\007devices", 
+ "\014occupational\006thrill", 0, "\007lenders", "\013distributed", 
+ "\006models\006relate\013switchboard", 0, 0, 
+ "\010specific\013girlfriends\003tun", "\006resell\005forum\005brush", 
+ "\012uninformed", "\007reaches\006micron", "\010analysis", "\012nutritious", 
+ "\014acquaintance\007affairs\010homework\007tragedy", 0, 0, 
+ "\007culture\007forfeit", 0, "\004stud\012allocation", 0, "\010selfless", 0, 
+ "\004whip\015oceanographic", 0, "\010identify", 0, "\003now\010cheaters", 0, 
+ "\012counselors", "\004kook", "\006giving\010grabbing", 0, 0, "\006peeled", 
+ "\007suggest", 0, 0, "\012strengthen\010sniffing\006packer", 
+ "\006spears\003nil\010guardian", "\010giveaway\003pud", 
+ "\005space\010internal\011represent\012accounting", "\006esters", 
+ "\015revolutionary", "\005rough\005junto", "\003wed\011cancelled\006dinner", 
+ 0, 0, "\010warnings", 0, "\004kids", 0, "\005guild", 0, 
+ "\007illegal\010embedded\006harder", "\006wolves", "\005spare\013hexadecimal", 
+ 0, "\005grade\010globally\003zap", 0, "\006indent\010learning", 0, 
+ "\012indicators", 0, "\006united\012ordination\010indecent\013sympathetic", 0, 
+ "\006jilted", "\006orange\005seven\010responds", "\006cleans", 
+ "\006proper\016administrators\007depends", "\004goes\005grape", 0, 0, 
+ "\007unnamed", 0, "\005fraud", "\007mansion\012continuous\007raunchy", 
+ "\007appears\014infiltrating", "\004intl", "\003fun\007maxwell\007laptops", 
+ "\006sexual\012pheromones", "\004into\006movies\012collection", 
+ "\013disapproved", 0, "\003men\013requirement", "\006earned", "\007jealous", 
+ 0, "\011electrons", "\015miscellaneous", 0, 0, "\012equipments", "\005cites", 
+ "\007secrets\007portals", 0, "\004head\007seconds", 0, 0, "\005shock", 
+ "\014fundamentals\005crimp", "\012confidence", 0, "\007cultist\006rewind", 
+ "\006cation\004heal", 0, "\005crisp", 0, "\004mars\006killer\011evolution", 
+ "\011education\007rolling\004mart\004tune", "\004hear", 0, 
+ "\010becoming\010referral\004heat", "\007postage\011installer", 0, 0, 
+ "\011interfere\013dysfunction", 0, 0, "\011professor\007toppled", 
+ "\011giveaways\005uncut\012remittance", "\010educated\011triggered", 
+ "\011processor\015exhibitionism", 0, "\007chester\003wok", 0, "\010original", 
+ "\011pickering", "\007warming", "\007warning\003lap", "\011smoothing", 0, 
+ "\010insanity", "\011blessings", "\004ruin", "\006broker", "\003eke", 0, 
+ "\007species", "\011ascendant", 0, 0, "\012struggling\003tri\012indirectly", 
+ "\004lord", "\004lore", "\010capsules\011alternate", 0, "\004nill", 
+ "\010provider", "\010provides", "\013directional", 0, 
+ "\006priced\012activating", 0, "\010upcoming\007jumping\010impulses", 0, 0, 0, 
+ 0, "\005ulcer", "\007anymore", 0, "\010provided\006primed", 0, 0, 
+ "\011printable", "\005relay\010requests", "\014requirements", "\011envelopes", 
+ "\011circulate", "\013consumption", "\010audition\007suspend", 
+ "\007forever\005bring", "\005dirty", "\012especially", 0, "\007respond", 
+ "\004skep", "\006street", "\004fund\010holidays\006mainly", 
+ "\005multi\010cassette", "\006choose", 0, 0, "\011installed", 
+ "\006strive\012whirlpools", "\003dye", "\007bullets\010infected", 0, 
+ "\007primero", "\011harvested", 
+ "\012protection\011homestead\005imply\007bankers", 0, "\010securing", 
+ "\007banners\005kiosk", 0, 0, "\007handles", "\006demand\005tonic", 0, 
+ "\013responsible", "\005cubic\004menu", 0, "\006rivers\012analytical", 
+ "\010expended", "\010planning\013exceptional", 0, 
+ "\004crab\004alto\012prediction", "\005their\010expanded\011creations", 
+ "\005sever", 0, "\011vanishing", "\005dicks", 0, 0, 
+ "\013undervalued\011exploring", 0, "\013illustrated", "\011luxurious", 0, 
+ "\011followups", "\004crap", 0, "\007claimed", 0, "\006outlay", 
+ "\004buds\011arthritis\015technological", "\012repeatedly", 0, 0, 0, 
+ "\007pacific\011sweetness", "\013constitutes\012monumental", "\007routers", 0, 
+ "\010fighters", "\013millionaire\011extremely\010trespass", "\006reward", 
+ "\005glass", 0, "\004trib", 0, 0, "\006pieces", "\007coveted", 0, 0, 0, 0, 0, 
+ "\010symptoms", "\005snaps\004trim", "\006wisdom\005maize", 0, "\004trip", 0, 
+ 0, "\005upset\010bookmark", "\007holiday\005maxim", 0, 
+ "\011cassettes\014surprisingly\007capsule", "\010imported", "\007provide", 0, 
+ "\010gathered", "\010superior\007impulse\005zones", "\007handful", 
+ "\010relative", "\004soap", "\007natural", "\013temporarily\004soar", 
+ "\012technician", "\005fetch", 0, 0, "\006remote\006utmost", 
+ "\007drivers\003tod\011curricula", 0, 0, "\010importer", 0, 0, 
+ "\012measurable", 0, "\011emotional\010speeding\010fashions", 0, 
+ "\007raising", "\007dollars", "\013prospective", "\003emu", 
+ "\003ana\011universal\003gym", "\010spending", "\010visiting\010connects", 
+ "\006scales", "\010statutes", "\006urgent\012fingertips", "\010boarding", 
+ "\013sensitivity\004yang", "\005hover", "\003los", "\006update", 
+ "\006stated\010fingered\006recipe\011consensus\003wee\004dyes\006meadow", 
+ "\007postman", 0, "\006little\010consider\007borders", 0, 0, 0, 
+ "\006grants\011transport\010markedly\010dividing", 0, "\010romantic", 0, 
+ "\005clave", "\012importance", "\014notification", "\004cite\005argue", 0, 
+ "\007rotunda", "\017representations", "\013substantial\003hut", 0, 0, 0, 
+ "\007ventura", "\007rigidly", 0, "\004wait", 0, "\012incredibly", 
+ "\011voyeurism\006anchor\006strain\012cumbersome", "\007omitted", 
+ "\006except\007stroked", "\003eft\006snails", 0, "\004smut", "\004city", 
+ "\005herbs", 0, "\012depictions", "\005heres", 0, 0, 0, 0, 
+ "\006pretty\006wallet\007wonders", 0, "\005ebony", "\011relatives\005solve", 
+ "\007quarter\012executable", "\014millionaires\005prove\003erg\007blossom", 
+ "\005gurus", "\013enterprises", "\005berry", "\006limits", 0, "\006expire", 
+ "\006player\011suppliers\004mask", 0, "\006phrase", "\006create\011identical", 
+ "\005types\010fraction\005ended\004void", "\007fashion\007signals", "\003tab", 
+ "\013calculating", "\010explorer\004mass\006savior", 
+ "\007banking\004mast\014reproduction", "\007pursuit", "\014overspending", 
+ "\011surprises", 0, "\007worries", 0, 0, "\007connect", "\007weavers", 0, 
+ "\007transit", 0, 0, 0, 0, 0, "\014consequently\015corresponding\010shipment", 
+ 0, "\011considers", "\012astrologer", 0, "\012nomination", 0, 0, 0, 
+ "\012guarantees", 0, "\011certified\006locals", "\007profits", 
+ "\015relationships", "\005await", "\006exceed", "\004anal", 0, 
+ "\004lose\003sum\012privileged", 0, "\006wonton", "\007statute", 0, 
+ "\007slipped", "\012simplicity", 0, "\004week\005blink", "\005broke", 0, 0, 
+ "\015entertainment", "\014architecture", 
+ "\004loss\007proctor\016scientifically", "\004lost\006galore", 
+ "\014reassignment", "\011satellite\012metabolism\011centrally", 0, 
+ "\005speed", "\011hierarchy", "\005crowd\006replay\012attendance", 
+ "\005admit", 0, "\011receiving\006seized", "\006mirror\007urgency", 
+ "\014transitional", 0, "\005spend\006dulles", "\005hinds", "\010disorder", 0, 
+ 0, 0, "\010totaling", "\012resumption", 0, 0, 
+ "\006desire\006reside\011threshold", 0, "\010accident", 0, 
+ "\007explore\012pharmacies", "\007managed", "\011explorers\003pew\006draper", 
+ "\004glad\005hints", 0, 0, 0, "\005press\016commercialized", 
+ "\013association\013researchers\004bird", "\006drawer", 0, "\010filename", 0, 
+ "\011sovereign", "\005epoch\012assumption", 0, 
+ "\006artist\013shareholder\005alloy", "\010produced\004meow", 
+ "\011abundance\004ergo", "\006opting", "\005arrow", 
+ "\012newsletter\005doors\010revealed", "\010campuses", "\005sewer\005baton", 
+ "\012generating", "\006charts\003gog", 0, 0, "\004nave\007arising", 0, 
+ "\010honestly", 0, 0, "\014relationship\007kingdom\012hesitation", 0, 
+ "\004tabs", "\006livery\007wildest", "\010strictly", 
+ "\010boundary\007dubious", "\006ground\006drills\006writer\010producer", 
+ "\006annual\011collector\010produces\003inv", 0, "\004size", "\010revealer", 
+ 0, "\007torpedo", "\010backpack\011analyzing", 0, "\004navy", "\005whats", 0, 
+ "\007accepts", 0, 0, 0, "\015vulnerability", "\006muscle\010withdrew", 
+ "\010suburbia", 0, "\012activation\010patients", 
+ "\011averaging\012influenced", 0, 0, "\003mat", "\011calendars", 
+ "\011poinciana", "\010drinking", 0, "\011factoring", 
+ "\006animus\004hype\010heritage", 0, "\007because\006native", 0, "\004sums", 
+ "\007surgery", 0, "\006select\011emergency", "\012subscribed\005stand", 
+ "\004move", "\011liability\010neighbor", 0, 0, 0, 0, "\010quitting\006giants", 
+ "\007pussies\014associations\007tasting\006relied", "\006oliver", 
+ "\006hardly", 0, 0, "\003toe\007spelled\007stretch", 0, "\005liver", 
+ "\005loose", "\011evaluated\006aiding", 
+ "\011objective\014shareholders\006tenure", "\006metric", 0, "\007flooded", 0, 
+ 0, 0, 0, "\004eden", 0, 0, "\011alliances", "\012vigorously", 0, 0, 0, 0, 0, 
+ "\003lot", 0, "\006enough", 0, "\007produce\004shah\007thunder\011rectangle", 
+ "\010entrants\010hastings", "\005depth", "\005risks\011gardening", 0, 0, 0, 
+ "\011introduce", 0, 0, 0, 0, "\006minute", "\012approached", 0, 
+ "\005drugs\007stories\015effectiveness", 0, "\010emission", 0, 0, 
+ "\014protactinium", "\007patient\010actively\007senders", "\005shift", 0, 
+ "\006repair", 0, "\005light\014contributing", "\007unlocks", "\005child", 0, 
+ 0, 0, "\012advisement", "\005react", "\005shirt", "\006scribe", 
+ "\012connection\005nexus\012authorizes", 0, 0, "\007targets\012variations", 
+ "\007perhaps", "\007blocker\006burger", "\011neighbors", "\005known", 0, 0, 0, 
+ "\004mate", "\006burner", "\005cease", "\004math\012yourselves", 0, 0, 
+ "\010concerns\004acer", "\005alert\004aces", 0, 
+ "\007company\004heck\007jitters", 0, 0, 
+ "\006ensure\007clarity\010jackpots\012discreetly", 0, 0, 0, 0, 0, 0, 
+ "\003naw", "\011explained", 0, 0, "\011readiness", 
+ "\010archived\011rejection\015functionality", "\011competing", 0, 
+ "\012pertaining", 0, 0, "\006packed", "\006victim\011rewarding", 
+ "\006output\012graduation\006shiver", 0, 0, 0, "\005audio", 0, 
+ "\011purchased", "\011promotion\010archives\007rounded", "\011perfected", 
+ "\010reversed", 0, "\007nagging", "\004piss\006trolls", "\006gallon", 
+ "\004nine\010clinical", "\010greatest\012responding\006modems", 
+ "\005sorry\005crawl", "\005myers\011worthless", 
+ "\003sun\006flyers\007flowing", "\003jam\003ply", "\010screened", 
+ "\006carpet", 0, 0, "\011seductive", 0, "\011unlimited", 0, "\011trebuchet", 
+ "\010magnetic\010spanking\012oxygenated", "\010reverses", 0, 
+ "\004lots\011inclusive", "\011intending\006thrown\007revoked", 0, "\003hah", 
+ "\006offers", "\005lunch\003mow", 0, 0, "\005fully", 0, "\010screener", 
+ "\004sqrt\012interstate", "\005loads", "\007tsunami", "\012homeowners", 
+ "\006attend", "\007brought", "\003mil", "\006bonded", 
+ "\012decoration\014transferring", "\010absolute", 0, "\005guide\005loans", 0, 
+ 0, "\007concern", "\006colors\010emotions", "\006traded", "\006legacy", 
+ "\005mount", "\010laughing", "\007jackpot", 0, 0, "\007doubled\005apart", 
+ "\011generated\010arriving", "\006killed", "\007booking\006backed\007exiting", 
+ "\014desirability", "\007booming", "\005topic", 0, 0, "\006cotton", 0, 
+ "\005never", "\011exchanges", "\006covert\007wording", 0, 0, 
+ "\007issuing\015constellation", "\010injector", "\012moderators\007archive", 
+ "\006beaver", "\007working", "\013established", "\013incompetent", 0, 
+ "\012alienation\014osteoporosis", 0, 0, 0, "\005issue\011exploding", 
+ "\005diets\011enjoyable", 0, 0, "\011serialize\010nutrient", 0, 0, 0, 
+ "\007breathe\010nicklaus", 0, "\006mahler\016understandable", 
+ "\011practiced\007licking", 0, "\006patrol", "\006breeds", 
+ "\012commercial\007reverse", "\012incentives\016qualifications", "\006flight", 
+ "\006triple", "\010rosemary", "\006sooner\013enforceable", "\010football", 
+ "\007housing\011convinced", "\005write", "\011forbidden\006globes", 
+ "\012registered\003bra", "\004gear", "\005couch\004sung", "\012prevention", 
+ "\006purple", 0, "\012warranties\004sunk", 
+ "\011processes\016identification\010terrible", 0, 0, "\007solving\007redwood", 
+ "\011terrorism", "\005boats", 0, "\010possibly", 0, 0, "\003urn", "\004sock", 
+ "\015configuration\006gloves", "\005cheap\006framed", 0, 0, 0, 0, 
+ "\006potent\010tailored\010chandler\011residuals", "\010possible", 0, 0, 
+ "\010junction", "\007prodigy\011meanwhile\004mild", "\004quit\004mile", 
+ "\011exceeding", "\004face\011paragraph", "\003pit", 0, "\016electronically", 
+ "\005range\004quiz", "\006abused\004mill", 0, 0, 0, 0, 
+ "\010likewise\006census", "\007spacing", 0, "\014considerable", 
+ "\013disappeared", "\004fact\011resurrect", 
+ "\010promoted\006esteem\006vanity", "\004pray", "\003pug\006needle", 
+ "\012technology\010mismatch\015confirmations", 
+ "\006option\006titled\012pathfinder", "\005brief", 
+ "\005extra\010outbound\011telephony", "\007tenants", "\010national\005relax", 
+ "\012generation\005fails\011budgeting", "\007topless", 0, 0, 0, 
+ "\005print\007condone\005pulls", "\006enlace", "\012punishment", 
+ "\004wake\014considerably", "\003nub", "\005sugar\005nitro\011upgrading", 0, 
+ "\010promotes", "\010promoter", 0, 0, "\003the\010decoding", 
+ "\004once\004lend\007beneath\004nest", 0, 0, "\012meditation\010slowpoke", 0, 
+ "\010deciding", 0, "\007torture", 0, "\006titans", 0, 
+ "\014optimization\007seabird", 0, "\011technique", 
+ "\011therapies\015accomplishing", "\015dissemination\007alchemy", 
+ "\005ahead\011declining", "\003ext", "\007trained\014contribution", 0, 
+ "\005donor", "\016redistribution", "\012respecting", 0, 
+ "\010ignorant\013arrangement", "\005level", "\010literacy", "\010exploded", 
+ "\014transmission\004brag", "\011spillover", "\003bit\006entire", 
+ "\011calculate\011shrinkage\006monkey", "\013friendliest", "\010enabling", 
+ "\013instructive", 0, 0, "\006buster\011countries", 
+ "\006became\014specializing", "\013hospitality", "\007cutters", 0, "\003tad", 
+ "\013contractual", 0, "\006saddle", 0, "\003bug", 0, "\006memory\010breakage", 
+ "\006spacer\010explodes", 0, "\005email\006become\003ops\006raised", 0, 
+ "\007records\005tails", 0, "\011temporary\005saint\007pissing", "\007symbols", 
+ 0, 0, "\003won", "\004rule", "\007puzzles\010airports", 0, 0, "\003las", 
+ "\005feeds", 0, 0, 0, "\006lowest", 0, "\004road", "\007members\010occupied", 
+ "\007promote\005feels\012dedication", "\010friendly", 0, "\010steadily", 0, 
+ "\004loud", 0, "\006thrush\012unaffected", "\014introduction", 
+ "\006excite\010improves", "\007leakage", 
+ "\006ticket\010pleasant\011indicated", "\007savings\011logically", 
+ "\007impetus", 0, "\005cutie", "\010students", 
+ "\007scanned\017internationally", "\003sod", 0, 0, "\007sailing", "\005monte", 
+ 0, "\013possibility", "\015entrepreneurs", "\007society", 
+ "\006worlds\010struggle", "\007amounts\006salary\013bestsellers", 0, 
+ "\010existing\010improved", 0, "\014suspiciously", 0, "\004them\006typing", 
+ "\004then", "\011alongside", "\003mim\006rabbit", 0, "\003fad", "\007desktop", 
+ "\006bomber", 0, "\003vee", 0, "\006hiring\007planets", "\004they\003eve", 0, 
+ "\010watching\011telephone\007inverse", "\011responder", "\010detected", 0, 0, 
+ "\007explode", "\006pledge", "\006secure\006speedy", "\007corners", 
+ "\003ion\004bite", 0, "\006coupon", 0, 0, 0, "\010anywhere\006richer", 
+ "\006depend", "\007samples\010wrinkles\014arrangements\004dull", 
+ "\003net\006rocket\011safeguard", "\013troublesome", "\005slice", 
+ "\010families\005slide\012excitement", "\012permitting", "\006greedy\004bits", 
+ "\006appear", "\005teens", "\006laptop\012conference", 0, "\006ignore", 0, 
+ "\005folks\004duly", "\007greater\011displayed", "\007stuffed", 0, 0, 
+ "\005shall", "\007anyways\007workout", "\005thank\010settings", 
+ "\011connector", "\012thoroughly\012deficiency", "\005gross\004bugs", 
+ "\007percent\011letterman", "\005proud\007volumes", 0, 
+ "\006mailer\003air\005grows\010cordless", "\003use\007student", 0, 0, 
+ "\005pizza\013undoubtedly", "\007airport", "\006before", 
+ "\007sensual\012moneymaker", 0, "\005hobby\005stops\011primitive", 
+ "\004wont\012assistants", "\012attempting\006sorted\006expose\005bumps", 
+ "\006seeder\005vogue", "\011stressful", "\004lass", "\004last", "\006thrive", 
+ "\010sessions\005hagen", 0, "\015investigation\006seeker", 
+ "\012vulnerable\013dreadnought", "\006unfair", "\003hee", 
+ "\013affiliation\005trend", "\012consistent", "\010catering", "\010profound", 
+ 0, "\006tramps", "\011stimulant", "\007improve\010assuring", 0, 0, 
+ "\016administrative", 0, "\005users\012diagnostic", "\013explanation", 
+ "\012copywriter", "\012subscriber", 0, 0, 0, "\003via\015registrations", 0, 0, 
+ "\004hair", "\004mime\007focuses", "\005click", "\004fade", 
+ "\005broad\016recoverability", "\005acted\003rum", "\006prices\007locator", 
+ "\010promised\006rented\006prides", 0, "\007wrinkle", 0, 0, 
+ "\006pounds\013erroneously", "\005clink", 0, "\003and\010divorced", 
+ "\010database", 0, "\007genesis\011memorable", "\004even\012electronic", 
+ "\007streams\003nip", 0, "\003gag\011champions\005ember", 
+ "\004ever\010hardware\010harassed", 0, "\007rebuild", "\010pinpoint", 
+ "\011appointed", "\010promises\007elegant", "\006prizes\011intrusion", 
+ "\011uppercase", "\005scoop", "\006review\007supreme", 0, 0, 
+ "\005total\004ions", "\011contacted", "\005pumps\010harasser", 0, 
+ "\006assets\013cooperation", 0, "\005demon", "\004walk", "\004wall", 
+ "\010endeavor", 0, 0, 0, "\013anticipated", "\010acquired", "\005quack", 
+ "\010articles\006flicks", "\006eraser\010plethora", "\004flag", 
+ "\011downloads", "\007damages", "\004used\011allowable", 
+ "\007sellers\007setting\007outlets", "\005catch", 0, "\005eaten", 0, 
+ "\006nested", "\014carbohydrate", "\013maintaining", "\003mer", "\004flat", 
+ "\006wizard", "\010promptly\012reasonable\007funding", "\005boost\006terror", 
+ "\014verification\006shapes\005acres\006riding", "\004uses", 
+ "\006shares\003fog", 0, "\007pursued", 0, "\006height\006values", 
+ "\014registration\006valves", "\006reason", 0, "\011interface\007cardiac", 
+ "\007tossing", "\006butter", "\006bounds\013prosecution", 0, 0, 
+ "\007masters\012deployment", "\007session\006thirty\011desperate\005enemy", 
+ "\013description", "\010tomorrow\012definition", "\014necessitates", 
+ "\007attacks\010crescent\004turf", "\006finest", 
+ "\015conversations\010cleanses", "\005sharp\003nay", 0, "\011databases", 
+ "\003opt\012impossible\011dividends\010averages", 0, "\004turn\011structure", 
+ "\007morales", "\004volt", 0, "\007amongst", 0, "\003woo", 
+ "\007halcyon\010violated", "\007promise", 0, "\014necessitated", "\005noted", 
+ "\010confront", "\011struggles", 
+ "\007escorts\014construction\010prepared\012unmodified", 0, "\007divorce", 
+ "\012basketball", "\005stage\012affiliated", 0, 0, 0, "\005stake", 
+ "\005stale", "\004gage", 0, "\004love\003sup", "\005dying\007unheard", 0, 
+ "\020multimillionaire", "\010violates", 
+ "\005state\007article\007parents\004ands", 0, "\012qualifying", 0, 
+ "\013deliverable", "\016recommendation", 0, "\004like\007cyclone", 0, 0, 
+ "\005lunes\005moose", "\010tracking\006hugged\005screw", "\005lungs", 
+ "\010anabolic", "\005armed", "\005fixed\007tracker", 
+ "\010publicly\005funky\006belong\006bypass", "\006honest\005repay", 0, 
+ "\013specialized\005funny\007acquire", "\010cleaning\011enjoyment", 0, 
+ "\006fiance\003och\007context\006aerial", "\007mothers", "\010teaching", 
+ "\010ordering\010surround\007hunting", 
+ "\005owned\004hold\004skin\003min\007bundled", "\004hole\007towards", 
+ "\005reply\006avenue\004skip", 0, 0, 0, 0, 0, "\006tonnes", 
+ "\014descriptions\007enzymes\011commodity", 
+ "\004mere\006opened\011cautioned\011appliance\013accompanied", 0, "\003apt", 
+ "\013consultants", 0, 0, "\011medically\006zenith\007cleanse", 0, "\004dumb", 
+ "\011cognition", 0, "\004grow\004holy", "\003tea", 
+ "\010holistic\013disciplines", "\010blocking", "\011particles", 
+ "\010standard\011reproduce", "\006todays", 
+ "\012government\005trash\007sampler", "\011profiting", 
+ "\011beginning\005intro\010ministry", "\007phoenix", "\004dump", 
+ "\011similarly", "\007welcome", 0, "\010morality", 0, "\013interviewed", 0, 
+ "\010formerly", "\011beautiful", 0, "\011remainder\014accumulation", 
+ "\003one", 0, 0, 0, "\005karma", "\007prepare\010distress", 
+ "\006launch\007violate\012turnaround\006comedy", "\007resorts", 
+ "\012supplement", "\006empire\004crew", "\007average\007reduced\010demanded", 
+ "\004late\010combined", "\010intended\012supporters", "\007bastard", 
+ "\005civil", 0, 0, 0, "\004wide\011listening", 
+ "\012activities\014transmitting", 0, "\013exclusively", 
+ "\010combines\007playoff\012principals\010drainage", "\003sac", "\007hosting", 
+ "\005juice", 0, 0, 0, 0, "\003maw", 
+ "\012profitable\005incur\007camping\011trickling", 0, 0, 0, "\011motivated", 
+ 0, "\012concerning", "\011excellent", "\006casual", 0, "\005dildo", 
+ "\013reinventing", "\010agencies\012recovering", "\011interview\011cookbooks", 
+ "\007peoples", "\010nuisance", "\013discussions", "\015embarrassment", 
+ "\010interior\010rewarded", 0, "\010soldiers", "\014unbelievably", 0, 0, 0, 
+ "\004mind\010assemble", "\004mine", 0, 
+ "\007himself\006driven\013progressing\004ming", "\011determine", 
+ "\003run\010question\004mini\007retails", 0, "\006discos\004unto", 
+ "\006groups\011formation\014unbelievable\013photography\010memorize", 
+ "\007shrinks", "\003yen\006relive", "\007rubbing", "\006hijack", 0, 
+ "\015approximately", "\010underage", "\005trick\004mint", "\003roc", 0, 
+ "\004yard\007crowder\010survivor", "\006scenes", 0, "\011sincerity", 0, 
+ "\003low", "\006teenie\005bible", "\006donate", 0, "\010assembly", 
+ "\013permanently\005blend\006sphere", "\007remarks\004teal\006naming", 
+ "\004team\010vitamins\004snow", 0, 0, "\006twelve", "\003ora", 
+ "\014northwestern", "\005pouch", "\006laguna", 0, "\011monitored", 0, 
+ "\010counting\005sunup", "\006unless\014customizable", 0, "\006emblem", 
+ "\011irritated", "\007combine", 0, 0, 0, 0, 0, "\007callers", "\005tract", 0, 
+ "\007cooking", 0, "\006invest\013assumptions", "\004ones\012recreation", 0, 0, 
+ "\006indeed\012chemically", 0, "\012contracted\013advancement", 
+ "\013newsletters\011exchanger\013interchange\010warrants", "\011fertility", 0, 
+ "\004ache", "\013receivables", "\006famous\005erase", 
+ "\006though\006vendor\007reposts\011exception", "\010platform", 
+ "\011achieving\011principle", "\007imitate", "\007several\010warranty", 
+ "\007details\012downstream\003dun\006enters", "\007markers\012limitation", 0, 
+ "\010yourself\010sexually", "\013frustration", "\011deploying", "\007however", 
+ 0, "\005sieve", "\010envelope\006behalf\007gallery\007affords", 
+ "\010lavender\005tribe", "\010previous\007granite", 0, 
+ "\003you\013proportions", 0, "\012appreciate\010prospect", 
+ "\005takes\011interests", 0, "\011expanding", 0, "\006client", 
+ "\005prima\007gesture", "\007viewing\015undergraduate\007routine\010precious", 
+ 0, "\012controlled\020characterization", "\013archaeology", "\010resolved", 0, 
+ 0, "\006poorer\011petroleum\003wop", "\004rune", "\004fear", 
+ "\012distribute\006mental\005wafer", "\007related\010shooting\004feat", 
+ "\007vitamin\015personalities", "\005shore", 0, 0, "\007somehow", 
+ "\013appointment", "\006turned\007reloads", "\012situations", 0, 
+ "\010resolves", "\004runs\011teachings", 0, "\006weekly", 
+ "\005yours\011resulting\012hesitating", "\010whopping", 
+ "\005empty\012purposeful", "\010increase", "\006garlic", 
+ "\006agreed\011dependent\006boxing", "\013metabolizes", 0, "\006learns", 0, 
+ "\007suspect", "\006jumped\007surplus\004anew", 0, "\007england\007forcing", 
+ "\010followup", "\011financial\004nude", "\012introduced\013landscaping", 
+ "\007forging", 0, "\004each", "\005where\004oral\012sensations", 
+ "\012equivalent", "\016disappointment", 0, 0, "\003who", 
+ "\012population\013conclusions", "\012membership", 0, "\016considerations", 
+ "\011complying", 0, 0, "\005emacs", "\004home\004lily", 0, 
+ "\014satisfactory\005hitch", "\007mystery\004amor", 
+ "\005sites\012containers\004fuse", "\004mesa\010tangible\006sauces", 
+ "\007experts", "\006inform", "\006safest", "\005gains\007anybody", 
+ "\010mortgage\007massive", 0, 0, "\007copiers", "\006boomer", 0, 
+ "\006struck\011explosive", 0, "\006cheats\004fuss", 0, "\006tigers", 
+ "\003hrs\011batteries", 0, "\003she\006rights\007endless\004mess", 0, 0, 
+ "\003bye", "\005sweat", 0, "\010honoring\007envelop\015clarification", 0, 
+ "\005sweet", "\005event", "\010unstable", "\003wan\012providence", 0, 0, 
+ "\006legion", "\004dock", "\013consummated", 0, "\010unlawful", 
+ "\007private\005weber", 0, 0, "\007resolve", 
+ "\004your\011erections\014availability", 0, "\005voice", "\005fonts", 0, 
+ "\013copyrighted\011marijuana", 0, 0, "\007lawyers", "\012precedence\004opus", 
+ 0, 0, 0, "\003sym\010periodic\010expedite", "\006twenty\010stickers", 0, 
+ "\005ficus", "\012originated", "\007shipped\007distant", "\010diplomas", 
+ "\003sad\007monster\006tracks\003gnu\010withdraw", 0, 0, 
+ "\010category\014appointments", "\007calling", 0, 
+ "\013promotional\014redundancies", "\003max", "\011completed\006beings", 
+ "\006rebels\010climaxes", 0, 0, 0, 0, 0, 0, "\010prostate\007parkway", 0, 
+ "\003von", "\007naughty", "\006choice\012production", "\013hierarchies", 
+ "\004whoa\003tut", 0, "\011mortgages\006frozen", 0, 0, "\010scenario", 0, 
+ "\006expiry\012encourages", "\004soft", 0, 0, 0, "\004whom\012benefactor", 0, 
+ "\010entitled", 0, "\007offered\004ough", "\005tones", "\010youngest", 
+ "\017congratulations", "\007bureaus\016insurmountable", 0, 0, 
+ "\011scrollbar\017pharmaceuticals", "\007doctors", "\010historic", 
+ "\005bonne", "\006remove", "\007pelican", "\015participation", 0, "\004edit", 
+ "\014truthfulness\012insulation", "\010claiming", 0, 0, "\006radios", 
+ "\010benefits\007starter\003ego", "\006splash\006affect\007colored", 
+ "\005falls", "\010surprise\010weekends", 0, 0, 0, 0, 
+ "\006decade\015documentation\007assures", "\012restraints", "\010restrict", 
+ "\014contributors", "\006traces\012skateboard", "\006trades", 0, "\006mailed", 
+ "\006decide\010politics", "\007diploma\013inquisition", 
+ "\007default\006cooked\007sticker", "\005plant\010corporal", "\003amp", 0, 
+ "\015breakthroughs\006teamed", 0, 0, "\004want", "\005crypt", 
+ "\005babes\007implied\011shrinking", "\010speakers\003ilk", 0, 
+ "\012withdrawal\006abuses", 0, "\003age\006passes", "\007serving\010stunning", 
+ "\005table", "\007hideout", 0, 0, 0, "\003met\006socket", "\007aligned", 
+ "\004acid", "\005skill\007provost", "\010bargains", 
+ "\005havoc\006reckon\014progesterone", "\011libraries", "\004cult", 0, 
+ "\006towers\007placebo", 0, "\014irresistible\004coal\003duo", 0, 
+ "\011classroom", "\011discloses", 0, 0, "\004gnus", 0, 
+ "\007renewal\004coat\005merit", "\005alder", "\006licked", "\003tag", 0, 0, 
+ "\010deposits\007vandals", "\006router", 0, 0, 
+ "\011sensitive\006blanks\013autographed\006birdie", "\015strategically", 0, 
+ "\012psychology", "\005talks\004vice", "\006wisely", 
+ "\010complies\010controls\007quicken", 0, 0, "\012historical", 0, 
+ "\007benefit\013continental\011attending", "\011converted\006fuller\005demos", 
+ 0, "\005wager", 0, "\011pituitary", "\011providers", "\007weekend\004isle", 
+ "\003gee", "\006commas\013overvoltage", "\006peeper", 0, 0, 
+ "\007maestro\004rode", "\012cartridges\006frames", 0, 0, "\010pedigree", 
+ "\005patch\011digestion\006styles", "\010reversal", "\011submitted", 0, 
+ "\004slap", "\012elementary", "\006rarely\007corpora", 
+ "\013convenience\010princess", 0, "\004gain", "\010pipeline\007speaker", 0, 
+ "\006condom", 0, "\004lime\011launching", "\007absence\007bottoms", 
+ "\007twister", 0, "\012heretofore", "\006bettor", 0, 0, 
+ "\010presents\006slowly", 0, "\007sinkers", 
+ "\007updated\012prohibited\006wounds", "\011genuinely", 
+ "\010prevents\010bankroll", 0, "\006proved\012advantages\007bargain", 0, 
+ "\012experience", "\003pre", "\007sucking\005chaos", "\011broadband", 
+ "\013importantly\003fag\006walnut", "\004hong", "\011editorial\006bergen", 
+ "\006habits\004amps", "\004meta", "\007outside\007terrace", 
+ "\006window\007chronic\007thereof", 0, 
+ "\007address\007sources\007courses\012millennium", "\010calendar", 0, 0, 0, 0, 
+ "\004vale", "\007deposit\004ages", "\006refuse\003dab\013credentials", 
+ "\011candidate\010flagship\005dotes", "\011secretary", "\013eliminators", 
+ "\007charity", 0, "\011attention", "\004type", 0, 0, 0, 0, 0, 
+ "\012accomplish\007builder\005cobra", "\006smiles", 0, "\005foods", 0, 
+ "\005fatal", "\012statements", "\005batch\003fro\010mutually", 0, 
+ "\006liking", "\014technologies", "\005fools", "\005allow\004okay", 0, 
+ "\011ingenious", "\004tags", "\011privilege", 0, "\014municipality", 0, 
+ "\007created", "\007country", "\007control\012purchasing\007pintail", 
+ "\011stability", 0, 0, "\014additionally\011stimulate", "\006season", 
+ "\012encryption\007arrives", 0, "\005award", "\004wife\010manually", 0, 
+ "\013denigration\010embraced", 0, "\005scrub", "\006crimes", 
+ "\011yesterday\003cob", "\012admiration", 
+ "\004sure\012laboratory\012storefront\011testimony\013dignitaries", 
+ "\004surf\012ministries", 0, "\006others\010stalking\010downtown", 
+ "\003may\010measures", "\010magazine\007audible\015undeliverable", 
+ "\010checkout", 0, "\007hassles", "\011selection\005lynch", 0, "\007happily", 
+ "\012identifies", 0, "\005spent", "\004zero", "\006credit\011preparing", 0, 
+ "\004half\013frightening\010insuring", 0, "\010accurate\005usual", 0, 
+ "\005swipe", 0, "\006beside", 0, "\005staff", "\007islands\010vouchers", 0, 
+ "\012represents", 0, "\005carat", 0, "\005thing\006series", 
+ "\007present\007economy", 0, 0, "\007prevent", "\011furniture\007betting", 
+ "\010theories", "\012literature\003yep", "\007awarded\003ism", 0, "\005tools", 
+ 0, 0, "\006serves\010dumpster", "\015organizations\003roe", 
+ "\011forgeries\014restrictions", 0, 0, "\004much", 
+ "\007dreamed\005feast\012forfeiture", 0, "\003loy\006sloppy", 
+ "\013plagiarizer", "\004tech\012registrars", "\006pagers", 0, 0, 
+ "\010argument", 0, 0, 0, "\006prompt", 0, "\004gold\010emphasis", 0, 
+ "\007freeing", "\007actions\007packets", "\003jot", 0, "\006copied", 
+ "\006labels", "\004news", "\004newt", 0, "\004from\007combing", 0, 0, 0, 0, 
+ "\006immune", "\007variety", "\003ill\006piling", 0, "\013examination", 0, 
+ "\003sea\005kills", "\004sync", "\007someday", "\005owing", 
+ "\010cloaking\005intnl\007ceramic", 0, "\013projections", "\005chief", 
+ "\011subscribe", 0, 0, 0, "\013conferences", "\011appearing", "\011intuitive", 
+ 0, "\011personals\013demographic", 0, "\011procedure", "\007various", 
+ "\005large\013commissions", "\005start\005mommy", 0, 0, 0, 
+ "\005fiber\010meetings", 0, "\007regards\011excitedly\010advocacy", 
+ "\006bullet", 0, "\003doe", "\007measure", 0, 
+ "\005party\007younger\005tuned\010occurred", 0, "\010odorless", 0, 
+ "\012newspapers\011defending", "\007bitches", "\014organization\005stick", 
+ "\007routing", "\007timothy", "\010throwing\007buttons", 0, "\006posted", 0, 
+ 0, 0, "\003arc\011directive", "\013participant\012bestiality", 
+ "\006answer\005stink", "\005cocks\013renaissance", 0, 0, 0, 0, "\006device", 
+ "\011tolerance", 0, 0, "\013subscribing\007pensive", 0, "\012impressive", 
+ "\007enemies", "\005block", 0, "\011amendment", "\004char\012masturbate", 
+ "\013subscribers\003jar\012horizontal\005assay", "\004chat\006outlaw", 
+ "\007airmail\006county\013exploration", "\005silly", 
+ "\005packs\007pounded\012soliciting", 0, "\004orca", 
+ "\010favorite\006exists\010pitfalls", "\012delivering\012obfuscated", 
+ "\011resistant", "\007magenta", "\004line\014examinations\010prolongs", 
+ "\013instruments", "\011constrain", 0, 
+ "\015jurisdictions\010cartoons\005raped", "\011therefore\003ham", 0, 0, 
+ "\005rapid", 0, 0, 0, "\010harmless", "\013departments", "\014demographics", 
+ "\007centers\010founders", "\011freshness\007cycling\013dynamically", 
+ "\004thin\006insane", "\007greatly", 
+ "\006pencil\010fabulous\004hood\010installs", "\012operations\003oui", 0, 
+ "\004this\006weeded", "\006taking", "\005river", "\012assignment", 
+ "\004hook\015demonstrating\012surcharges", 0, 
+ "\007hackers\007blowout\010prohibit\013compatibles", "\004seal", 0, 0, 
+ "\012accountant\004hide", "\013undisclosed\010accounts\007workman", 
+ "\006degree", "\013significant\004hoot\004sear", 
+ "\012percentage\004seas\011keyboards\011successes\003mud", 
+ "\004seat\006unsure", 0, 0, "\006beyond\005rouge", 0, 0, 0, 
+ "\010depicted\010district\007whereas", 0, "\013motherboard", 
+ "\005count\005sound\012brainstorm", "\010majority\012inaccurate", 
+ "\006dollar", 0, "\005court", "\010familiar", "\005route", 0, 
+ "\010strategy\007meeting", 0, "\013positioning\006shaken", 
+ "\011component\015dysfunctional", 0, "\004kept\004yowl", 0, 0, 0, "\005piece", 
+ "\005shell", "\004does\015psychological\012structured\010academia", 
+ "\004race\011hijacking\007trigger", "\004word\010unwanted\010academic", 0, 
+ "\007reports\011turquoise", "\006figure\006portal\007alright", 
+ "\012quantities", "\013scientology\004rack\004arch", 0, 
+ "\004work\012cigarettes\007linking", 0, "\004worm\012recommends", 
+ "\011aerospace\004worn", 0, "\006dosage", "\010verified", 
+ "\014experiencing\005maple\007refunds", 0, "\007journey", "\014participants", 
+ 0, "\006attack\006hybrid", "\005tried\004laws", 0, 0, 0, 
+ "\012downloaded\004scum", "\007removal", "\010verifies\011reviewing", 
+ "\006optimo", "\007founder", 0, 0, "\015substantively", 0, 
+ "\011technical\007sweeten", "\011underline\006tongue", 0, "\013distinctive", 
+ "\011expertise\007indexed\010plumbing", "\005phone\010happened\010firewall", 
+ 0, "\016responsibility\006gladly", "\010sentence\004zest", 0, 0, 
+ "\010allowing\003fed", "\011messenger", "\006timely", "\012inquisitor", 
+ "\007account\007engines\003vie\005mould", 0, 0, 0, "\007nowhere", 
+ "\006errors", "\011attorneys\010insights", "\007poverty\007novelty", 
+ "\006steady\006makers", 0, 0, 0, "\007install", "\010selected", "\007highway", 
+ "\010realized\011amazingly", "\010instance\013interrupted", 0, 0, 0, 
+ "\012acceptance\004evil\010settling", "\005theme", 0, "\005nasty\003nit", 0, 
+ "\005spice", "\005there\006issued", "\005these\004kill\006adhere", 0, 
+ "\012supporting", 0, "\013astrologers\010baseball", 
+ "\007lottery\011discussed\006clever", "\006mother\012reproduced", 
+ "\005viral\011regulated", 0, 0, 0, "\014particularly\006decent", 0, 
+ "\012preference", "\012plagiarism", "\005spite", 0, 0, 
+ "\005rules\012imaginable", 0, 0, "\004next\011advertise\011nutrients", 
+ "\011expensive\005urban", 0, 0, 0, "\005prior\006cooler", 0, 0, 
+ "\012depression\005roads", "\005orbit\017supplementation", 
+ "\005limit\012outrageous", "\007stopped\006square\003hop\006onload", 0, 0, 
+ "\006hidden\005shoot\012contribute", "\004less", "\007designs", "\005short", 
+ "\003aye", "\010consists", "\010excluded\005thous\005shout\004coca", 
+ "\006engage", "\007artwork", "\004safe\011concluded\003hie", "\007weapons", 
+ "\007implore\004flex\011provision", "\005money\006camera\003van\007lessons", 
+ "\004cunt\006stereo", 0, 0, "\004cock\004maze\013legitimizes", 0, 0, 0, 
+ "\004coco", 0, "\005sheep", "\007insight", "\007changes", "\011destroyed", 0, 
+ 0, "\007charges\012regulatory", "\010excludes", "\006ranked", "\006hotels", 
+ "\015prescriptions\004heir", 0, "\006anther\013commercials\010slashing", 0, 
+ "\006extent", 0, "\006stolen", 0, "\012inspection\011favorites", 0, 0, 0, 
+ "\003rad\005codes", "\010exterior\003boa", 0, "\007discard\005hired", 
+ "\006porter", "\012determined\006shower", 0, "\003lax", 0, 
+ "\007realize\006paired", "\006exotic", "\004abut", "\012cheerfully", 
+ "\004view", "\007workers", "\011gathering", 0, "\007kiddies", 
+ "\012obligation\011producing", "\005smart\005plaza", 0, 0, "\007visited", 
+ "\012understand", 0, "\006checks\005labor\006prefix\010abetting", 
+ "\010ordinary\004pull", "\007bookman\011obviously", 
+ "\012overloaded\011hospitals", 0, "\007through\013conflicting", "\004well", 0, 
+ "\010relevant\010insecure", 0, "\006retail", 0, 
+ "\011applicant\012martingale\013perspective", 
+ "\005excel\010cosmetic\007divided\003quo", "\005toner", "\007typical", 0, 0, 
+ "\004lion\011leisurely", 0, 0, "\007studios\012geographic", 0, "\005knife", 
+ "\010drowning\012worthiness", "\005furry", "\017confrontational", 
+ "\006policy", "\006either\010wordless", "\004hope\012unworkable", 
+ "\007golfers\007festive", "\005often", 0, 0, 
+ "\006border\012completely\005logos\014uncontrolled", 
+ "\006direct\010showroom\013businessman", "\005retry\005dummy", 
+ "\007virtual\007exclude", 0, "\005point\007piquant\006slaves", 0, 0, 
+ "\010actually\010expenses", "\004hops\010gigantic", "\005organ", 
+ "\011subsystem", "\003dad\012attractant", 0, "\006horror", "\005dryer", 
+ "\006chrome", "\012completion\007consist\007threads", "\005flame", 
+ "\005plans\005gland\005llano", "\007sabbath", 0, "\011harassing", 
+ "\007persons\011snowflake", "\011according\010switches", "\011mentioned", 
+ "\006result", 0, "\012automotive", 0, "\005plays", 0, 
+ "\007entered\011recruited", "\013celebrities\010cannabis\004tail\010switched", 
+ 0, "\006cousin", "\007classes\010peaceful", "\004bulk", "\004bull", 
+ "\003let\006pocket\007fifties\015enlightenment", 0, 0, 
+ "\006repost\010beauties", 0, "\015authoritative", "\010discount", 
+ "\006postal", "\006detail\007anxious", 0, "\013interesting", "\011impotency", 
+ "\006manner\005savvy", "\005exams", 0, "\006lethal", 0, "\005front", 
+ "\005knick", "\004boat\005burns", "\003zip\011cosmetics\011conducted", 0, 
+ "\003sag\007taxable", "\005wrote\011directory\005hurry\006forces\003cod", 
+ "\006modern\013ingredients", 0, "\007submits\003wry", "\005giant", 
+ "\012nationally\010launched\006unlock", "\010chargers", "\007applied", 
+ "\005agree\007blondes\006fluids", "\007magnets", "\010notebook\011scientist", 
+ "\010dilating", "\006grease", "\005cream", 0, 
+ "\005bogus\007kitchen\012objectives", "\012reparation", 0, 
+ "\004hand\007outlive\006somers", "\011sightings", "\005wages", 
+ "\004hang\011interrupt", "\005moved\011specially\010stranger", 0, 
+ "\010launches\011proposals", 
+ "\003fee\007expense\011substance\014perspectives", "\011fantasies", 
+ "\010activate\013agriculture", "\011enhancing\011penetrate", 0, 
+ "\012fraudulent\011specialty", "\005fresh", 
+ "\010stronger\012plagiarist\012parliament\015specification", 
+ "\012comprehend\004quot", "\014astrological", "\006locate\012administer", 
+ "\006geared", "\005dates", "\010evaluate", "\006rested\006libido", 0, 0, 
+ "\012promotions\004fail\011operating", 0, 
+ "\007similar\011pheromone\006finish", "\007voyager", "\011sincerely", 
+ "\003ani", "\004fair", 0, "\012expiration\005spook\013discovering", 
+ "\005pixel", 0, "\010advisory\006palmer", "\003gal", "\007running", 
+ "\010hundreds\007mammoth", "\010overview", 0, 0, "\010upstream", 
+ "\005blind\005cline", 0, "\007limited\012encounters\010advisors\010tantalum", 
+ "\004teen", "\004dads", "\005troop\013sweethearts", "\003ore", "\003lip", 
+ "\012dictionary\004gone\005volts", 0, "\004guys\006madams\004gong", 
+ "\007leading\006expert\005bursa\006dialog", "\012assistance\010planners", 0, 
+ "\010cheapest", "\007couples", 0, "\003ams", 
+ "\011astrology\012staggering\006copier\006taboos", 0, "\005pinch", 
+ "\005kinds\006export\012redeemable", 0, "\005treat\011functions", 
+ "\010profiled\005kings", "\003cab\013broadcaster\006copper", "\007leasing", 
+ "\006oxygen\007dealers\014understaffed", "\014prescription\007beastie", 
+ "\003sec\007leaving\007sectors", 0, "\004lets\010legality", 0, "\007ignored", 
+ "\007dossier", "\007wrongly", "\006absorb\003mew", "\012precaution", 
+ "\005optic", "\007strange", "\004code\011unsettled", "\005goods\011riverside", 
+ "\011positions", "\005leads\010profiles", "\010replaced\013imagination", 
+ "\006notify", "\003biz", "\012pleasantly", "\006motley", "\012recruiters", 0, 
+ "\006anyway\004zips", 0, 0, "\006balder\011capturing", 
+ "\006fought\007synapse\005leaps", "\013manufacture\013orientation", 0, 
+ "\014commonwealth\007charger\011judgement\010avionics", "\003dog", 
+ "\005enjoy\004item\006knacks", "\005awake", 0, 0, "\004feed", 0, "\006lifted", 
+ 0, "\005aware\006rubbed", "\007garbage", 0, "\007oceanic", 
+ "\004told\004feel\007hundred\007kidding", "\007aspects", 
+ "\010bulletin\006primus", "\006brings\012installing", "\011reflected", 
+ "\003are\011intensive", "\006poster\010standing", 
+ "\007servers\006myself\004fees", "\004toll\004feet\005umber", 
+ "\003lay\007advisor\012harvesting", "\013technically", 0, "\007bounces", 
+ "\010purchase\010seminars", 0, 0, "\012unforeseen", "\005final", 
+ "\007highest", "\011delighted", "\004tial", "\012university\007planner", 0, 
+ "\013ejaculation", 0, "\014entrepreneur\003ova\012exacerbate", 
+ "\007freeway\006sticky", 0, 0, "\005thief", 0, 0, "\007federal\004pump", 0, 0, 
+ 0, "\005while\011redirects\004gals\013microscopic", 0, "\005whine", 0, 0, 
+ "\010modified\012incomplete", "\010exciting", "\012advertiser", "\005white", 
+ 0, 0, 0, "\006beacon", "\005adult\005earns", "\004lips", 
+ "\010involved\013electronics\007offense", "\007profile\005beans", 
+ "\011customize", "\011wondering", 0, "\005games", "\005tease", "\005beats", 
+ "\004luck", "\005heavy", "\013intensifies\010dominate", "\010ultimate", 
+ "\007mergers", 0, 0, 0, "\006heaven", "\010involves\004cabs\010interact", 
+ "\010reaching\011reflector", 0, "\004fold\003kin", 
+ "\007replace\004swan\011roommates", 0, "\006driver\003yaw", 
+ "\006august\007newbury", 0, 0, 
+ "\013performance\010intimate\004swat\004folk\011deceptive", "\010cookbook", 
+ "\012industrial\006spleen", "\013directories", 0, "\005downs\007amended", 
+ "\014manufactured\010raindrop", 0, "\006arcade", "\014southwestern", 
+ "\005swift", "\005train", "\003war", "\003goo\007whisper", 
+ "\007seminar\011trademark\007antique", "\005villa", "\011affiliate", 
+ "\005usage", "\013commonality", "\014manufactures\012exclusions", 
+ "\005forms\014manufacturer\010mountain", "\006target\006manual", "\003leu", 
+ "\012compounded", "\004area\004dogs", 0, "\012externally", 0, 
+ "\006delete\010announce\005crazy\006bolero", "\005studs", 0, 0, 0, 
+ "\006divert\003cup\005whips", "\007defamer", 0, 0, "\011confusing", 
+ "\012biological\016representation", "\011apologize", 0, "\007logging", 
+ "\005fleet\010meltdown\005peaks", "\010unlikely", "\010hardened", 
+ "\010convince", "\012registries", 0, "\010monetary", "\003auk", 0, 0, 0, 0, 
+ "\012submitting", "\004oval", 0, "\005gamma", 0, "\007smokers", 
+ "\010itemized", 0, 0, 0, 0, 0, "\007nozzles", "\005sense", "\007procure", 0, 
+ 0, "\011qualifier", "\011concealed\014breathtaking", "\007involve", 
+ "\010hesitant", 0, "\004misc\007refresh", 
+ "\007whining\006opcode\010dilution\005cloak", 0, "\005clock", 0, 
+ "\011highlight\005faced", "\005brain\006hearts", "\010conceive\006parade", 
+ "\006facing\011softwares\005crude", 0, "\014imprisonment", "\003yes", 
+ "\010pendulum", 0, 0, "\005forma", 
+ "\006bottom\005apply\004miss\014accompanying", 
+ "\004kind\012specialize\012victimizes", "\007unusual", 0, 0, "\010cheating", 
+ "\012occupation\005sight", "\003gam\007calcium", "\003ime\004kink", 0, 
+ "\006proves", "\003wen", 0, "\012authorized", 0, 0, 
+ "\005vital\005titan\012recognizes", 0, "\004shim", "\004shin", "\004ward", 
+ "\004good\004ship\006abroad\004ware", "\012reimbursed", "\004read\007peanuts", 
+ "\011stainless", "\004shit", 0, 0, "\010violence\007clipper", "\004warm", 
+ "\010relieves\004warn\010watchdog", "\004real", "\004warp", 0, 0, 
+ "\004wars\004reap", "\007tactics", "\012keystrokes\004rear", 0, 0, 0, 
+ "\004wary\012wristwatch", 0, "\011processed", 0, "\010dropouts\005rocks", 0, 
+ "\011deadlines", "\010reviewed", "\005round", 0, "\007placing\010pavilion", 
+ "\010mediates", "\006region\004acme", "\007hottest", "\006larger", 
+ "\007warmest", 0, 0, "\011partially", 0, 0, "\012prescribed\010slightly", 0, 
+ 0, "\011purchases", "\010printers", 0, "\007itemize\010shortest", 0, 
+ "\010religion", 0, "\006crypto\007careers", "\007playing", 
+ "\010listened\007aureate", 0, 0, "\005tapes", 0, "\006motors", 0, 0, 
+ "\014biographical", "\010piercing", 0, "\012throughout", 0, "\011inventory", 
+ 0, "\010earrings", "\013distributor\006zoning", "\003spa\006backup", 
+ "\006potter", "\007evening\007kicking", 
+ "\004dead\004rush\005snack\013adolescence", "\005dozen", 
+ "\012identified\006yachts", 0, "\011permitted\012creativity", 
+ "\005drink\011medicated", "\006violet\011miserable", "\005telia", "\004deal", 
+ 0, "\007cheaper", 0, "\003odd", "\005below", "\004dear\004game", 
+ "\007relieve", 0, "\010vascular", "\006volume", "\004punk\010overhead", 
+ "\013technicians", "\007patents\012astounding", "\010indicate\010obsolete", 
+ "\010earnings\007compact", "\015appropriately", "\004slew\006mazing", 
+ "\011untrusted", 0, "\004punt", 0, "\014periodically", "\005mouse", 0, 
+ "\004went\011adventure\007catchup\006examen\011violators\003hap", 
+ "\012defamation", "\004poco", "\006served\011graduates", "\007climate", 0, 
+ "\013implemented\006toasty", "\007gnostic", "\012legalities", 0, "\006assist", 
+ "\004hora", 0, 0, 0, 0, "\006lender", 0, "\010billions", 0, "\007printer", 
+ "\016susceptibility", 0, 0, "\011exclusive\006bother\004horn", "\007collier", 
+ "\010security\005rodeo", "\010oriented", "\005plain\011ideograph\010minimums", 
+ 0, "\006action\004high", "\007fulfill\011announces\003mug", 0, 0, "\005grief", 
+ "\007blatant\007bowling", "\005ruler", 0, 0, "\010displays", 0, 
+ "\011governing", "\012deactivate", "\011reference\010imagined", 
+ "\015insignificant", 0, "\004take", 0, "\005hello\005belle\005tells\005waive", 
+ "\003was", "\003add\004dust", "\005drive\011variation", "\006showed", 0, 
+ "\007insults", "\011brightest\010woefully", 
+ "\011implement\011parenting\013enslavement\005chore", "\007trusted\005chose", 
+ 0, "\003lev\006merged\005soaps", "\006upside", 0, 0, 0, 0, 0, 0, "\006frenzy", 
+ "\015substantially\010roulette\016simultaneously", "\011indicates", 
+ "\007crafted", "\007earning\010handling", 0, 0, "\004span", "\007earring", 
+ "\010goodness\014distributors", 0, 0, "\007tracing", "\007trading", 0, 0, 
+ "\005union", "\005butts", 0, "\013translation\010fighting", 0, 
+ "\011healthier", "\004lazy\006seller", 0, "\006button\006hitter", 0, 0, 
+ "\004odds", "\007billion\010passport", "\013connections", "\005avoid", 
+ "\010stopping", 0, 0, 0, 0, "\012innovative\006bright", "\006stupid", 
+ "\006lucent\010deferred", 0, "\007minimum", "\010reserved", "\005class", 0, 
+ "\005paint", 0, "\007balance", "\010partners", "\012dealership", 
+ "\010remember", "\004fake", "\017professionalism\005laden\010wetlands", 
+ "\010included\005saved\003rut\011chocolate\011spreading", "\007enables", 0, 
+ "\006saving\006albeit", "\006causes\010credited", 
+ "\006sheets\003yet\010servants", "\006creamy\015geometrically", 
+ "\012understood\014aggressively", "\010repelled", "\007margins\012goldsmiths", 
+ "\011following\010reserves\012transplant", "\005rural", "\003roi", 
+ "\003oft\012binoculars\011separated", 0, 0, 0, "\011celebrity", 0, 0, 
+ "\012visibility", 0, "\010contacts", "\010includes\006places", "\007imagine", 
+ "\012unlicensed\011checkbook", "\006picked", 0, 
+ "\005proto\003pod\014effortlessly", "\007display", "\010readable", 
+ "\013instruction", "\005month", 0, 
+ "\006verify\004wash\006planes\012efficiency", 0, 0, 
+ "\011broadcast\011originate\006entice", 0, "\007central\011shameless", 
+ "\006plates", 0, "\005doing", "\013consignment", 0, 0, "\004adds\005glade", 
+ "\014professional\006corner", 
+ "\005value\011insurance\011synthetic\010complain\012astonished", 
+ "\005model\003cad\005valve", "\011hydraulic\005dover", 
+ "\011waterside\005dodge", "\010prompted", "\003see\007passion", 0, 
+ "\010monopoly", 0, 0, "\012publishing", "\010fourteen", 
+ "\011exploited\005cabal", "\011published", "\004said\006graphs", "\004acne", 
+ 0, 0, "\010exercise", "\013tournaments", 0, 0, 0, 0, "\004held", 0, 0, 0, 
+ "\010invested", "\007matches", "\010inhibits", "\006report\007emperor", 
+ "\005weeks\004hell", "\010numerous\007matters\003elf", 0, 0, 
+ "\004help\005whack\003aft", "\010untitled\006banker", 0, "\010anything", 
+ "\006banner", "\007reserve", "\007foreign\012originally\010stumbled", 
+ "\006handle\006spider", 0, "\005gifts\010gimmicks", "\012waterproof", 
+ "\006blades", "\007singles\013underground\010infinite\003yip", 
+ "\012meaningful\004tone\013governments", "\003rag", 
+ "\003bod\004tong\011histories", "\007include\010infinity", 
+ "\013predictions\005thumb", "\005adobe\010prisoner", "\010physical", 
+ "\007partner\011mysteries", "\006verity\005versa", 0, "\010equities", 
+ "\004chef", "\013complicated", "\010conflict", "\004tons", 
+ "\007refills\005freak", 0, "\007sleeper", "\004tick\012separation", "\003ode", 
+ "\006commit", 0, "\005pages", "\004debt\004gang", "\005loved", 
+ "\006latest\007schools", "\006paying\006beanie", "\012discussing", 
+ "\011expressed\005movie\010rejected\013parenthesis", "\006itself", 0, 0, 0, 
+ "\011immediate\014photovoltaic", 0, 0, 
+ "\014pornographic\005liked\010marketed", 
+ "\007removed\007iceberg\012whitespace", 0, "\005rated", "\011physician", 0, 
+ "\006rating", 0, 0, 0, "\010marketer", "\012advertised\010necklace\006sought", 
+ 0, "\014instructions\006astute", 0, "\006stream", "\006hunted", 
+ "\012relatively\004cade", "\004orgy", 0, "\004seed", "\007concede", 
+ "\006partly", "\005sizes", 0, 0, "\007aliases", "\004seek", 0, 
+ "\004seem\013accordingly", "\004seen\010achieved\013overwhelmed\007optimum", 
+ 0, "\010negative\012sunglasses\005sixth", 0, "\004host\007cockpit\003kip", 
+ "\004sees\005perky", "\010jeopardy\013surrounding\012transition", 
+ "\006writes\005charm\006vacuum", "\013cancellable", 
+ "\007assumed\007gimmick\006timers", "\005chunk", 0, 
+ "\012registrant\011penalties", 0, 0, 0, 0, 0, "\007meaning\006closed", 
+ "\010informed\006ideals\010dentists", "\004font", 0, "\010aluminum", 
+ "\011pyramidal", "\011gemstones", "\005which\005thick\007coffees", "\004talk", 
+ "\006buyers\004tall\010esquires", 0, "\004duty", "\013determining", 
+ "\005opted", 0, "\010inserted", 0, "\012reassuring", 
+ "\005think\012industries\010discrete\004rage\004doit", 
+ "\013unsolicited\007popular", 0, "\012formulated\013biographies", 
+ "\011greetings", 0, "\006stylus", "\005duals", "\003cur\004clan", 
+ "\004buoy\007fiction", 0, "\006virgin", "\003sys\006barely\005avail\005meals", 
+ "\007follows", "\005means\007silence", "\012commitment", 0, 0, 0, 
+ "\004java\007sharper", "\010midnight\013magnificent", "\004body", 0, 
+ "\006caller\006unpaid\005matte\011retaining", 0, "\011necklaces", 0, 
+ "\012saturation", "\010ignoring", 0, 0, 0, 0, "\004sold\011consulate", 
+ "\004sole", 0, "\005disco\006prints", 0, "\013compensates", "\006wonder", 0, 
+ "\010deceased", "\012exhibition", "\006agents", "\004solo\005risky", 0, 0, 0, 
+ 0, "\014surroundings", "\011allocated", 0, "\006rather", "\003too", 
+ "\013enforcement\007quantum", "\011villagers", "\007achieve\007spooled", 0, 
+ "\005swing", "\006parent", 0, "\010flooring\004falk", 
+ "\004fall\013progressive\007caution", 0, 0, "\011terminate", 0, 
+ "\014persistently", 0, "\012treatments", "\012evaluating", 0, 
+ "\013transaction\006symbol", "\003pup", "\012individual\007swamped", 0, 0, 0, 
+ 0, "\007passing\003aha", "\010amateurs", "\005farms", 
+ "\013alternative\014breakthrough\013forgiveness", "\007employs", 
+ "\016communications", "\010thirteen", "\010vertical\006reload", 
+ "\010reseller\004babe\005parse\010relating", 
+ "\006member\012affordable\007sharing\010portions", 0, 0, 0, 
+ "\006walrus\007dentist\007shaving\007lending", "\011existence\007refused", 
+ "\005super", "\006source\006course", "\006unused", 0, 
+ "\011extensive\006speaks", "\007rentals", 0, 0, "\005quick\010affected", 
+ "\004watt", 0, "\006edited\005gamut", "\007nearest\011presuming", 
+ "\004baby\010monitors\014incorporated", "\003hot\010keywords", "\004cure", 
+ "\015interruptions", "\011purposely", 
+};
+
+
+
+#define word_tbl1_LEN 9001
+static const char *word_tbl1[9001] = {
+ "\013propietario\006suelta", 0, "\011mantenido", "\006limpia", 0, 
+ "\010consulta\011colocando\015supermercados", 0, 0, 
+ "\006a\361adir\010soportar", "\005env\355a\010consulte\016evangelizaci\363n", 
+ "\011populares", 0, "\010consejos", "\007masivos\013financiarse", 
+ "\010conozcas\007cazuela", "\014confidencial", "\010estudios\012brevemente", 
+ "\005medir\011tituladas", "\011prestamos", "\010despegar", 
+ "\012preguntar\341", "\013imaginaci\363n\007alegr\355a\006urbano\006sallan", 
+ 0, "\012sindicatos", "\011contempla", "\012proponerle", "\012espec\355fica", 
+ 0, "\010estadios", "\016perfeccionando", 0, 0, 
+ "\011controles\013conseguiste\011tonifique", 0, 0, 0, "\004voto", 0, 0, 
+ "\010venganza", "\007cabrera", "\006serv\355a", 
+ "\015transferencia\007gaseosa\010tristeza\006iron\355a", "\005cruz\363", 0, 
+ "\007estados", 0, "\010response\005harto", "\012licenciada", 
+ "\013expresiones", 
+ "\007simples\005plena\012principios\012obst\341culos\014recorreremos", 0, 
+ "\010suspenso\007beltr\341n", "\012ampliaci\363n\007mu\361ecos", 
+ "\005tenga\004roja\015implicaciones", "\011autopista", 
+ "\005tenia\012demostrado\012monumentos\014parcialmente\006cesi\363n", 0, 
+ "\005vamos\010pregunt\351", "\017entretenimiento", 0, 
+ "\013adicionales\007imagin\363\006costas", 0, 0, "\012confirmada", 
+ "\006prensa\012presb\355tero", "\010recuerde\011artesanal\007temario", 
+ "\010librer\355a\012biol\363gicos", 0, 0, 
+ "\013comentarios\010recuerda\007postula", 0, 0, 0, "\007monitor\006exacto", 0, 
+ "\010recuerdo\005sacar\007renueve", "\005reina", 
+ "\010espa\361ola\006sufrir\014cordialmente", 0, "\010circunda", 
+ "\014propietarios\005gorda", "\010carpetas\006leerla\010s\355ntesis", 
+ "\007noruega\005nacho", "\013espect\341culo\007casinos\005ceder", 0, 
+ "\006parece", 0, 0, "\011presentes", 
+ "\007soporta\006c\341ncer\010tardar\341s", 0, 
+ "\007t\355tulos\007bolet\355n\013reconocidos", 
+ "\005flora\007aviones\010implique", 0, "\005flota\005corre\006robles", 0, 
+ "\007j\363venes\005corte\007conozca", 0, "\007canilla", "\007infanta", 
+ "\005se\361al", "\003ven\005museo\010padrones", 0, 0, "\010encontr\351", 0, 
+ "\007estadio\010advertir", "\004caer", 0, 0, "\010pensione", 0, 
+ "\010divulgar", "\005llego\006desean", 0, 0, "\005guste", "\007causado", 0, 0, 
+ "\005lleno\010pesquisa", "\010tenencia", "\017enfrentamientos", 0, 
+ "\011solamente", "\005local", "\007estudio\007consejo\005tenis", 0, 
+ "\007tiernas\005llevo", "\007quienes\013conformando", "\006acelga", 0, 0, 
+ "\014veinticuatro\015corporaciones\005viola", "\013importancia\010dediques", 
+ "\010luminoso", 0, "\010conectar\007radical", 0, 0, 0, 0, "\010bandejas", 0, 
+ 0, 0, "\012cancelable\012centenares", 0, "\014autorizaci\363n", 0, 
+ "\007n\355tidos", "\010resuelve\010escalera", 
+ "\006nuboso\005fijos\012detallarte", 0, 0, "\006grande", 
+ "\006normal\011mediaci\363n", "\006coloca\006medido", 
+ "\013felicidades\011evoluci\363n", "\007delgado\004pace", "\007felon\355a", 0, 
+ "\016personalizados\014instant\341neos", "\007mejoran", 
+ "\011adecuadas\011anticipar\005maure", 0, 0, 
+ "\003aun\005viene\014conservaci\363n", "\006parque\010formulas", 
+ "\010concepto", 0, 0, "\010constata", "\007consumo", "\007espa\361ol", 0, 
+ "\010demostr\363", 0, 0, "\010descenso", 0, "\012detuvieron", 0, 
+ "\011solicitud", 0, "\015subsecretaria", 0, 
+ "\005ten\355a\012mantenerla\006macias\011vagabundo", 
+ "\012cuadr\355cula\010recambio", "\013determinado", "\007carpeta", 
+ "\012mensajer\355a\007confiar\012medianoche", 0, "\015ayuntamientos", 
+ "\014espect\341culos\012comprendas", "\011conectado\007zapatos", 
+ "\010estomago", "\005radio", "\012disponemos", "\006apache\005bah\355a", 0, 0, 
+ "\020estacionamientos\012distorsi\363n", "\014respondiendo\007humedad", 
+ "\011temporada\012precisando", 0, "\007ponerle\011educativa", 0, 
+ "\010contraer", 0, 0, 0, 0, 0, "\012documentos", "\007conecta", 
+ "\005fases\010perdidas\012recibirlos", "\010tribunal", 
+ "\012tendencias\011aportando", 
+ "\007bandeja\005ped\355a\005beber\011apostando\006cambas", 0, 
+ "\006cuales\007digerir\014antropolog\355a", 0, "\011adquieren", 
+ "\012pudi\351ramos\013encontrarme", "\014importadores", "\012enviaremos", 
+ "\014ciclomotores", 0, "\006le\355ste", 
+ "\010buscando\006opci\363n\010dominios", "\005paste", "\012mensajeros", 
+ "\006alerta", "\006anexos", "\006devoto\013fotogr\341fico\011retirarse", 
+ "\016recomendaremos", "\010ignorado\007sentado\016representaci\363n", 
+ "\007c\351lulas", "\006activo\011sencillas", "\006elevar\011configure", 0, 0, 
+ "\010ladrones\012palestinos", "\005dieta", 0, 0, "\006minuto\007formula", 0, 
+ "\012eliminarse\011conceptos", 0, 0, 0, "\006helado\010facturas", 
+ "\006orden\363", "\010comisi\363n\011prohibida", 
+ "\011sustituto\013subdivisi\363n", "\006ligera\006romana", "\006nombra", 
+ "\006acceda", "\005caf\351s\007apagado", 0, "\010hospital\011brindamos", 
+ "\006pronto\005firm\363", "\011dedicamos", 0, "\003vas", 0, 0, "\006anotes", 
+ 0, "\007quieres", "\012ense\361anzas", "\010t\351cnicos\013modificarlo", 
+ "\013instructivo", "\006torneo\010respirar", "\007ofreci\363", 
+ "\010gustar\355a\015complic\341ndose", 
+ "\006quemen\017norteamericanos\013proveedores\011dominante", 
+ "\016visualizadores", "\012particular", 0, 
+ "\003tan\006forman\007inicios\010constat\363", 
+ "\016organizaciones\005tasas\017estacionamiento\010sat\351lite\011caminando", 
+ 0, "\006surgi\363", "\014desgraciados", 0, "\012fundamenta\011teol\363gico", 
+ "\005unido\011excepci\363n", 0, 0, "\006buscar", "\007perdida", 
+ "\006visual\013registrados", 0, "\006cierre", "\015conocimientos\010albahaca", 
+ "\011cartuchos\014determinados", "\011espa\361olas\010levadura", 0, 
+ "\010extra\355da\007tribuna", "\011inquietud", 
+ "\010duraci\363n\011espa\361oles", "\012alquileres\011silencios", 0, 
+ "\004har\341", 0, "\003gel\010mediod\355a", 
+ "\007dominio\005pagan\006vacuno\015subsecretar\355a\014descubriendo", 0, 
+ "\010extra\355do", "\013tradicional", 0, 
+ "\010proyecto\014cristianismo\010te\363ricos\011confirman", 
+ "\011direcci\363n\010ansiedad\007quincho\011necesitar", "\013corporativa", 
+ "\012adhesiones", "\007adherir", 0, "\007pedidos\007tiempos\012categor\355as", 
+ 0, "\013disfrutando\007decorar\010cardinal", "\005magos\007lograr\341", 
+ "\011responded\011oposici\363n\005danza\005flujo", 0, "\005baila", 0, 0, 0, 0, 
+ "\007remueva", "\004lite\006anotar", "\010potencia\010adaptada", 0, 
+ "\010impostor\010milanesa", 0, "\003has\005estas\006moreno", 0, 0, 0, 0, 
+ "\014conectividad\010rodeados", "\006d\351cada", 0, 
+ "\014tranquilidad\010entierro", "\012aproximado", 0, "\017comercializamos", 0, 
+ 0, "\005estos\010cr\355ticas\013interacci\363n\006decid\355", 
+ "\007holanda\007caracas\005coste", 
+ "\010docentes\015gratuitamente\010gravedad", "\007t\351cnico\005pollo", 
+ "\011beneficio", "\012bloqueados", 
+ "\003veo\006s\341bado\011virtuales\010pol\351mica", "\007hacemos", 0, 
+ "\004hija", "\011enviarlos", "\006peleas\007factura", "\006piense", 
+ "\005polvo\010nutridos\006docena", "\012periodista", "\014encontrarlos", 
+ "\005orlan", "\006nacido\005piano\015fortaleciendo", 0, 
+ "\005salga\011culminar\341", "\010cereales", 0, "\006chofer", 
+ "\010variable\013voluntarios\004hijo\006normas", "\007sincero", 
+ "\012cabalgatas", 0, 0, 0, "\006jersey", "\006bloque\011pretenden", 
+ "\005salsa", "\005salta\006pileta\013resistentes\012comensales", 
+ "\012respondido", "\015inmobiliarias\013habilidades", 
+ "\013municipales\010fenicios\010detector\013amplificada\006asador", 
+ "\010obsequio\015inmobiliarios", 0, "\011parientes\014instructivos", 
+ "\011suspender\010abiertas\007extraer", "\006negras", "\003ave", 
+ "\011mariposas", "\007canchas\012caracteres", 0, "\003ley\011geograf\355a", 0, 
+ 0, "\013calculadora\007navarro", 0, 0, 
+ "\006gratis\007apuntan\006asumir\012cautiverio", 
+ "\006alegre\011tentaci\363n\006miedos", "\011preparado\005artes\011ordinario", 
+ "\010oficinas\006grupos\011did\341ctico", 0, "\003uso\013prometieron", 
+ "\004rain\012iniciaci\363n", 0, "\010elemento", "\006vuelto", 
+ "\005alias\007navegan", 0, "\010observar\012preescolar", "\006ribera", 
+ "\013resistencia", "\004suya", 0, "\010cumplido\010reunimos", 
+ "\006cubrir\010analizar", "\007reitera\007eritrea", 0, 0, 
+ "\007ponerse\011informar\341", "\015esparcimiento", "\010segmento", 
+ "\007te\363rico", "\005color\013interesante\014deshabilitar", 0, 0, 
+ "\005serie\004suyo\014corporativas\005ayude\013corresponda", 
+ "\007docente\006salome", "\011italianos\012protegidos", "\013parasitaria", 
+ "\006belgas", "\010convenir\011distingue", "\007mirador", "\011imponente", 
+ "\006hombre\005gemas", "\013emprendedor\006bellas", 0, "\007procura", 
+ "\007animada\007escobar", "\005serve\012pronunciar", "\011recetario", 
+ "\006clases", "\007reenv\355a", "\013capricornio", 
+ "\006claves\005valor\015arqueol\363gicos\017vitivinicultura", 0, "\005pagar", 
+ "\011contactar\011permitido", 0, "\011productos\011iniciamos", "\006timbre", 
+ 0, "\010convenio", 0, "\006poseen", "\006frente\007cr\355tica", 0, 
+ "\006negros\007humanas", 0, 0, "\005est\341s", "\011celulares", 0, "\003rol", 
+ "\007abierta\007pasando\014producciones\006vieron", 
+ "\010pensamos\010igualdad\007so\361ados", "\005sabes\012inagotable", 
+ "\004plan\011consumido", "\013fundaciones\005est\351s", "\010sistemas", 
+ "\011preparada\011mecanismo", "\007encarar", "\012encontrada\006venci\363", 0, 
+ 0, 0, 0, "\004caf\351", 0, 0, "\015constituyendo", "\012establecer", 
+ "\010poniendo\006rallar", "\007oficina", 0, 0, 0, "\006dictar\012proveedora", 
+ 0, 0, "\014amplificadas", "\010acuerden", 0, "\007analiza", "\006abuelo", 
+ "\007ordenes\005bajas\006firmas", 0, 0, "\005bolsa", 
+ "\016construcciones\006granel", 0, 0, 0, "\011caballito\014calculadoras", 0, 
+ "\010coincide\010veintena", "\010colisi\363n", 0, 
+ "\007entrar\351\011vinculado", "\005bajos", "\004sala", 0, "\012municipios", 
+ "\006puedes", "\011actualiza", "\007irlanda", 0, 
+ "\004usos\007aclarar\014rompecabezas", "\006aceite\006sopera", 
+ "\010sexuales\005temor\006ag\374ero", "\007decreto", "\011separando", 0, 0, 0, 
+ 0, "\010soluci\363n\013universales", "\010v\355nculos\016estadounidense", 
+ "\006riesgo\006bast\363n", 0, 0, "\006m\351dica", "\011convenios", 
+ "\011propuesta\012descriptos\011peri\363dico", "\010asientos", 
+ "\007muebles\006dieron", 0, "\012intervenir", "\011ordenador\014interesantes", 
+ "\011removidos", "\011antolog\355a\010gelatina\007tapizar", 0, 
+ "\006cu\341nto", "\006sierra", "\010sinergia", 0, 
+ "\007impresa\014confeccionar\012infecciosa", 
+ "\005suave\010invierte\007agregan", 0, 0, 0, 0, "\012transporte\004u\361as", 
+ 0, 0, 0, 0, "\010frutilla", "\010regiones\013verificadas", 0, "\007ninguno", 
+ 0, 0, 0, "\015descripciones\004pura", "\013inquietudes", 0, "\005mixta", 0, 
+ "\010pr\363ximas\015desesperaci\363n\007salvaje", "\011invitamos\004son\363", 
+ "\007vicario\007visiten", "\007tomamos", 0, 
+ "\007parcial\004rolo\015confirmaremos", 0, 0, "\007almagro", 
+ "\007sistema\004puro\013b\341sicamente", "\013significar\341", 0, 0, 0, 
+ "\004ba\361o", "\011generadas", 0, "\011comprador\012venezolana", 0, 
+ "\006pasi\363n", 0, "\011mon\363logos", 0, "\012bienvenido", "\005sacro", 
+ "\005lomas\006siento\004numo", 0, "\010agr\363nomo\006verdes", 0, 0, 
+ "\006huesos\006t\355pico", "\007espanto", 0, "\006huevos", "\005chips", 
+ "\012descuentos", "\012televisi\363n\011humanidad\007capilla", 0, 0, 
+ "\012minoristas\006manila\012continuar\341", 0, "\007v\355nculo\010tratados", 
+ "\005\351xito", "\007s\363lidos", "\003as\355\007asiento", "\007sientan", 
+ "\016intervenciones", 0, "\012aeropuerto", "\012espec\355fico", 0, 0, 
+ "\012utilizamos", 0, 0, "\013aprendizaje\011previsi\363n", "\013artesanales", 
+ "\006agotar", "\007tensi\363n", "\011arzobispo\012utilizados", 
+ "\013enganchados", "\006c\363ctel", 0, 0, 0, "\007avanzar", 
+ "\011precisi\363n", 0, 0, 0, 0, 0, "\005enero", 0, 0, 0, 0, 
+ "\005dudas\006picado", "\012normativas\010mercadeo", "\011suscribir", 
+ "\005files", "\005dudes\006ortega", 0, "\007trueque", 0, 
+ "\004dial\007pagados", "\005asado", "\013adquiriendo\007reducen", 0, 
+ "\010libertad\012fraccionan\010cordones", "\010sellados", 0, 
+ "\015desobediencia\005chica\007refiero", "\013trasladarse\007tercero", 0, 
+ "\007abrirse\011demasiado\012producidos", "\007teatral", "\011dise\361ando", 
+ "\011pendiente", "\006lector\006cifras", "\005comer\010pudiendo", 
+ "\007retomar", 0, "\007semanal\005china", 0, "\014exportadores", 
+ "\005etapa\012desarroll\363", "\010nosotros\007turismo", 
+ "\011cordiales\012unilateral", "\007pr\363xima", "\010redentor", 0, 0, 0, 
+ "\005carne", "\007obtener\006sabido", 0, "\012cient\355fica\007figuran", 
+ "\006cursos", 0, 0, "\012divertidos", "\007futuros", 0, 0, "\007cambios", 
+ "\015intelectuales\005garza\006cursar", "\007cordial", "\010naciente", 
+ "\012administre", "\007fomento\012consultora\012procesadas", "\010juventud", 
+ 0, "\016administrativo\007proceda", 0, "\010criatura\012reposter\355a", 
+ "\011industria", "\004sido", "\011encontrar", 
+ "\015completamente\012biblioteca", "\007herrera", 0, "\014intensidades", 
+ "\006revive", "\010reunidos", "\006tardar", 
+ "\013observaci\363n\013abandonados", "\010esfuerzo", "\003ano\004s\363lo", 
+ "\005sacas", "\007tratado", 0, 0, 0, "\006colina", "\017fraccionamiento", 0, 
+ "\011fidelidad", "\012comprimido", "\006vereda", 0, 0, 0, 
+ "\010aceptado\006deriva", 0, "\010millones\013comprobable", 
+ "\004gota\013productivos", 0, "\005bomba\007sublime", "\012integrales", 0, 
+ "\005rusas\007atender", "\012ejercicios\012aficionado", "\010revisi\363n", 
+ "\006sobres\013autom\363viles", "\006diaria\007amplios", "\005digna", 0, 
+ "\014imaginativas", "\005bytes\010ballenas\012publicando", 0, 
+ "\016principalmente", "\007mineral\012averiguado", 0, 0, 
+ "\006fuente\014remuneraci\363n", 0, 0, 0, "\006fuerte", 0, 0, "\011complejos", 
+ 0, 0, "\005gesto", "\005tener\005libro\011ensaladas", 
+ "\005jugar\011escuchado", "\014acompa\361arnos\011compartan", "\010centenar", 
+ 0, 0, "\010escriben", 0, "\006juicio\010atender\341", "\017certificaciones", 
+ "\006quiero\005tenor", 0, 0, "\007propaga", 0, "\012mantenerlo", 0, 
+ "\010casillas\014comprometida\011moderador", "\016convencionales", 
+ "\014generalmente\006collar\005avi\363n", "\010cargados", 
+ "\014demostraci\363n\010cargador", "\013procreaci\363n", 
+ "\005pens\363\013visualizado", 0, "\007activar", 0, "\006actuar", 
+ "\011destaquen\010firmando", "\003bus", "\010hablarlo", "\007revelar", 0, 
+ "\006armada\014calificaci\363n\015posibilidades\005har\341n", "\006drogas", 0, 
+ "\005medio\005pedir\011israel\355es", 0, "\005fauna", "\010tropical", 
+ "\006fraude\015incondicional", "\006adonde", "\013necesidades\010reciente", 0, 
+ 0, 0, "\011adicional\010copiarse", 0, 0, "\007aprenda", 
+ "\003gen\014comprobables\010antemano", "\010llamarte", 
+ "\013encontrarse\016independencias", "\010virtudes\010tostadas", 
+ "\010contiene\013desarrollos\011reuniones", 0, "\010conviene", 0, 
+ "\012apost\363lica", "\004puse", "\006puente\007ballena\007marcada", 
+ "\006escena", "\006trofeo\010cobraran", "\006\372ltima", 0, 0, 
+ "\014radicalmente", "\004anos\005jam\363n", "\011tinieblas\010vaciados", 
+ "\004puso", 0, 0, 0, 0, "\010modifica\012proximidad", "\005culto", 
+ "\006oficio", 0, "\011extractor", "\006env\355an", "\010diciendo", 
+ "\006barbas", 0, "\010imprenta", "\014religiosidad", "\006romano", 
+ "\016aut\351nticamente\006largos", 0, "\010v\355rgenes", "\011registrar", 0, 
+ "\005pongo", "\011traductor\007escribe", "\007reinado", "\013brindaremos", 0, 
+ "\007casilla\011compuesta", "\006barras\012recordamos", "\007anillos", 
+ "\011cambiante", 0, "\010hablamos", "\004hila", "\007cargado", 
+ "\011distintas", 0, 0, 0, 0, 0, "\011distintos\004seis\011manifest\363", 
+ "\007pre\361ada\011ministros", 0, 0, "\007pensado\012exteriores", 
+ "\011maximizar", "\010informal", 0, "\011repuestos", 0, 
+ "\004foro\010alimento", "\004tapa\013ocasionadas\016estructuraci\363n", 
+ "\011impresi\363n\013registradas", "\011optimista", "\007higiene", 
+ "\007equipos\005santa\004tape", 0, 0, 0, 0, 0, "\010machines\010lamentar", 
+ "\005velas\006gracia", "\006latino\014visualizador\010quedaron", 0, 
+ "\014coordinarlas", 0, "\006carb\363n\012publicarla", 0, 0, 
+ "\012combinados\016manipulaciones", 0, "\010informar\005rosca", 
+ "\011violencia", 0, 0, "\006afirma", 0, 
+ "\010texturas\014concurrencia\007aportar", 
+ "\010manuales\010cortinas\007apostar", 0, 0, 0, "\011implantes", "\006alumno", 
+ 0, "\010gabinete", 0, 0, "\006deseas", "\003san", 0, 
+ "\005ponga\014conversaci\363n", 0, "\010edificio", "\006corren", 
+ "\007adhiero", "\006sacado", "\012geogr\341fica\010edilicia", 
+ "\004odio\012reproducir", 
+ "\006olvide\005bonos\006tiende\011incentivo\010agravios", 
+ "\006guiara\010ambici\363n\004sopa", "\007blancas", 0, 
+ "\020estrat\351gicamente", "\006estado\007pudiste\006march\351", 0, 
+ "\010borrarte\007vajilla", "\012formulario\015publicitarios", 0, 
+ "\010desfilan\010comiendo\015publicitarias", 
+ "\016necesariamente\006comit\351", "\005islam", 0, 
+ "\012navegables\011escultura", 
+ "\011alcanzar\341\017electromec\341nico\013distintivas", 0, "\013coordinando", 
+ 0, 0, 0, "\014conquistando\005giran", "\007regalen", "\011necesitas", 
+ "\010comillas\013ejercitador\010vuestros", "\010contenga", "\006tragos", 
+ "\014conclusiones", "\006paseos", "\005caiga", "\010detenido", 
+ "\006b\341sico\006ataque", "\013disponibles", "\011calificar", 
+ "\011ecum\351nico", "\005ideas", 0, "\013triplicados\006ocupar", 
+ "\011religiosa", "\005radar", "\006guisos\006abusos", 0, "\004tela", 0, 
+ "\006camino\005fuego\014alimentarias\006inici\363", "\003gas", 0, 0, 
+ "\012seminarios\006cuello", 0, "\005himno", 0, "\006t\355tulo", 
+ "\013alternativa", "\007informa\010actuando", "\005polen", 0, 
+ "\013capacitarse", "\010hab\355amos\011boleter\355a", 0, 0, 
+ "\005rutas\006romero\013diplom\341tico", "\011despedida", 
+ "\011autonom\355a\010viviendo", 0, "\007conduce\010vivienda\012desniveles", 
+ "\010veh\355culo", 0, "\006racial", 0, "\003ola", 
+ "\013ensamblados\010correcta", 
+ "\012asociaci\363n\016inconvenientes\012manifiesto", 
+ "\016universitarios\011sevillana\013abrochadora", "\012cerveceras", 
+ "\010flexible\015compartimento", "\007enviar\351", 0, "\012subtitular", 
+ "\010cantidad\006thread\012emergencia", 0, "\010correcto\011provocado", 0, 
+ "\005picos", "\010teclados\005palma", "\005humor", "\005calor", "\007textura", 
+ "\007simulan\013canciller\355a", 0, "\006extras\010soportes", 0, 
+ "\006\341giles", 0, "\011procesar\341", "\013distinguido", 0, 0, 
+ "\007visitas", "\006idioma\010progreso", "\011estrellas", 
+ "\012reveladora\015domesticaci\363n\004sano", "\007estar\341n\012casualidad", 
+ 0, 0, 0, 0, "\013integrantes", "\005secan", "\015instrucciones", 
+ "\013diversiones", "\007activas", "\010ciencias", 
+ "\010apellido\011noticiero\010eficacia", "\006pastas", 0, 
+ "\011avanzados\006brazos", "\006frutas", 0, 0, 0, 0, 0, 0, 
+ "\007dolares\012microondas", "\010decirles", 0, "\006nietos", 
+ "\007asegur\363", 0, 0, "\010entradas", "\010ayudarte", 0, 
+ "\014alternativas\011madrugada", "\015oportunidades", 0, 0, "\010c\341lculos", 
+ "\007vuestro", 0, "\003ala\004aqu\355", "\010recaudos", 0, "\006provee", 0, 
+ "\006bailar\006orilla", "\007vidrios", "\010llevarse", "\004rica", 
+ "\013inspiraci\363n\013terroristas\010indicara", 0, "\013temporarios", 
+ "\012feminismos", "\011rep\372blica\007monedas", 
+ "\006ordena\011laminadas\011invitarlo", "\010renovada", 0, 0, "\007renueva", 
+ "\005sali\363\011misoginia", "\007teclado\011correctos", "\005balta", 
+ "\004rico", "\010colonias", "\012redactando", "\006busque\012hipot\351tico", 
+ 0, 0, 0, "\012imprimirlo", "\005cobro", 0, 
+ "\007soporte\013tecnolog\355as\011decorador", "\013casta\361uelas", 
+ "\011finalista", 0, "\007despu\351s", "\003pro", "\007entorno", 
+ "\010hacerlos\007recibos", 0, "\012porcentaje", 
+ "\012ciudadanos\015caracterizado", 0, 
+ "\003ver\007talento\007volante\013capacitados", "\011secuestro", 
+ "\004lujo\006saldar", "\006salmos", 0, "\014solicitarlos", 0, 
+ "\013consultados", "\007ciencia\010entregar", "\010entregas\012comenzaban", 0, 
+ 0, "\016vulnerabilidad", 0, "\011remitirle\013vegetariano", 0, 0, 
+ "\006tratan", "\010sabrosas\006saltar", "\007decirle", 
+ "\015adecuadamente\007absurdo\006salvar", "\011centrales\012difundidas", 0, 
+ "\005cajas\005bajar\010divisi\363n\014distinguidos\017espec\355ficamente\005golpe\010tenerlos", 
+ "\005ambas", "\005feria\007hubiera", 0, "\004done", "\012cotidianas", 
+ "\004juez", 0, "\014realizaremos", "\010privados", "\011recitales", 0, 
+ "\007forales", "\007c\341lculo\010entregan\006simula", "\005finas", 
+ "\005ambos", "\004dice\010resultar", "\005vista\015recientemente", 
+ "\006quedes\005fines\015negociaciones\014legislativas", 0, "\010se\361alada", 
+ 0, "\011candidata", "\016especialidades\007recital", 
+ "\005vemos\015farmac\351uticos", 0, 0, 0, 
+ "\011b\372squedas\010resaltar\013preguntar\341n\005finos", 0, 
+ "\012despliegue\012afinidades", "\004paga\016norteamericano\007mapuche", 0, 
+ "\005tinta\011obtenci\363n", 0, 0, 
+ "\007entrada\010resultan\010ocupando\012exportable\007p\341rroco", 0, 
+ "\011organizar\006basura", "\006adem\341s\007colonia\007indicar", 0, 
+ "\011protector", 0, "\012reinstalar\011ordenados\011considero", 
+ "\005marc\363", "\004pago", 0, "\014protagonismo\007juder\355a", 
+ "\007agradan\004alas\013seminarista", 0, "\006petrus\006n\363mina", 
+ "\007hacerlo\011potencien", "\013especificar\007pierdan\007caridad", 
+ "\013explotaci\363n\010inaugur\363", 0, "\013profesional\013cancelaci\363n", 
+ "\003voy", "\007externa", 
+ "\020particularidades\016acontecimiento\012campeonato", 0, 
+ "\007rallado\010pr\363spero", "\007m\355stica\015peregrinaci\363n", 0, 
+ "\013clasificado", 0, "\013estrat\351gico\013discursivas", 
+ "\012validaci\363n", "\012compromiso\007tabelle", 0, "\007escapes", 0, 
+ "\007ultimas", "\011barcelona\006planta\012meditaci\363n", "\004beb\351", 
+ "\004vera", "\010obtienen\006salom\351\007saliera", 
+ "\007existen\006grises\011previstos", "\007actitud", "\010granular", 
+ "\007tenerlo", "\006patios", 0, "\007ponerla\010cacerola", 
+ "\003etc\013corpulentos", "\012libertades", "\005haces\011autobuses", 0, 0, 
+ "\011subscrito", 0, "\004tema\010separase", 0, "\012libertador\007metales", 
+ "\005votes", "\016constantemente\004teme\011coincidan", 0, "\010riquezas", 0, 
+ "\006brillo", "\007entrega\007hablado", 0, "\012limitaci\363n", 0, 
+ "\005votos", "\007formada\012asignatura", "\011carretera\006donado", 
+ "\014vegetarianos\012dirigentes", 0, 
+ "\012dominicana\011estupendo\011occidente", 0, 0, 
+ "\013tratamiento\017responsabilidad", 0, 0, 0, "\014participaron", 
+ "\007filtros\006radial", 0, "\005vivan", "\006agrado", 0, 
+ "\005rojas\005pides", "\007privado\005viven\012ofreciendo", "\010merienda", 
+ "\006sujeta", 0, "\007resulta\011diskettes", 
+ "\003hoy\007medidas\006fusi\363n", 0, 0, 0, 0, 0, "\007perder\341", 0, 
+ "\005pampa\011discoteca\005rojos", "\010mudanzas\011destruyen", 
+ "\005act\372a", 0, "\012requerimos\010numerosa", 
+ "\010pel\355cula\012molestarte", "\010requiere", 0, "\010ver\363nica", 0, 
+ "\007muertos", "\014clasificados\007visitar\014estrat\351gicos", 0, 
+ "\012estaciones", "\004acto\010envasado", 0, "\005dudar", 0, "\012recalcaron", 
+ 0, 0, "\006origen", 0, 0, "\011surgieron", 0, 
+ "\005aviso\006contar\010utilizan", 0, "\007llamar\341\014seminaristas", 0, 
+ "\012organizado", "\007austral", "\010partidas", "\011conflicto", 0, 0, 
+ "\013jerarquizar", 0, "\007obtiene", "\006semana\005comes", 0, 
+ "\013recaudaci\363n", "\005sigla\004pag\363", "\011ediciones\005sigma", 
+ "\011accidente\004vino", 0, "\013incompletas\011solidario\014limitaciones", 
+ "\011auditorio\013comunicamos\007cl\341sica", "\011educaci\363n", 
+ "\013interactiva\005ocupa", "\011practicar\007armamos", 0, "\006ayudas", 0, 
+ "\010utilizar\007d\341ndole\012audiencias\012modificada", "\013productivas", 
+ 0, "\014tratamientos", 0, "\003d\355a\007inform\363\010propicia", 
+ "\010grabador", "\010grabados\006teatro", "\010desastre\005vence", 
+ "\005vende\012estuvieron\005peste\005renda", 0, "\010pr\363tesis", 
+ "\005renga\010reducida", "\004ver\341", "\006id\363neo", "\004gato", 
+ "\010incluido", "\011referidos\014consideraron", 
+ "\007riqueza\012soldaduras\010reducido", 0, 0, "\010comunico", 
+ "\010clientes\012psicolog\355a", 0, 0, 
+ "\005renta\006estafa\013acompa\361ante", "\010incluida\006habr\341n", 0, 0, 
+ "\010teniendo\011aceitunas", "\012consorcios", 0, "\007capital", 
+ "\004caja\010trayecto", "\005aquel\006veamos\007heladas", 0, "\012mencionada", 
+ "\006accede", "\005ovino", "\013importaci\363n", 
+ "\010nataci\363n\011brindemos", "\012vocaciones\006ra\355ces", 0, 
+ "\012interesado\014mensualmente", "\003ves\005evite", 
+ "\016identificaci\363n\007guiadas", "\007gustado\012morfolog\355a", 
+ "\012lamentamos", 0, "\007quieras\006ser\355as", "\010qu\355micos", 
+ "\006lograr\010pensando", "\011capacitar", "\007algunas\016automovilistas", 0, 
+ 0, "\013represalias", "\005bruta", 0, "\011resultado\006pobres\007coment\363", 
+ "\003ten", "\012presentar\341\011fortaleza", 
+ "\010habitado\011aventuras\005vigor", "\010enchufar", 0, 
+ "\011principal\004ella\004foto", "\006a\361aden", 0, 0, "\005tesis\004elle", 
+ 0, "\007botines", "\013produciendo\013corporaci\363n", 0, "\006novela", 
+ "\012servilleta", "\007propios\014interactivas", "\007botones", "\007utiliza", 
+ "\004ello\004cu\341l", "\016posteriormente", 0, 0, "\011importado", 
+ "\010almorzar", "\004arma", "\007recetas", "\007partida", 
+ "\006asesor\007grabado\012respectiva", "\011elaborado\011agradezco", 0, 0, 0, 
+ 0, 0, 0, 0, 0, 0, "\016minuciosamente", "\013integrarlos", 
+ "\007cliente\012cat\341strofe", "\006aparte", "\006deudas\013comodidades", 0, 
+ "\011magn\355fico\012despiertan", 
+ "\010alcanzar\012inmediatas\014reproducci\363n", 0, 
+ "\013fundamentar\007sanci\363n", 0, "\014programaci\363n", "\004ma\355z", 
+ "\010subrayar", "\005redes\010cubierto\006morr\363n", "\014reparaciones", 
+ "\010cubierta", 0, "\014concepciones", "\013corresponde\004nace\007haberlo", 
+ "\011empezando\013especificas\006marine\007otorgar", "\004d\355as", 
+ "\016personalizadas", 0, "\003voz\010parlante", "\005cause", "\010contener", 
+ "\005tiene\015especializada", 0, "\004siga\006novios\006mu\361eco", 
+ "\011utilizada", 0, 0, 0, 0, 0, "\011utilizaba", "\005mitad", "\010vampiros", 
+ "\014capacitaci\363n\013estudiantes\005pieza", 0, "\010aparecer", 
+ "\013cuadernillo\012asesoramos", "\004sigo", 0, "\012ingenieros", 0, 0, 0, 
+ "\007calculo\012excitaci\363n\015present\341ndose", 0, 0, 
+ "\006cuadra\004faro", 0, "\011corazones\004dama\012misteriosa", "\006arenas", 
+ "\011porciones", 0, "\011argentina\011grabadora\007lecci\363n", 
+ "\007hierbas\007relojes", 0, "\007bosques", 
+ "\011monitores\010biling\374e\013bienvenidos", 0, "\006marcos\005selva", 
+ "\006latina\010pimienta", "\011migraci\363n", "\005aqu\351l", 
+ "\006cargas\010denuncia\007lechero", "\011registros\010aparecen\006cubano", 0, 
+ "\005ponen", 0, "\007ustedes\011numerosas", "\016funcionamiento", 
+ "\017correspondencia\013laboratorio", 0, "\011magn\355fica\006carpas", 0, 
+ "\005micas\011numerosos", "\013actualmente", "\006cartas\005monos", 
+ "\010procesan", "\012accesorios", 0, 
+ "\007armando\012desarrolla\007alcanza\011ocasional", "\010procesar", 
+ "\011dentarios", "\007costar\341", "\007depende", "\013calendarios", 
+ "\013solicitarla\005panda", 0, "\006gestor\005licor\005micos", 
+ "\011cubiertos", 0, 0, "\007facetas", "\015especializado", "\010promesas", 0, 
+ "\010glosario\010analista", "\013convertirse", 
+ "\004flor\007salidos\010cacer\355as", "\007declara", "\014corresponden", 0, 0, 
+ "\004come", 0, 0, "\006extrae", "\007motivar", "\015decodificador", 0, 
+ "\007saludos\007escrita\011prohibido", "\005se\361or\007vencer\341", 
+ "\007cambiar", "\004como\013consultor\355a\005ven\355s", "\006pueblo", 0, 
+ "\007obispos\007capit\341n", "\005mejor\012fabricante\005lejos", 
+ "\005sirve\007sufrido", 0, "\007cuantos\014concordancia\006granja", 
+ "\005cuyas", "\007subraya", "\011responses", 
+ "\011argentino\007autob\372s\006regazo\007cuartos", 
+ "\010regional\005suele\010estrag\363n\005pegar\006aclar\363\006pautas", 
+ "\006cedros\012provincial\012disertante", 0, 
+ "\007dibujos\010zapatero\006b\341sica", 0, "\015requerimiento\006huerto", 
+ "\011modificar\016tranquilamente\012destinados", "\014sudamericano\006guarda", 
+ 0, "\011incluidos\007querida", "\005cuyos\010nombrado", 0, "\012posiciones", 
+ "\013conveniente\010dise\361ada", "\007juliana\013alimenticia\010posibles", 
+ "\011mejorando", 0, 0, "\010diagonal", "\010dise\361ado\011incluidas", 0, 
+ "\007realizo", "\010conteste\005conos", "\005malas\004ropa", 
+ "\015organizadores", "\007aparece", 
+ "\010contesta\013liquidaci\363n\010fracasos", 
+ "\005\372nico\005vasos\010canastas", "\011formulada\007sobrado", 
+ "\010consumir\007revisar", 0, "\006futuro\011aproveche", 
+ "\005banda\013advertencia\007f\355sicos\010contesto", "\005panes", 
+ "\011cristiano", 0, "\012recipiente", "\005malos\011funcional\011subordina", 
+ 0, "\005jap\363n", "\013ferreter\355as\012metaf\355sica", 
+ "\013museograf\355a\014concluyentes", "\010enviadas\011proveemos", 0, 
+ "\011trabajar\341\013complejidad\011conforman", 
+ "\014homologaci\363n\006exigen\017precipitaciones", "\003sos", "\006garfio", 
+ "\007promesa", 0, "\011econ\363mico\005llama", 
+ "\015protagonistas\011cobertura", "\004pe\361a", 
+ "\010promover\013complementa", 0, "\011memorizar", 0, 0, 0, 
+ "\005haber\007sonidos\004lula", "\014laboratorios", 0, 
+ "\013extractores\013reaparici\363n", 0, "\012equilibrio", "\015optimizadores", 
+ 0, 0, 0, 0, "\012orientados", "\010ambiente", 0, 0, 
+ "\004load\012suavemente\007pensaba\010creencia", 0, 
+ "\005favor\014solicitarlas", "\005muere\006fiscal", "\007barrios\005turbo", 
+ "\007\372ltimos\010chequear\015concentraci\363n\010facilita", 0, 
+ "\005mueve\013aficionados", "\005mondo\014disfrutarlas", 
+ "\005poner\012moderaci\363n", "\006amplio\004loan\005rollo", 0, "\007tomillo", 
+ 0, "\005tetas", "\004jugo\016almacenamiento", 
+ "\011relajante\005turno\004fija\011garantiza\014convenientes", 
+ "\005picar\010racional", 0, "\007posible\004tasa", "\005calas", 
+ "\014adolescentes\005viuda", 0, "\005gu\355as", "\005monto", "\011mosquitos", 
+ "\004ped\355", 0, "\016imprescindible", "\010privadas", 
+ "\010producto\004fijo\010tragedia", "\012copiadores\004hubo", 
+ "\013instalarlos\007canasta", "\013profesorado\012participen", 
+ "\005micro\010quisimos\007prometo", "\010aprobado", 0, 
+ "\005india\015probablemente", "\011comenzado", "\006dorada\012consulados", 0, 
+ "\016distribuidores\014convocatoria", "\014incorporarse\010directas", 
+ "\006borrar\007bebidas\007trasero", 0, "\011variantes", 
+ "\012disminuyen\013condimentar\010centrado", "\007enviada", "\006carril", 0, 
+ "\012destacadas\015experimentada", "\011seguridad", 
+ "\010recargue\012antig\374edad", 0, 0, 
+ "\014alimenticias\006efecto\011proclaman", 0, "\004diez", 0, "\007mayores", 0, 
+ 0, "\010palmitos", "\012ocasionado", "\013exportaci\363n\012directivos", 
+ "\006grutas\007embalar", 0, "\004nada\012orientadas\006vuelvo", 0, 
+ "\010insisti\363\004sosa", "\006gastar\007evitare\012preguntaba", 
+ "\011milanesas", 0, "\016vicepresidente", "\005toque\010ecuaci\363n", 0, 0, 0, 
+ "\005habr\341", "\010finanzas", 0, "\014complementan", "\010sufragio", 
+ "\007limpios", 0, "\013contrase\361as\010diabetes", "\006sangre", 0, 
+ "\014provenientes", "\012nunciatura", "\007di\341logo\013visitaremos", 0, 0, 
+ 0, "\006define\011tentativo\007sienten\004fase", 0, "\010prote\355na", 0, 0, 
+ 0, 0, "\013experimenta\012incremente", "\010rincones", 
+ "\013referencias\005logra\005calma", "\011saludable\010modernas", 0, 
+ "\006rebaja", 0, 0, "\006templo\011corrector", 0, 0, "\013solicitados", 0, 0, 
+ "\015obsequiaremos", "\011autentico\014profesorados", "\004moda", 
+ "\005a\361ade\005envi\351", "\005tipos", 0, "\007directa", 0, 
+ "\010galer\355as", 0, "\010aprender\006repare", "\003oro\006pecado\004reja", 
+ "\006cubr\355a", 0, "\013constituyen\011mandarnos", "\006aunque\007impacto", 
+ "\004modo", "\010escribir", "\006gastes", "\006cruzar", 
+ "\007inter\351s\012importador\005piden\010preocupe", 
+ "\011consolida\012confirmado\014forzosamente\013conseguimos", "\007aportes", 
+ 0, "\014directamente", 0, "\005solar", "\007privada", 
+ "\013incrementar\006taller\006quince", "\004cuya", "\005roles", 
+ "\011enseguida", "\012complicado", "\006alabar\006fuiste", 0, 0, 
+ "\005clima\012parlamento", "\017recomendaciones", 
+ "\014colaboraci\363n\011desempe\361o", "\011relevante", "\010bastante", 0, 0, 
+ "\004cuyo", "\010nacional", 0, 0, 0, 0, "\010reenviar\012satisfacer", 0, 
+ "\014clasificadas", 0, 0, "\012variedades", "\006copiar\004cono\005eches", 
+ "\013preguntarle", 0, 0, "\004auto", "\016representantes", 0, 
+ "\011aut\351ntica", 0, 0, 0, "\012estudiando", "\010exportar\011gratuitas", 
+ "\007moderna", 0, "\011almendras", "\013mobiliarios", 0, 
+ "\011gratuitos\017alfab\351ticamente\005sufre", "\010soberana", 0, 
+ "\010inmigrar", "\011proyectos", 0, "\006perder", "\010soberano", 0, 
+ "\004deja\007cl\355nico", "\005secas", 0, "\006perros\015aparentemente", 
+ "\004deje\007papeles", "\007galer\355a", "\007aprende", "\013interactivo", 
+ "\005mamas", 0, 0, "\007orlando", "\014experimental", "\015personalizado", 
+ "\007enga\361os\004dejo\007salgado\011adaptadas\007nobleza\010piquetes", 
+ "\005secos", "\006chalet", 0, "\013formulaci\363n\014experimentar\006tribus", 
+ "\011destellos", "\011siguiendo\012insertarte", 0, "\012neum\341ticos", 
+ "\007trancas", 0, 0, "\004mo\361o", 
+ "\014inolvidables\011emprender\013conciencias", "\010expuesto", 0, 0, 
+ "\012portuguesa", "\007alcanz\363\013terap\351utica", "\013proveeremos", 
+ "\007empezar", "\010paisajes", "\003hay", 0, "\004polo\014higienizadas", 
+ "\010conexi\363n\011m\372ltiples\014inicialmente", "\010ocurrido", 
+ "\010nuestras\013suscribirse", 0, "\007c\363digos\010cruzando", 
+ "\005hacer\010explicar", 0, 0, 0, "\004gu\355a\005usado\010aburrido\005votar", 
+ "\007espiral", "\005morir", "\006juegos\011aut\351ntico\007declar\363", 
+ "\005hotel\004esos\011aquelarre", 0, 0, 0, "\004loba\007hacerte", 
+ "\012definitiva\004semi\012instancias\013competici\363n", "\006agenda", 
+ "\013cent\355metros\012compartida", "\013veterinario", "\005porto", 
+ "\012expresados\004vaya", 0, 0, 0, "\014inmobiliaria", "\012cordobesas", 
+ "\011recepci\363n\014inmobiliario", "\010escuchar\007sesenta", 
+ "\004lobo\006cantan", "\010pizarras", 0, 0, "\007subray\363", 
+ "\017caracter\355sticas", 0, "\006cu\341ndo", "\006pirata", "\005camas", 
+ "\013informaci\363n", 0, "\007m\351todos", "\013emergencias\006chapas", 0, 
+ "\010comandos\012azimutales\014dependientes", 0, "\011totalidad", 
+ "\014interactivos", 0, 0, "\015universidades", 0, "\012contactado\007tristes", 
+ "\011noviembre", "\012desactivar\014inmovilizaba\013atracciones", 0, 0, 0, 0, 
+ "\012transfiri\363", 0, "\013traductores", "\010m\372sculos", "\011depositar", 
+ 0, 0, "\013eliminarlos\011obligados", "\004clip", "\010software", 
+ "\013contestador", 0, "\006puerta\005ultra", "\006puesta", 0, "\006activa", 0, 
+ "\010solicito", "\012conseguida", "\010solicita\011aplicarse\012tribunales", 
+ "\007tendr\355a", "\010emisoras", "\010abonamos\007explica", "\010solicite", 
+ "\010supuesta", 0, "\012satisfecho", "\012masajistas\011sacadores", 
+ "\005entre\007permiso", 0, "\005torre\010supuesto", 0, "\005porta", 
+ "\004haya\013transportes\012expresadas", 0, "\005busco\015sincronizados", 0, 
+ 0, 0, "\010tarjetas\016vocalizaciones", "\013intelectual\011agr\363nomos", 0, 
+ "\005tigre\004soto\007capaces", 0, 
+ "\007tambi\351n\012psiquiatra\012adjuntamos", "\006charla\011compasi\363n", 
+ "\004dej\363", 0, "\007suceden", "\010ensalada\012navegantes", 
+ "\007paisaje\010desfiles", 0, 0, "\007cultivo", 0, "\012dormitorio", 
+ "\007nuestra", 0, "\007alguien\007exponen", 0, "\012irrompible\005alg\372n", 
+ "\007m\341ximos\007cuartel", 0, 0, "\007comando", "\016conversaciones", 0, 
+ "\011parciales", "\005noche\005junta", 0, "\007engorde\013destrucci\363n", 
+ "\011canciones\014incrementar\341", "\020comercializaci\363n", 
+ "\014conquistador\006vecina", "\012garantizar", 
+ "\010centavos\014veterinarios", 0, "\013establecido", "\014proped\351utica", 
+ "\007escucha", 0, "\004baja", "\005grado\007pruebas", 0, 
+ "\010enviando\010reclamos", "\004baje", 0, "\006habr\355a", 0, "\005cruda", 0, 
+ "\010fen\363meno", "\010funciona\013excluyentes\005grano", 
+ "\006pedido\006ingles", "\012argumentos", "\004bajo\011distancia", 
+ "\007durante\010funcione\016emocionalmente", "\005graso", 0, "\007cerebro", 0, 
+ "\010patentes\005borra\012anunciaron", "\011ajustarse", 0, "\011visitando", 
+ "\012peregrinos\007dijeron\010tertulia", "\011domicilio", "\007m\363viles", 0, 
+ 0, 0, "\012requiriese", 0, 0, 0, 0, "\013facturaci\363n", 
+ "\010vaticano\007vinculo\005viv\355a", "\006olvida", "\010vaticana", 
+ "\007tarjeta", "\020electrodom\351stico", 0, 0, "\010tormenta", 
+ "\011indicando", "\010infantil", 0, "\007regalos\007reciben\007desviar", 0, 
+ "\015visualizaci\363n", "\007hermosa\007desfile", "\010voluntad", 
+ "\003ira\005venir\007viajera", "\010efectuar", "\012lentamente\007ciertas", 
+ "\007emisora", 0, "\013periodistas\005tazas", "\006apague", 
+ "\006siglos\007colmena", "\013relacionado\011cap\355tulos\010respecta", 
+ "\011agradable\006signos", 0, "\010urgencia", 0, 0, "\006alem\341n", 
+ "\013realizaci\363n", "\006precio\005copas", "\003ac\341\006educar", 
+ "\012consumirla", "\006edades\007definir", "\010distinta", 0, 
+ "\006ofrece\011horizonte\010respecto\007percat\363", "\013procedencia", 
+ "\010trabajan\006iconos", 0, "\006premio\010distinto", 0, 
+ "\010trabajar\012lanzaremos\007reclamo", "\010trabajas", 0, 
+ "\011divididos\011confianza", "\011semanales\014convicciones", 
+ "\004ante\011divididas\011fijadores", 0, "\006previo\006vencer\012sobrevivir", 
+ "\010desayuno\006vender\014automatizado\011aerol\355nea", 
+ "\011automotor\005papas", 0, "\010servidor", 0, 0, "\012atractivos\006robots", 
+ 0, "\011exclusivo\014recomendable", "\005manos", 0, 
+ "\013integraci\363n\007patente", 0, 0, "\006ultima\010enviados\012creaciones", 
+ "\007centavo", 0, "\006escala", 0, "\005lapso", 0, "\011dimensi\363n", 
+ "\012procesaran", "\003haz", 0, "\005pelea", "\006genial", "\011aparici\363n", 
+ "\011organismo", "\015sobresaliente", "\012iniciativa\013telegr\341fico", 
+ "\004luna\007salidas", "\004cama\005mitre\011durmiendo", "\005poseo", 0, 0, 0, 
+ 0, "\013devaluaci\363n\006choque", 0, "\012volveremos\007untador", 
+ "\010arrancan\006tard\355a", "\007bazares\010capturar\005apaga", 0, 
+ "\010seriedad\007bodegas", 0, "\006hombro\010facultad\007prefijo", 
+ "\007visites", 0, 0, 0, "\006darles", "\014cuestionario", "\007pedir\341n", 
+ "\005negra\014disfrutarlos", 0, "\010delicias", "\004loco\011santuario", 
+ "\010elecci\363n\004baj\363", "\005copia\013representar", "\011peritajes", 0, 
+ "\006dise\361o\010capturan", 0, "\012mantenerse", "\005cubre\013editoriales", 
+ "\004file\005miedo", "\013empuj\341ndolo", 0, 
+ "\006dinero\005ciega\013recreativas", "\014desperfectos", 
+ "\007fuerzas\013suscribirte", 0, "\006piojos\015problem\341ticas", 
+ "\007trabaja\007amistad", "\013directorios\012clasificar", 0, 0, 
+ "\014cooperativas\004diga", "\011exclusiva\011armonizar", 0, 
+ "\014relacionados\012distinguir", 0, "\011menciones", "\004arpa", "\004rape", 
+ "\011sanatorio\007planeta\010mercados", 0, "\013traumatismo\010plantear", 
+ "\007enfoque\010batallas", "\007compare", "\010navide\361o", "\004digo", 
+ "\007enviado\004cruz\010gen\351rico\010navide\361a", 0, 0, 0, 0, 
+ "\007enviara", "\014invitaciones", "\005puede", 
+ "\014prestaciones\014contribuci\363n\011regalando\010prelados", 
+ "\011atl\341ntico\010resulten", "\010postales\005bella", 
+ "\006merece\010invertir\012baloncesto", "\006rotura", 
+ "\006abuela\011propiciar\007tachado", "\006puedan", "\007filmado", 
+ "\005anula", "\012intensivos", "\012conectarlo", "\011alentarte", 
+ "\010comuni\363n", 0, 0, "\010noroeste", 0, 0, 
+ "\005seg\372n\005posta\011seducidos\011art\355stica", 
+ "\013autoridades\007oscuros", 0, "\012investigar", 
+ "\012exportador\007arranca", 0, "\006indic\363", "\012incluyendo\006separa", 
+ "\012generadora", "\014antecedentes", 0, "\010comience\017lamentablemente", 0, 
+ "\006febril", 0, "\011prolongar", "\005salen", "\006sellos", "\006planos", 0, 
+ 0, "\013seguimiento\005mando", "\013espec\355ficas", "\006olvid\363", 
+ "\006platos", "\010llevar\341n", 0, 0, "\005hagas\015conjuntamente", 0, 
+ "\007oraci\363n\010jerem\355as\006plazos", "\007laboral", 
+ "\011hirviendo\006mimbre\010secundar", "\007captura", 0, 0, 0, "\010hallazgo", 
+ "\007noventa", 0, 0, 
+ "\015independiente\013orientaci\363n\010provisto\006rasgos", "\010programa", 
+ 0, "\005firma\005claro\007invitar", "\011vitaminas", 
+ "\006simple\004byte\005tiros", "\012escuchaste\012preocupado", 
+ "\007residen\006querr\341", "\007batalla", "\007mercado", 
+ "\013verificados\007plantea", 
+ "\014traumatismos\011navide\361os\012necesitar\341\006dorado", 0, 
+ "\011navide\361as\006praxis", 0, "\013interesados", "\006tangos", 0, 
+ "\007prelado\014condicionada\006causas", 0, "\011comenzar\341\013habilitamos", 
+ 0, 0, "\013seleccionar", "\014supervisoras\004gozo", "\005fuera", 
+ "\011inmediato", "\007d\351biles", "\004pega", 
+ "\005huevo\006tantos\006biblia", "\005donde\016transferencias", 
+ "\003can\011desvisten", 0, 0, 0, 0, "\013transferir\341", 
+ "\005hogar\006placas", 0, "\005ricas", "\017fortificaciones", 0, 
+ "\006equipo\004copa\007concibe", "\014fraccionados\014afiliaciones", 0, 
+ "\010cantamos", "\005nicho", "\015transformando", "\010di\341metro", 
+ "\007elevado", "\015electricistas\012festividad", 0, 0, "\005ricos", 
+ "\011contestar\010llegaste", "\006quinta", "\006f\355sica", 0, 
+ "\014publicitario\007salte\361a", "\006playas\006cortos", 
+ "\006plazas\012racionales", "\010juguetes", 0, 0, 0, "\003dos", 
+ "\011agrupados\013aeropuertos", "\004cien\006cortar", 
+ "\012comprobada\014metal\372rgicas", "\013instalaci\363n", 
+ "\011ense\361ar\341n\016desinfectantes", 0, "\006tratar\014precauciones", 0, 
+ "\015inscripciones\007amantes", "\013destituci\363n", 
+ "\012cumplea\361os\010lumbares\012diet\351ticas", 
+ "\011modelizar\012mermeladas", "\007llevar\341\005mel\363n", 
+ "\004c\363mo\017revolucionarios", 0, "\011programas", 0, 
+ "\006bancos\010absorber\013interventor", "\006bandos", 
+ "\011margarita\005techo", 0, "\004dele", "\011positivos", "\013contencioso", 
+ 0, "\004time", 0, "\007secreta", "\007pelados", "\006coches", 0, 
+ "\010comercio", 0, "\007pasajes", "\014subjetividad\007pidamos", 
+ "\004timo\013tentaciones", 0, 0, "\015pr\341cticamente", 0, 
+ "\010banderas\007c\363smica\010inmersos", "\011facilitar", 
+ "\016impresionantes", 0, "\006llamar", "\004pone", 0, 0, 
+ "\005curso\012reciclamos", 0, "\012lubricante", "\005sal\363n", 0, 
+ "\016envejecimiento\007agregar\005verse", "\005verte", "\005salir", 0, 
+ "\012reciclados", "\005dalle\014representar\341", "\011flexibles", 
+ "\007amnesia", "\013recorriendo", 
+ "\013utilitarios\007esperan\006innata\004cana", "\005lotes", 0, 0, 
+ "\010textiles\007escribo\005adora", 0, 0, "\005joven\012inicializa", 
+ "\010disculpe\007motivos", 0, 0, "\005motor\004r\355en", 0, 
+ "\010ordenado\007diezmar", "\004cano", 0, 0, 0, "\012cuestiones", 
+ "\010competir\010joystick", "\012confecci\363n", 0, "\007sabemos", 
+ "\011palestino", "\012nuevamente", 
+ "\010enviamos\006captan\014geopol\355ticas", "\006ciclos\011clausulas", 
+ "\015legislaciones\006excusa", "\007aumento", 
+ "\005hojas\007poes\355as\012astronom\355a", 0, 0, "\012referentes", 0, 0, 
+ "\012ilustradas\005solas", "\006\341cidos\010mascotas\010pensaron", 
+ "\006titulo\007absorbe\007result\363\007relatos", "\010atenci\363n", 
+ "\007pechuga", 0, "\015informaciones", 0, "\012alimentare\010hermanos", 
+ "\005veras", "\013emocionales", "\010vocaci\363n", "\010testigos\006termal", 
+ "\005cifra", 0, 0, "\011esperando\012adyacentes", 
+ "\013enormemente\007salario", "\010describe\007celem\355n", "\011accesible", 
+ "\006masivo\007bandera\012solicitada\007mirando", "\007t\363picos", 0, 0, 
+ "\012sorprendi\363\007ascenso", "\012remitentes", "\011dise\361ador", 0, 
+ "\010asesores", "\004alfa\004bono", "\013fabricantes", 0, "\010perchero", 
+ "\014continuaci\363n\014terap\351uticos", 
+ "\013presupuesto\006unidos\007l\372gubre", "\006quer\355a", "\010portales", 0, 
+ "\005hayan", 0, 0, "\007diarios\010bloquean", 0, "\006viendo\013esperanzada", 
+ "\015inimaginables\004palo", "\007tutores", "\006sector", 0, 0, 0, 
+ "\013principales\012industrias\007locales", 0, 0, 0, "\007ensayos", 
+ "\015destinatarios", 0, 0, 0, "\006aclara", 0, 
+ "\011literaria\010condenan\007\355ntimos", 0, 0, "\006quiere", 
+ "\010p\372blicas", 0, "\005parto", 0, "\007coloque", "\006g\351nero", 0, 0, 0, 
+ "\012hormiguita", "\006inicio", 0, 0, 0, "\006teor\355a", 0, 0, 0, 
+ "\013temperatura", "\010optimiza\007mascota", 0, "\011incre\355ble", 
+ "\007hermano", 0, "\006single", 0, "\006casino", 0, "\012impedancia", 0, 
+ "\011veh\355culos", "\005ni\361os\007creados", "\010muestras", 
+ "\011enciendan\005moras", 0, "\013alternativo", 0, 
+ "\013completando\006ovejas\013generadores", "\007flechas\004am\351n", 
+ "\011c\351ntricos\010inferior", "\010fantas\355a", "\013porcentajes", 
+ "\010verdades", 0, "\010pasajero", "\007viernes", "\012trabajamos", 
+ "\016disponibilidad\012entendemos", "\006tiempo\014presupuestos", 
+ "\005vinos\011frecuente", "\013navegadores", 0, "\006\372ltimo", 
+ "\007quillas", "\004remo", 0, "\011bailarina\007testigo", "\005tarde", 
+ "\005bares\006rostro\007bloquea", 0, 0, 
+ "\010est\341ndar\013presentar\341n\010muestran", 
+ "\012deliciosos\011semejante", 0, 0, "\010flacidez", "\010caravana", 0, 
+ "\011gram\341tica\011ofrecerle\010criticar", 0, 0, 
+ "\006podr\341s\013registrarte", "\005l\355der\005ver\341s", 
+ "\010plateada\011sostienen", "\013postulantes\014curiosidades", 0, 0, 
+ "\007p\372blica\013veinticinco", 0, 0, 0, 0, "\012requeridas\011precarias", 
+ "\011extensiva", "\007tomando", 0, "\007condena\011serenidad", "\006costos", 
+ "\005menor\014afrodisiacos\011violaci\363n", 
+ "\003fin\011manejable\007sueldos\010cometido", "\006logran", 
+ "\007famosos\007cuentos\007caldera", 0, "\014departamento", 0, 
+ "\007ganar\341s", 0, "\015independencia\013renunciando\010s\355mbolos", 0, 
+ "\004visa", 0, "\014gustosamente", 0, "\007cuentan\005reino", 
+ "\010adquirir\010levantar", 0, "\005env\355e", "\012originales", 
+ "\007cuestan", "\013ense\361aremos\006sue\361as", "\007cositas", 
+ "\005silla\005trama\006avanza", 0, "\011protegido", "\012permanente", 0, 
+ "\007anuncie\014alternativos", "\010prospero\011bloqueado", 
+ "\005trata\012aseguramos\012encuentren\005coros\010mejorado", 
+ "\005mapas\015contracciones\007mezclar\004rota\013posteriores", 0, 
+ "\005silva\011marcaci\363n", "\014construyeron", 
+ "\012escritorio\013atravesando\007agregue", "\010enviaron\012presencial", 0, 
+ "\010ejemplar", 0, "\010sinopsis\007miseria", "\004tino", 0, 
+ "\005largo\011solidaria\007totales", "\007esquiar", "\014temperaturas", 
+ "\012desinstala", "\013concentrado", "\010variadas\007sagrada\011decididos", 
+ 0, 0, "\007marista", "\005llega", "\005ramas", "\005barra", "\013comprobados", 
+ "\006senado", "\011coordinar\005cenar", "\011contin\372an", 
+ "\004pida\007muestra\005llena", "\005resta", "\013transmisi\363n", 0, 
+ "\004pide", "\012informarle", "\010adelante", 0, "\005lleva\005ramos", 
+ "\007comidas\006secada\011actitudes\005vivi\363", 0, "\012contribuir", 0, 
+ "\004agua\004sepa", "\004pido\007blancos\015transmisiones", 0, 0, 
+ "\013formularios\004ague", 0, 0, 
+ "\006elenco\012adjuntando\006revise\011carest\355as", "\010externos", 
+ "\011negativas", 0, "\005vac\355o", "\006reales\006calcio\012equitativo", 0, 
+ "\003ida\013profundidad", "\010turrones", 0, "\006d\351bito", 
+ "\007s\355mbolo\007critica", "\003dar\005corea", 
+ "\012encontrar\341\012tropicales\005\341rabe\007setenta", "\012substancia", 0, 
+ "\012estrategia", "\005abajo", 0, "\004fina", "\005riego", 
+ "\012explicar\341n", "\010consigue", 
+ "\004fine\006humana\011altavoces\012sacerdotes\005capas", 0, "\011verdadero", 
+ "\014arquitectura", "\005corta", 0, "\006pa\355ses\011modelando", 0, 0, 
+ "\012excelencia", "\006suples", 0, "\011solitario", 0, "\005desea", 
+ "\005somos\004rara", "\015inconveniente\007heridas", "\015universitario", 
+ "\013gobernantes\013sacramental", 0, "\011retirados", "\006deseen\004mead", 
+ "\010marcaron\014seguidamente", "\011discretos", "\005\372rico", 
+ "\015especialmente\010ayudar\341n\011dispuesto\007variada", "\005penas", 
+ "\011comprobar", "\010extracto\010desearte", "\012trabajando\004raro", 
+ "\010reacci\363n", "\004mean", 0, "\006globos", "\010descarte\007cansado", 
+ "\020vulnerabilidades\007cantado", "\007entrado", "\010registro", 
+ "\006deber\341", "\010registra\016incertidumbres", "\005mafia", 
+ "\010adquiri\363", 0, "\010radiante", "\004algo\006acerca", 
+ "\006menudo\005lento", "\010fundador\012utilizarse", "\011dep\363sitos", 
+ "\007manejar", "\010carezcan\007estaban", "\013reflectores", "\007vegetal", 0, 
+ "\007esteban\005paseo", 0, "\010golosina", 0, "\007externo", 0, 
+ "\012fosforitos", 0, "\010guitarra", "\010elevando", 0, 
+ "\013incorporar\341\011iniciadas", "\013determinada\011fronteras", 0, 
+ "\007aportan", "\005niega", "\005debi\363", "\006campos\010devolver", 
+ "\005rango\007retirar\012planificar", 0, "\011funcionar", 0, "\005par\355s", 
+ 0, "\012quinientas", "\006mueble\011enfrentar", 0, 0, 
+ "\013segmentadas\010alcances", 0, "\011humanista", 0, "\007m\351dicos", 0, 0, 
+ "\006ilegal\012autonom\355as", 0, "\010comenzar", 0, 0, "\007espalda", 0, 
+ "\017aprovechamiento", "\012ilimitadas", "\005anual\007creador", 
+ "\011sanitario", "\010cruceros", 0, 0, "\013catalogador", 0, 0, "\006dotado", 
+ 0, "\006dental", 0, "\013inform\341tica\006aborto\006trauma", "\007vigilia", 
+ 0, 0, 0, 0, 0, "\003pie", 0, 0, "\005diosa\012candidatos", 0, 0, 
+ "\007peralta", 0, "\005bases\007fundado", 0, "\007francos", "\005beben", 
+ "\007canci\363n", "\011simb\363lico", "\014honestamente", 
+ "\007militar\007frascos", "\012elecciones\007postura", 0, "\011consultas", 
+ "\007alarmas", "\005canal\006m\351todo", "\007mendigo", "\012predadores", 
+ "\005pasta\010decisivo", 0, 0, "\011solicitar\013incorporado", 0, 0, 0, 0, 
+ "\012habituales\014coincidencia", "\011configura", "\011mencionar", 
+ "\014incorporar\341n", 0, "\010llamadas\005nieto", 0, 0, 0, 
+ "\012hip\363critas\012sorprender", "\012producci\363n\007pasarlo", 0, 
+ "\012secretario\011producido", 
+ "\007alcance\007lectora\010radiales\010naciones", 
+ "\013espec\355ficos\006recoge", "\006juntar", 0, 0, 0, "\012informamos", 
+ "\012detalladas", "\010teolog\355a\010prefiere", "\007perdido", 
+ "\005cosas\005celia\005quede", "\011cualquier", 0, 0, "\010prefiero", 0, 
+ "\005nadie\007censura", "\006quedan\006legajo\013consultarlo", 
+ "\005legal\007sucedi\363\005regar", 0, 0, 0, 0, 0, 0, 
+ "\011divertido\006quiz\341s\007mezclas\010registr\363\010encendi\363", 
+ "\014inform\341ticas\013encontramos", 0, "\012revelaci\363n\014analizaremos", 
+ "\010enviarlo\012traducci\363n\011reducidos", "\003pan", 
+ "\007seguros\007ratones\005pasas\012licenciado", 
+ "\013corporativo\011rotativas", 0, "\013escucharnos", "\005pases\010enviarle", 
+ "\010tr\341nsito\006repara", "\011retenci\363n\014protagonizan", 
+ "\010turistas", "\007abordar", "\006f\351cula\007tomaste", 
+ "\014evolucionado\007sudeste", "\010hormonas", "\013estimativos", 0, 
+ "\005pasos", 0, "\005sanar", 0, "\005dance\005salio", "\005basta", 
+ "\010bancaria", "\011cristiana", "\011diferente\005salmo", "\004dar\341", 
+ "\006m\341ximo\010trinidad\011funcionan", 0, "\012telef\363nica", 0, 0, 
+ "\010rebeldes\005salto", "\007aprueba", "\005salvo", 
+ "\010calafate\007barrica", "\004piel", "\007exactas\007delante", "\006cocido", 
+ 0, "\011econ\363mica", "\003fax\007enormes", "\010cardenal", 
+ "\007llamada\004pies", 0, 0, 0, "\013actualizado", 0, 
+ "\006cambio\016peri\363dicamente", "\013remplazando", "\015tradicionales", 
+ "\010pelearon", 0, "\013consultoras\010sombrita", 
+ "\013fraudulento\007daremos\013excepcional", 
+ "\005f\341cil\006siguen\010buscamos", "\003das\010concreta", 0, 
+ "\012realizando", 0, "\013residencial", "\012torpemente", 0, 0, 
+ "\012fragmentos", "\006m\341laga", 0, 0, 0, "\007recibi\363\012tur\355sticos", 
+ "\005suyos", "\005costa", "\010italiano", "\013diferencial\006jungla", 
+ "\011alcanzado", 0, "\003reo", 0, 0, "\005ayudo\011detenci\363n\007reverte", 
+ "\011descargan", 0, "\010italiana\005dumas", "\014transiciones\004dije", 
+ "\013considerado", 0, 0, 0, "\007turista\011golosinas", "\012inscriptos", 
+ "\012divertirse\007adictos", 0, "\014participando", "\004dijo", 0, 
+ "\007juvenil", 0, "\006usadas\012recuperado\012combinando", 
+ "\012contadores\011hist\363rica\010alabanza", 0, "\012recomendar", 
+ "\006filtro\006ahorra", 0, 0, 0, "\011parlantes\010alianzas\005roble", 
+ "\007poderes", "\005magia", 
+ "\004bien\012cachalotes\010continua\012genealog\355a", 
+ "\011delantera\006revela\005rejas", "\010calific\363", "\013r\341pidamente", 
+ "\006medida", "\012construida", "\006eramos\010continuo\014satisfacci\363n", 
+ 0, "\006apagar\005matar\005latas", "\013supervisi\363n", "\014corporativos", 
+ "\013comerciante", "\012importante\014protagonista\007lectivo", 
+ "\010reclutar\006vicios", "\011programar", 0, "\005patio", 
+ "\010queridos\012encontrara\006mandas", "\012utilidades\007existir", 0, 
+ "\013inscribirse\013pronunciado", "\006divino\013particiones", 0, 
+ "\012conflictos", 0, "\007muertes", 0, 0, "\007suceder", 0, 0, 0, 0, 
+ "\006verano\006cantos", "\013recipientes\012accidentes", 0, 
+ "\004lado\016indispensables\012demag\363gico", 0, 0, 
+ "\005colon\011esc\341ndalo", 0, "\006cantar", "\005ayuda\005seria\006griega", 
+ 0, "\005oliva\013prestigiosa\007anuales", 0, "\005salas\006plasma", 
+ "\014actualizados\012degenerare", "\010sagrados", 0, "\013conferencia", 0, 
+ "\007octubre", 0, 0, "\010fan\341tico\011concierto", "\006claras", 0, 
+ "\011ocurriera", 0, 0, "\010realidad", 0, "\006madres", 0, "\006afecta", 
+ "\006masiva", "\013optimizador", "\012entregar\341n", "\010organiza", 
+ "\007autores\020correspondientes\006posean", 0, 0, "\010calvario", 0, 
+ "\010removido\004gira", "\011obtenidos\007alianza", "\006sigui\363", 0, 0, 
+ "\005l\341ser", 0, "\011omisiones", "\007crucial\006dichos\010rentadas", 0, 0, 
+ "\006active", "\015promocionarte", 
+ "\014exposiciones\010recordar\013apartamento", 
+ "\004giro\010removida\014considerados", "\005arroz", "\014aseguradoras", 0, 
+ "\012continente", "\010recupera", "\004cosa", "\011envueltas", 0, 
+ "\020establecimientos", "\011herb\341ceas", 
+ "\007debates\007tocador\006helada\005elige", "\017funcionalidades", 0, 
+ "\005elije", 0, "\012comunicado", "\005d\363nde\011escenario", 0, 
+ "\003le\355", "\006anillo", 0, 0, 0, "\012innovadora\010acumulan", 0, 
+ "\010mar\355timo", "\011dise\361adas\010ancianos", 0, 0, 0, "\012intentando", 
+ 0, "\007algunos\013eliminaci\363n\006tablas", "\006laurel", 0, 
+ "\010vacantes\012configurar", "\014comerciantes\007estar\355a", 0, 0, 
+ "\010perfecto\006puedas", "\014conocimiento\007querido", "\005opera", 
+ "\010interese", "\010gobierno\005cotes", 0, "\010m\341quinas", "\010interesa", 
+ "\006sabr\341s", 0, 0, 0, "\010acumular", 0, "\014artificiales\010calzados", 
+ "\005paran\010entonces", "\007propias", 0, "\006cancha", "\005mares", 
+ "\007rogamos\011disculpen\007carguen", 0, 0, 0, "\010pr\363ximos", "\004tipo", 
+ 0, 0, "\006surgir", "\012prohibidas\007l\355mites", 0, "\013eficazmente", 
+ "\006metros\007sagrado\006ritual", 0, "\007cierran", "\014optimizadora", 
+ "\007acuerdo\014conferencias\015personalizada\006verter", 0, "\003soy", 
+ "\013entrecalles", "\010noticias\011apicultor", "\010er\363ticos\010entraron", 
+ "\005col\363n\004tuco", "\006indica", "\006copete\012utilizadas", 
+ "\005ser\355a", 0, 0, "\012beneficios\012fortalecer", "\012postulados", 
+ "\017correspondiente", 0, "\017establecimiento\007espesor", "\010encierra", 
+ "\007tejedor", "\004esto", "\004sera", "\011muchachos", 
+ "\013infidelidad\012definieron", 0, 0, 0, 0, "\003vez\012alcanzando", 0, 
+ "\006revel\363", "\012federaci\363n\012utilizaras\013importunado", 
+ "\017estandarizaci\363n", 0, "\007cirug\355a", "\007reciban", 0, 
+ "\012argentinas", "\007ingrese\007famosas", 0, "\012alquilamos\007vacante", 
+ "\010fomentar", "\005chico\005sigan", 0, 0, 0, 
+ "\010agujeros\006buenas\011estrictas\006pienso\007valioso", 0, 
+ "\012ingresaron", "\011gobiernos", "\012elaborados\015racionalmente", 
+ "\014recomendamos\006piscos", "\010practica", "\005chino", 0, 
+ "\011similares\010tr\341mites", "\015meteorol\363gica", 
+ "\013capacidades\007suaviza", 0, "\007trabaje", 0, "\014incorporamos", 
+ "\010troyanos", 0, "\005deuda", 0, "\011juguetean\007piensan", 
+ "\007dise\361an", "\013universidad", "\010jur\355dico\012vespertina", 0, 
+ "\012preparamos", "\010probadas", "\004arte", 
+ "\010especial\016espectaculares\010probable", 0, 
+ "\012extranjero\006cursor\010cantando\006empez\363", 0, 
+ "\010espacial\010paciente\012meritorias", "\007pensi\363n\010jur\355dica", 
+ "\014enfermedades\004rato", "\010prefer\355s", 
+ "\007m\341quina\010buscados\005guiar", "\011legumbres\010buscador\005altas", 
+ "\007noticia\006fondos", "\005menos", "\013declaraci\363n", 0, 
+ "\010mamarias\012pontificia", 0, 0, 0, 
+ "\007calzado\012identifica\010p\351rdidas\007aptitud", "\011realizado", 
+ "\006vuelta\012encantados\016consentimiento", "\010difundir", "\011optimizar", 
+ "\005altos", "\007pr\363ximo", 0, "\006hechas", "\005coral", 0, 
+ "\011alfombras\012recetarios", "\006salido", "\016felicitaciones", 
+ "\012observando", "\005jorge", "\012argentinos\010revistas", 
+ "\011prote\355nas", 0, "\010activado", "\010judicial", "\007emporio", 
+ "\005ataca\011rescatado\010prometi\363", 0, "\006saludo\007m\355nimos", 0, 
+ "\012grabadoras\007sortear", "\011proveedor", "\011duod\351cima\010circulan", 
+ "\005comen", "\010podremos\006hambre\005parar", "\011disculpar", 
+ "\010circular\014presentarles\015penitenciario", 0, "\004sino", 
+ "\010discurso", "\005carga\010llevar\355a\007enfocar", 0, "\007intacto", 0, 0, 
+ "\012preparadas\007perejil\012mecanismos\011reguladas", 
+ "\004est\341\007tr\341mite", "\010modernos", "\011baj\341rsela", 
+ "\004data\007capture\010reciclar\010dominico", "\013lubricantes\007rivales", 
+ "\006pistas\012transici\363n", "\003qu\351\005fundo", "\005carta\007horaria", 
+ "\004est\351\012instalando\011finalidad\005cenas", 0, 0, 0, 
+ "\013combinables\007chequeo\005libra\011emigrante\005tened", 
+ "\006hablan\005marzo", "\013inversiones\005clero", 
+ "\004ser\341\006habana\006rebate\010acostado\006benito", 0, "\004dato", 0, 
+ "\007probada", 0, 0, "\011balneario", "\004ser\351", 
+ "\006motivo\012obtuvieron", "\012econ\363micos\010violento", 
+ "\006unidad\006dentro\011bienestar\012encontrado\013inform\341tico\006noveno", 
+ 0, "\007hacerse", "\010marcador\006rebote\011jubilados", "\007inicial", 
+ "\007buscado", "\003ata", 0, "\005pilas\005graba", 
+ "\007p\351rdida\010subastas\011gabardina\014supervisores\004unen\007tomarme", 
+ 0, "\005traer", "\010bailando\005moral", "\007troyano\013ilustrativo", 0, 0, 
+ "\006muchos\006az\372car", "\012transmisor\006arropa", 0, "\010anterior", 0, 
+ 0, "\005media", "\010conforme", "\010animando", "\005grasa\015especialistas", 
+ "\005token\010obesidad", "\007revista\012temporales", 0, "\005pausa", 
+ "\006invite\006cajita", 0, 0, "\011principio", 
+ "\012renovaci\363n\012distribuir", 0, "\013generosidad", 0, "\007r\351gimen", 
+ 0, 0, "\006juncal\010medianas", 0, "\011concluir\341", 0, 0, 
+ "\006t\363pico\011tutelados", "\007metidos", 0, 0, "\006listos", 0, 
+ "\007pagar\355a", "\014contrataci\363n", 0, "\012pectorales", "\004viva", 0, 
+ "\004lean", "\006prever", "\010talleres\004vive\012misioneros", 
+ "\011meriendan", "\015responderemos", "\004leas", 
+ "\006social\012materiales\005men\372s", "\012escritores", "\010cre\355bles", 
+ 0, "\010criminal\006barcos", "\007rurales\007decirte", 
+ "\004vivo\013rendimiento", 0, "\005gente\006largas", 
+ "\007aceptan\011ciudadano", 0, "\010s\355ntomas", "\005masas\005pasan", 
+ "\014inform\341ticos", "\006flotas", "\011retiraron\007influjo", 
+ "\013instalarlas\007canjear\014delincuencia", 0, 
+ "\006arriba\007razones\014recordatorio\014pr\363ximamente", "\006barros", 
+ "\011optimismo", "\007marcado", "\007segunda", 
+ "\007h\341bitos\012lapidaci\363n\010albergue", "\007subasta", 
+ "\006llegar\006restos\012solemnidad", 0, "\006carece", 0, 
+ "\015empresariales\011ocasiones", "\010comodoro\007sestean", 0, 
+ "\010diversas\005panel\006llenar", 0, 0, "\013solidaridad\011fen\363menos", 0, 
+ 0, "\007ocurren", "\011soberan\355a", 
+ "\011funciones\006llevar\012superficie\006tensos\006llenos", "\005atlas", 
+ "\007cocinan\007israel\355", "\017incumplimientos", "\010inspirar", 
+ "\010nuestros\012musulmanes", "\005com\372n\006fletes", 0, 
+ "\010proclama\006pensar\013hamburguesa", "\004cara\011remolacha", 
+ "\012venezolano", "\006vuelve", "\006hab\355an", "\012folcl\363rica", 
+ "\011restantes", "\013publicaci\363n", "\003faz\007mediana", 0, 0, 
+ "\011mam\355feros\007entreg\363\011dispersas\007esposos", "\007niveles", 0, 0, 
+ "\006prueba\013sustantivas", "\013gestionamos", 0, "\006pareja", 0, 
+ "\011efectuado", 0, 0, "\007siempre", "\012secretaria\006env\355en", 0, 
+ "\006cumbre\006virrey", "\010religi\363n", 0, 0, "\004juno", 0, 0, 
+ "\007iguales\012correcci\363n", "\004taza\016misericordioso\011registran", 
+ "\005casas", "\011encuentre\005sopas", "\007estival", 0, 
+ "\003eje\012calificado\011vitalidad\005cases", "\011despertar", 0, 0, 
+ "\013creatividad", 0, "\007p\341ginas\006temple\012disparador", 
+ "\011comunidad\013obligatorio", "\013sacrificios", "\011revelarse", 
+ "\011consultar\005casos\004huir", "\010saturada", "\010perfiles", 0, 
+ "\010pretende", 0, 0, 0, "\007reporte", "\006visita\007contada", 
+ "\004gran\011consultor", 0, 0, 0, "\010pretendo", "\012tabaquismo", 0, 
+ "\004papa", 0, 0, 0, "\003a\372n\007barrera", 0, 
+ "\007pedimos\011acad\351mica\005vodka", 0, "\012testimonio\010pidieron", 0, 0, 
+ "\011condici\363n\014hamburguesas", "\007solemne\011ecum\351nica", 
+ "\011demostrar", "\006m\341xima", 0, "\007record\363\012bolivianos", 
+ "\007comedor\007caprino", "\004viv\355\006cometa", "\011atributos", 0, 0, 
+ "\007enigmas", 0, "\007figuras\006viejos", "\011efectuada", 
+ "\004cena\007esperen", "\013celebraci\363n\005vuele", "\011inspirado", 
+ "\006pastor\006amparo", "\005ahora\010concurso", 0, 
+ "\012ambiciones\006muerto", 0, 0, 0, "\006cambia", 0, 
+ "\006desees\014preservaci\363n\013diagn\363stico", "\012recorridos", 
+ "\005pasar", "\007futuras\012tendr\355amos", 0, "\006domino", 
+ "\013situaciones\006corran", "\006invit\363", "\007nuestro\011trabajaba", 0, 
+ 0, 0, "\013ingrediente", "\004sube\006captar", 
+ "\010ejecutar\015enciclopedias", 0, "\005bicho", "\011invisible", 
+ "\005nutre\005sanas", 0, "\006gustos\011consorcio", 
+ "\005andar\007derrita\007parezca", 0, "\016comprobaciones", 0, 0, 
+ "\013informarles\007compete", "\014veterinarias\007preside", 
+ "\007produjo\012balanceado", 0, "\007maquina\012ideol\363gico", 
+ "\010anunciar\014especialista\010estrella\014intensifique", "\005sanos", 
+ "\007logrado", "\011champi\361\363n", 0, "\011permanece", 0, 
+ "\005amada\010se\361orial\011relaciona", "\010catalogo\014preocupaci\363n", 0, 
+ "\007hoguera\016extraordinaria", 0, "\013tonificando\006avisos", 0, 0, 0, 
+ "\007extreme\007galaxia", "\006barrio", "\012hist\363ricos", 
+ "\012disc\355pulos\004ejes", "\006llaman\013villancicos\011vaticanos", 
+ "\005virus\011vol\372menes\005rosas", 0, 0, "\005juego", "\005roses", 0, 
+ "\011tormentas\011represiva", "\012superaci\363n", 
+ "\005usted\006rubros\015reconfortante", "\005qued\363", "\015incomparables", 
+ 0, 0, 0, 0, "\004pelo\011tendencia", 0, "\006c\363digo\006nieves", 
+ "\010aleda\361os", "\005da\361an", "\010personas\010apuestas", 
+ "\007mundial\013prosperidad", 0, "\011planifica\007abdomen", "\010intentan", 
+ 0, "\010tuvieras", 0, "\010carrasco", 0, "\010quincena", 0, "\011amistades", 
+ "\006numero", "\012informando\012definitivo", 
+ "\011objetivos\006quiera\012almacenado", 
+ "\006litros\005perro\012compartido\011manzanero\010intentar\014revendedores\012reflejados", 
+ "\003ama\014ingredientes", "\004pap\341", 0, "\011uruguayos\007sostuvo", 
+ "\011necesario", "\012realizaron", "\010singular", 
+ "\012existencia\010rechazar", 0, "\007abrirlo", "\011indonesia", "\006padres", 
+ 0, 0, "\003fue\010personal\011impuestos\011desayunos", 0, 
+ "\005\341reas\010formando\014nacionalidad\012impositivo", "\013desconocido", 
+ 0, "\014aprovechando\011participe", 0, 0, "\005sobre", "\004tira", 
+ "\007anuncia", "\014singularidad\012comparamos\006avisas", 0, 0, 0, 
+ "\012itinerante", "\013desbloquear", 0, "\011ense\361anza", "\007modules", 
+ "\006agreg\363", 0, "\010sentimos", "\005habla\004tiro", 0, 
+ "\004rima\011primavera", "\007demoras\010consolas", 0, 
+ "\013compromisos\006rebaj\351", 0, 0, "\011arrancado", "\017permanentemente", 
+ 0, "\005duran", 0, "\010m\355sticos", "\011recibiste", "\007c\341tedra", 
+ "\013negociaci\363n", 0, "\011senadores\011contraste", 0, 0, "\006visit\363", 
+ 0, "\012multimedia\004sub\355", "\006espero", "\004post\007apuesta", 0, 
+ "\004casa", "\007clavero", 0, "\006creci\363", "\004case\007tuviera", 
+ "\010generara", 0, "\011sorprende", "\004casi", 0, 0, 0, 
+ "\007minutes\006dirige", "\005notas\013compradores\007intenta", 
+ "\004caso\010cat\341logo", 0, "\010apartado", "\005notes", "\014electricista", 
+ "\010dirigido\005dir\355a", "\014controversia", "\010dirigida\007rechaza", 0, 
+ 0, "\006causan", 0, 0, 0, "\005pinto\006quesos\010gr\341ficas", 
+ "\010congreso\017autom\341ticamente\010sucedido\015sorprendentes", 0, 0, 
+ "\005fresa\012opcionales\013humanidades", "\013irrompibles", 
+ "\007persona\012proponemos\014comunitarios", 
+ "\005breve\011continuar\006querer\010campanas", "\012decadentes", 0, 
+ "\011considere\006cambi\363", "\012traiciones\006reson\363", 0, 0, 
+ "\005batir", "\011gen\351ricos\006hierba\010delfines", 0, 0, "\004aman", 
+ "\010c\363cteles\006sirena", 0, "\011hoteleras", "\004amar", 
+ "\004amas\007exponga", 0, 0, "\010palabras\006usando", "\011hoteleros", 
+ "\007sideral", "\011modalidad", "\006tulipa", 0, "\010clausura", 
+ "\013indicadores", "\011separador", "\007baulera\010fiambres", 0, 0, 
+ "\005bajan", "\013auriculares", "\014desconocidos\005boyas", 
+ "\006podido\011publicar\341", 0, "\017cinematogr\341fica", 0, 0, 
+ "\007nativos", 0, 0, "\004http\013democr\341tica", "\012bicicletas", 
+ "\010atentado\016bibliogr\341ficas", "\010sentirse", 0, 0, "\014enciclopedia", 
+ "\011coalici\363n\010d\341ndonos", "\010lectores", 0, "\010proteica", 0, 0, 
+ "\006quejas", "\007consola\010ubicados", 0, "\006\372nicos\010proteico", 
+ "\006olvido\014regulaciones", 0, "\006amigos\012relaciones", 
+ "\011esperanza\006consta\020fundamentalmente", 0, "\013estrat\351gica", 
+ "\012contaminan", "\010recibido\012vanguardia\006serios", 
+ "\005hab\355a\006revisa\007generar\005entr\363", "\007redonda\010recibida", 
+ "\011organizan\007terrena", 
+ "\006obispo\005natal\012conducci\363n\005pinza\004tir\363", "\007portada", 
+ "\012religiosas\006elijas", "\010moraleja", 
+ "\007digital\007gr\341fica\006cuanto", "\013administrar\011provistos", 
+ "\004lago", "\007paginas", "\006cuarto", 0, "\004idea\005abrir", 
+ "\013profundizar\005tambo", 0, "\010segundos\006fiable", 0, 0, 
+ "\011potenciar\012utilizarlo\015identificador", "\012marcadores", 0, 0, 
+ "\016familiarizarse\011concretar", 
+ "\010debilita\015habitualmente\014radioterapia", "\011empezamos", 0, 
+ "\011recuperar\013comprensi\363n", "\010novedosa", 0, "\014equipamiento", 
+ "\007palabra", "\010apreciar", "\011paradigma", 
+ "\013maravilloso\010novedoso\013mencionadas", "\007centros\013provisional", 
+ "\013potenciales", "\011depuradas", "\015profesionales", "\013obsequiamos", 
+ "\006huelga", 0, "\006previa\012reduciendo\007campana", "\010contando", 0, 0, 
+ "\014asociaciones\006cables", "\005miles\010franqueo\011abarcando\007pedirle", 
+ 0, "\013espolvorear\007se\361alan", "\016requerimientos\010emisario", 
+ "\005truco\013congregados", "\006moneda\005lindo\007exponer", 
+ "\012cervicales\007manteca", "\006montar", 0, 0, "\006ultimo\007levant\363", 
+ "\013metodolog\355a", 0, "\007m\363dulos\010concluir", 0, 0, 
+ "\006viento\005toman\006salada\005pinos\007frescos", 0, "\006amigas", 
+ "\011partiendo", 0, 0, 0, "\005siglo\011jerusal\351n", 
+ "\006salida\011disfruten", "\007ubicado\005signo", "\007borrada", 
+ "\010llegadas", 0, "\005usar\341", "\011esperados", "\014estrat\351gicas", 
+ "\014alimentaci\363n", 0, 0, 0, "\006saluda", "\010ahuyenta", 
+ "\007implica\012jardiner\355a", 0, 0, "\006ayudar\006mambr\372", 
+ "\010momentos", "\010referido\015embarcaciones\013entretenido", "\003doy", 
+ "\012exhibici\363n", "\007estilos\016interpretaci\363n", 0, 0, "\005o\355dos", 
+ 0, "\013desacuerdos", "\010partidos\005guias", 0, 
+ "\011talonario\012mercader\355a", "\013tecnol\363gica", 
+ "\007olviden\007objetos", "\012sanatorios\012afirmaci\363n", 
+ "\011comercios\013sofisticado", "\010oponemos", 0, 
+ "\012cuadradito\015congelamiento", 0, 0, 0, 0, "\015reproductores", 
+ "\004tr\355o\010arenales", 0, "\007ganaron", "\013mencionando\006montes", 
+ "\010catedral", 0, 0, 0, "\011segmentar", 0, "\015transparentes", 0, 
+ "\010logrando\007maderas", "\007segundo", "\010insulina", 
+ "\010iglesias\006pagado\013tendencioso\012ontol\363gica", "\011resultase", 
+ "\011perdieron", "\006aporta", 0, 0, "\005combo\010ofrezcan\007sonrisa", 
+ "\011digitales", "\005papel\010diversos", 0, "\007allende", "\011disculpas", 
+ 0, "\005momia\011dietarios\006dictan", "\007parches\005cesar", 
+ "\012cronograma", "\005pesca", 0, "\004olla", 0, 
+ "\013adquisici\363n\006burles", "\005darle", "\005darme\015ofrecimientos", 0, 
+ "\012prepararse", "\004cata", "\007varones", 0, 
+ "\005darse\005besos\007llegada", 
+ "\005darte\010conjunto\012agrupaci\363n\006bosque", 0, 0, 0, "\006genero", 
+ "\007oscuras", "\010conjunta", "\011normativa\006c\341rcel", "\010lenguaje", 
+ 0, "\007momento\007eventos\010detalles\013abdominales", 
+ "\006limpio\013convencidos", 0, "\010qu\355micas\004fr\355a", "\007recibas", 
+ 0, "\007sueltos", 0, "\014psiqui\341trica", "\006operar", 0, "\006chicas", 
+ "\006c\363modo", 0, 0, 0, "\006peajes\006tolvas", 
+ "\005vac\355a\005pilar\004fr\355o", 0, "\007nirvana", "\012infernales", 0, 
+ "\010quedarse", 0, 0, "\003bar\012fascinante\006garaje\011prodigios", 0, 
+ "\005olmos", "\005drama\005millo", "\007caderas", 
+ "\014introducci\363n\013audiovisual", "\013continentes", "\007cintura", 0, 
+ "\014gobernadores", "\013registrarse", 0, 0, "\011verdadera", 
+ "\013ultras\363nico", "\010completa\007iglesia\007partido", 0, 
+ "\006\351xitos", "\012legendaria\013vaporizador", 
+ "\010complete\013confortable\011incesante", 0, 0, 
+ "\007botella\005rigor\014tecnol\363gicas", "\010an\341lisis", 
+ "\011confusi\363n", 0, "\015perseverantes", "\006mismos", 
+ "\007deseada\011recibidos\010escritor\011acercaron", 
+ "\010completo\010escritos", "\011recibidas", 0, 
+ "\006cotiza\017posicionamiento\007ofrezca", 0, "\007pasillo", 
+ "\005pesos\004dual", "\006antena", 0, 0, "\005feliz", 0, "\007civiles", 
+ "\005sello", "\005anche\010surtidas\010servirle\011normativo", "\005pulse", 
+ "\013patriarcado", 0, "\007pierdas\013propiedades", 0, 
+ "\007incluir\007bi\363logo", 0, 0, "\006marcas\013presentando", 
+ "\010impresos\010carreras\010resistir\011enc\355clica", 
+ "\007detalle\006mareas", "\010contamos", "\006cargos", "\007qu\355mica", 0, 0, 
+ "\004ni\361a", "\011pesimista", 0, "\010firmamos\011emociones\005hac\355a", 
+ "\006cargar", "\012fotograf\355a", "\006f\355sico", "\010divertir", 
+ "\006v\355deos\007disputa", 0, 
+ "\011comienzan\014instaladores\011proveer\341n", "\006s\363lida", 
+ "\013consultores\007estando", "\011ocasionar\006elegir\011saquearon", 
+ "\004ni\361o\007rechaz\363", "\013inscripci\363n\004trae\007ejercer", 
+ "\007calcula", "\012fant\341stico", "\011aceptamos\005copi\351", 0, 
+ "\010reformas\010habiendo", "\006etapas", "\010incluir\341\005tango", 
+ "\005mauro", "\005causa", "\007sentido", "\011averiguar", 0, "\012quinientos", 
+ "\011ajustar\341n", 0, "\010videntes", 0, "\010misiones", 
+ "\010contrata\007sugiere\014sensibilidad", 
+ "\005tanto\013continuamos\012localizada\013aconsejable\011creadores\006crecer", 
+ "\011zanahoria", 0, "\006ning\372n\011artilugio", 0, "\010contrato", 
+ "\006pelado", 0, "\010grupales\010canarias", "\013popularidad", 
+ "\012comprender\007po\351tica", "\004momo", 0, 0, 
+ "\011sanitaria\014equiparaci\363n", 0, 0, 0, "\007orgullo", 
+ "\010viajando\005grito\005pozos", "\010maltrato", "\007d\363lares", 
+ "\004pena", "\007tierras", "\012linotipias\007enfermo", 
+ "\006ahorre\010derivado", "\004pene", "\005dados", 
+ "\010tuvieron\007ilusi\363n", "\010ingresar\012destinadas", 
+ "\004o\355do\006dignos", "\006agrade", 0, "\004guia", "\012secretar\355a", 0, 
+ "\006atacar", 0, 0, "\007manejes", "\013simplemente\015entrenamiento", 0, 0, 
+ "\005deben\010entiende", "\005meter", "\005honor", 0, 
+ "\007escrito\011envasados", 0, 0, "\006libres\012libremente", "\005subi\363", 
+ "\011ejercicio", 
+ "\010integral\007sorteos\010universo\010promotor\012emergentes", "\005banco", 
+ "\010integran", "\014precisamente\014sentimientos", "\010ingresan", 
+ "\004all\341\010isl\341mica", 0, 0, 0, 0, 0, "\010prometen\007subidas", 0, 
+ "\005canon\007volando\010devuelva", "\010integrar", "\007reforma", 0, 
+ "\005aires\004all\355", "\007carrera\007impreso", 
+ "\013medicamento\012accesibles", "\007cartera\013comunitaria", "\005siria", 0, 
+ "\007explic\363", "\010quintana\006relato\005lanza", "\013desintoxica", 
+ "\011subrayado", 0, "\006editor\004tita", 0, "\012detallados", 0, 
+ "\007ninguna\007arroyos\004roza", 0, "\011nutrici\363n\005sirva\005firm\351", 
+ "\006triste\015temporalmente", 0, "\013presentamos\012detallamos\010esclavos", 
+ "\013encontraran\010adivinar\010ejemplos", 0, "\007detener\011prop\363sito", 
+ "\010irland\351s", "\004tito\006mar\355as", "\005suena\007votaron", 
+ "\010embajada\012aprender\341s", 0, "\013pasatiempos", "\014irreversible", 
+ "\012eficiencia", "\013descargarte\007autoras", "\011encendida", 
+ "\015correctamente", "\013subscriptor", "\015transpiraci\363n", 
+ "\012valenciana\010estatuto", "\011impresora", 0, "\011entidades", 
+ "\005betas\010quedando\012sedimentos", "\006lengua", 
+ "\011mayorista\007muestro", "\010comprime\011descargar", "\011recuerdas", 
+ "\010notables", "\007dirigi\363", 0, "\005da\361os\010subsuelo", 0, 0, 
+ "\010lanzamos", 0, "\007validez\011abdominal", "\005ciclo", 
+ "\011velocidad\013divertirnos", "\007mensual", 0, "\006encima", 
+ "\005andes\013comprobante\010creyendo", 0, 0, "\007fortuna", 
+ "\010caliente\010provecho", "\006dormir", "\007decides\006moraga", 
+ "\012mencionado\007inglesa", "\007ocasi\363n\011sensibles", 0, 0, 
+ "\006juntos", 0, "\012presidente\007ingresa\007folleto\015contempor\341nea", 
+ "\003muy\005llame\007avalado", "\012escribanos\007integra", 0, 
+ "\012proyecci\363n", "\010equipada\011responden", "\011apadrinar", 
+ "\011misionera\011gacetilla", 0, 0, 0, 0, 0, "\014comunitarias", 
+ "\007apoyaba\010cercan\355a", 0, "\010marginar", 
+ "\012protecci\363n\007comenz\363", "\006tama\361o", 
+ "\013funcionario\013exactamente", "\007esperar", 0, 0, 0, 
+ "\010publicar\007esclavo\012fundamento\007m\341gicos", 
+ "\012periodismo\010publicas", "\006abarca\004iris", "\007ejemplo", 
+ "\006turbio", "\010creativa", 0, "\012tur\355sticas\004mega", 
+ "\012entrevista", "\011militares\006lech\363n", "\011atracci\363n", 
+ "\010directos", "\010director\011boletines\016coleccionistas\011beneficia", 
+ "\011comprueba", 0, "\012disciplina", 0, "\011mi\351rcoles", 0, 
+ "\004dios\004alma\013fabricaci\363n", 
+ "\004pasa\012confiables\014medicamentos\011atrapados", 
+ "\014desintoxican\005viajo", 0, 0, "\005caldo", "\010especies", 0, 0, 
+ "\017extraordinarios\007notable", "\011integrada", 0, "\011misterios", 0, 0, 
+ "\004paso", "\010doctrina", "\010obtenida", 0, 0, 0, "\011bancarias\005malta", 
+ 0, "\014constituci\363n", "\013previamente\006grados", "\012visitar\355as", 0, 
+ "\010ubicadas", "\007p\341jaros\011posterior\011calientes\005novia", 
+ "\005envi\363", "\005pobre", "\006dedica\010pioneras\014sometimiento", 
+ "\006cartel\011etiquetas\016cuantificaci\363n\006m\355nima\012contestar\341", 
+ "\005mayor", "\006granos", "\010empeorar\011aprendido", 0, 
+ "\007se\361ales\012personajes", "\013licenciados", 
+ "\006grasos\014configurable", 0, "\005naval", "\010erotismo", 
+ "\012personales\007navegar\006mu\361eca", "\006cierta", "\003oso", 0, 
+ "\006luchan", "\014comunicaci\363n\015contaminantes\005otero", 0, 0, 0, 0, 
+ "\006dibujo", "\007estr\355as\012posibilita\007matinal", 
+ "\010haciendo\005pavor", "\003mal", "\006hostal\010hacienda", 0, 
+ "\007decidi\363\007publica", "\015suscripciones", "\017definitivamente", 
+ "\010ratific\363", "\007casetes\007abrirte", 
+ "\011almacenes\013telef\363nicas", 0, "\010cl\355nicas\010vigentes", 
+ "\004mona\012vigilancia", "\007colores\013permanentes", 
+ "\005royal\011idoneidad", "\011mensuales\006ballet", 
+ "\007directo\012financiera\012protegidas", "\004basa", "\011franceses", 
+ "\010vigencia", "\012fisiolog\355a", "\004base\010cuesti\363n\007piensen", 
+ "\012preventiva", "\004unid", "\006esposo", "\006regida", 
+ "\004mono\011integrado", 0, "\006cubana", "\011conservar\006amasar", 
+ "\005notar\007especie\012multicolor\005logre", 
+ "\011afiliados\006piloto\005bread", "\007pericos\014funcionarios", 
+ "\005crece", 0, "\012concepci\363n\011renovable\004reto", "\011operativo", 
+ "\004unir", 0, "\012inexorable\010disfrute", 0, 
+ "\014semanalmente\011hip\363tesis", 0, "\010disfruta", 
+ "\011compilada\005novio", "\014expectativas\007pagando", "\010corredor", 
+ "\015bibliogr\341fico", "\010pensadas\006vienen", 0, "\004apio", 
+ "\011exigencia", "\010rosarios", "\010memorias", 
+ "\006sonido\015institucional\014habitaciones\004peor\012importadas", 
+ "\007ubicada", "\010calor\355as", "\011siguiente\011instituto", 
+ "\005lim\363n", 0, "\006mierda\012encontraba", 0, 0, "\011eficiente", 0, 
+ "\010colegios\007incluso\011separadas\012percepci\363n", 
+ "\005lucro\010hojaldre\017manifestaciones", 0, "\006floppy\015colonizadores", 
+ "\013practicante", 0, "\005sepan", "\007triples", "\007pobreza\010opcional", 
+ "\006primer", 0, "\011comercial", 0, 0, 
+ "\016inmediatamente\005cerca\011separados\012resultar\355a\011reparador", 
+ "\007finales\011opiniones", 0, "\004leen", "\006podr\355a\010m\372ltiple", 
+ "\007cuantas", "\011intensivo\010cortadas", "\010pantalla\004leer", 
+ "\006pagues\007\351nfasis\014ordenamiento", "\005merlo", 
+ "\014inigualables\013identificar", "\007vigente", "\012esenciales", 
+ "\007cl\355nica\004pas\363", 0, "\006fuerza\006huerta", 
+ "\012socialismo\014ampliaciones", 0, 0, "\004mala", 
+ "\012imperdible\011patolog\355a", "\010honduras", "\005cines\010repartir", 
+ "\006hablar", "\007quieren\010requerir", "\014interpretada\007cosecha", 
+ "\016artesanalmente", "\006remoto", "\005jefes", "\010aplausos", 0, 
+ "\013descargarlo", 0, "\006bebida\010ayudante\004malo", "\013enfrentarse", 
+ "\014instructores", "\005sufra\010situarse", "\011americana", 
+ "\006cristo\010id\351ntica", "\007chascos", 
+ "\007mejores\006jueves\006nuevas\010oportuna", "\006futura\006tercer", 
+ "\007tendr\341s\006citado\007vistazo", "\005ratas\007cocci\363n", 0, 0, 0, 0, 
+ 0, 0, 0, "\007cocinar\012prohibidos", "\007rosario", 0, 
+ "\006fallos\011imposible", "\007alcalde", 
+ "\016independientes\016enjuiciamiento\010cultivar", "\007memoria", 0, 0, 
+ "\010legumbre", "\012reservados", "\006otorga\004cave", "\011concretas", 0, 
+ "\007colegio", "\006invita", "\011mantienen", 0, 0, 0, 0, "\010obligado", 
+ "\010indicado\011concretos\011inauditas", 
+ "\010obligada\010agilidad\007citando", "\006acorde\012sociedades", 0, 
+ "\014concerniente", "\006pintor", "\006escape\010recoleta", 0, 0, 
+ "\012seduciendo\007tablero", 0, "\013conversores\007pensada", 0, "\004hizo", 
+ "\005horas", "\007c\355rculo", 0, "\006teclas\007oleadas\006dedic\363", 
+ "\014simplificada", "\007aplicar", "\012finalmente", 
+ "\011presentan\015potenci\363metro", "\006m\351dico\007acentos", 0, 0, 0, 
+ "\007esperas\006rizoma", "\011variables\011electoral\005torno", 
+ "\016administrarlos", 0, "\010instalar\011graduados", 0, 0, 
+ "\007ganaran\010disfrut\363", "\004raya", "\005cinta\011pronunci\363", 
+ "\013considerada\015consecuencias", 0, "\006nuevos", 
+ "\005pesan\012llanamente\005mesas", "\013respaldadas", "\010elegidas", 
+ "\011sarmiento\007cortada", "\005meses", "\007cambien", "\013atentamente", 
+ "\007crearlo", "\010tratando", "\007electro", 0, 
+ "\012nutrientes\013innovadoras", 0, "\007estamos\015equivalencias", 0, 
+ "\003m\355a\013desarrollar\013estad\355stica", "\005penal", 0, 
+ "\012conversi\363n", "\011visitaron", 0, "\012asegurarme", "\006brecha", 0, 0, 
+ "\004pato", 0, "\005campo", 0, "\004uvas", 0, "\012excelentes\013gerenciales", 
+ 0, 0, "\011musicales", "\010andaluza", "\011plantilla", 
+ "\010problema\015exportaciones\010mercurio", 
+ "\010comienzo\010entender\012refrigerio", "\006vuelva", "\010comienza", 
+ "\011indicados\006cuenca", 0, "\011indicadas\005lazos\005pens\351", 
+ "\007parador\010cociente\007posadas\007pasados", "\006fechas\006hechos", 
+ "\011creciendo", "\007musical", "\011tem\341ticas\012discreci\363n", 
+ "\005bordo", 0, 
+ "\004cero\007decidan\005crudo\006pruebe\013entusiastas\012saludables", 0, 0, 
+ "\007sientes", 0, "\010falleci\363", 0, "\010articulo", "\006aurora", 0, 
+ "\005quiz\341\005vaci\363\012producidas", 0, "\013arquitectos", 
+ "\011determina", "\007casadas", "\012carpintero", 
+ "\011extender\341\011org\341nicas", "\012navegaci\363n\010inscribe", 0, 
+ "\007so\361ando", 0, 0, "\012comentamos\006trozos", "\005sexta", 
+ "\012comisiones", "\011unig\351nito", "\011caracoles", 0, 0, 
+ "\013electr\363nica\013ventilaci\363n", "\005vivas", 
+ "\007limites\012escalables", "\014personalizar", "\006lindas", 
+ "\011atendemos\006lineas\005vives", "\012igualmente", "\014personalizan", 0, 
+ "\007ecuador", "\017investigaciones", "\010marciano", 0, "\004mide", 
+ "\006visite\006llegan", "\005zenit", "\014estad\355sticas\007extrema", 0, 
+ "\011apostaste", "\010poderosa", 0, "\006r\341pido\007instala\012relevantes", 
+ "\014servilletero\007lanzado", 0, 
+ "\010festejar\012econ\363micas\013solicitudes", "\010poderoso", 
+ "\004pepe\010ocuparon", 0, "\006varios", "\006llevan", 
+ "\010ingresos\014consideradas\007cerrar\341", 0, "\006patria", 0, "\005mudan", 
+ "\007basados", "\012nutritivas", "\010dividido\006fideos", 
+ "\020innecesariamente\011panader\355a", "\010dividida", "\011requisito", 
+ "\006ayuden", "\015representante", "\007pasivos", "\007cumplen\004echa", 0, 
+ "\012municiones", 0, "\010listados\011abandonar", "\011ubicaci\363n", 
+ "\011audiencia", "\006vestir", "\004cine\007hermoso", 0, "\010enfrente", 
+ "\012asistentes\013reconocemos", "\011subtitulo", "\007injerto\006a\351reos", 
+ "\003ame\006cambie", "\016espiritualidad", "\006alguna", "\011toneladas", 
+ "\007incluya\005pesar", "\011asociadas\011oprimidos", 0, 0, 
+ "\015suministrador", 0, "\011reciclado\011otorgando", "\006poes\355a", 0, 0, 
+ "\003fui\011instalada", "\006nativo\011asociados", 0, 0, "\004cual", 0, 0, 0, 
+ 0, "\016transportadora\007public\363\007hogares", "\015proposiciones", 
+ "\006propio\004mama", 0, 0, "\003era\010acordado\011aptitudes", 0, 
+ "\010maderera", "\006\372tiles", 0, 0, "\014distribuci\363n\012negociador", 
+ "\011paradores\006receta", "\012sugerencia", "\006factor", 0, 
+ "\012lastimarse\012comprobar\341", "\007parec\355a", 
+ "\012comprimida\011poderosas", 0, 0, "\010art\355culo\007\341ngeles", 0, 
+ "\005debas\006darnos", "\011poderosos", "\013entrecruzan", "\010permitas", 
+ "\005debes", 0, 0, 0, "\013inseguridad", "\012operadores\014electr\363nicas", 
+ "\010afrontar", 0, "\006diario\007poderos", 0, 0, 
+ "\006estero\014nacionalismo", 0, "\013impresiones\013satisfechos", 0, 
+ "\012construido\010aparatos", 0, "\005cielo", 0, 
+ "\007r\355gidos\016circunstancias", "\006a\351reas", 0, "\007molesto", 
+ "\005mirar\006cobras\014ofreci\351ndole", "\004olor", "\010recursos", 0, 
+ "\007sujetos\007invento", "\011completar", 0, 
+ "\012patrimonio\017descomprimirlos\011descansar", 
+ "\011alentarlo\011reciclada", "\004sexo\005tries\005pu\361os", 0, 0, 
+ "\007ingreso\011embarcada\012funcionar\341", "\012occidental\012individuas", 
+ 0, "\015programadores\007ganador\014finalizaci\363n\012analizamos", 
+ "\004lomo", "\006millas\010derrotar", "\011soldadura", "\013dormitorios", 
+ "\005puedo", "\011encuestas", "\011bals\341mico", 
+ "\012psic\363logos\005bello", 0, 0, "\007listado\012ovacionado", 
+ "\014desarrollar\341", "\004le\363n\012canjeables\017caracterizaci\363n", 
+ "\005ardid", 0, "\012vinculadas", "\007recib\355s", "\006actual\011secuencia", 
+ "\011pacientes\010impronta", 0, 0, 0, "\010peque\361as\013explicaci\363n", 0, 
+ "\006creado\004raza\011meramente", "\013pulsaciones\010doctores", 0, 
+ "\010conducta", "\012diab\363licos", "\016especializadas", "\006cumple", 
+ "\006flecha\010lograrlo\014matriculados", "\011problemas\010estancia", 0, 
+ "\011facilidad", "\011art\355culos", "\012prisionero", 0, "\004duda", 
+ "\007permita", 
+ "\005vicio\014involuntario\012peri\363dicas\014aerodin\341mica\005cubra", 
+ "\007actores", "\011minerales", 0, "\005manda", "\010consigna", 0, 
+ "\006estilo\005manga", "\012enfermedad", "\004eran\007afronta", 0, 
+ "\013circulaci\363n", "\011comedores\010valiente\013abandonando", 
+ "\010polic\355as", 0, "\010defectos", "\007validos", 0, 
+ "\010efectiva\014criptograf\355a\011supuestos", "\005canso", 
+ "\011resueltos\005canto\010f\341bricas\005manta", "\010empiecen", 
+ "\011confiaron\011acad\351mico", 0, "\004mam\341\010\355ntegras", 0, 
+ "\007relleno\007criollo", 
+ "\011discursos\010aconsejo\013reflexiones\013arzobispado", 
+ "\010efectivo\012individuos", 0, 0, "\006mezcla\012autom\341tico", 
+ "\005sit\372a", "\006seguir\011grabaci\363n", "\007endorse\012centenario", 
+ "\005pueda\012siniestros", "\010cl\341sicos", "\007felices\011explosivo", 
+ "\015distribuidora\011patriarca", "\014publicidades\006pasaje", 0, 0, 
+ "\013respetuosos", "\010secretos\007lateral\007calibre", 
+ "\006famoso\006cuento\004sito", 0, "\007comedia\007embargo", 0, 
+ "\007tenerla\005\351stas\006vieras", "\007aparato\013dependiendo", 
+ "\005madre\011inmediata\006causar", "\007todav\355a\016especializamos", 0, 0, 
+ 0, "\011producir\341", "\012plataforma\011criaturas", "\007recurso", 
+ "\015recomendaci\363n\012perjuicios", 0, "\016especializados", 0, 
+ "\005\351stos", "\011descargas", "\007europea\013imprimiendo", 0, 
+ "\012hospitales", 0, 0, 0, "\012arrepentir\005dicho\012dirigirnos", 
+ "\012determinar\011pl\341sticas", 0, 0, 
+ "\015internacional\010cambiado\006gustas", "\010quisiera\013distribuido", 
+ "\012deportivos\010mencion\363", 
+ "\013televisivos\014verificaci\363n\005vallo", 0, "\010asimismo", 
+ "\007asuntos", "\011pl\341sticos", "\013certificado", "\005todas\007colegas", 
+ "\012soluciones", "\006morada", 0, 0, "\011ingeniero", 
+ "\003nos\011compresor\004miel\005urges", 0, "\010s\355ndrome", 
+ "\003r\355o\007polic\355a", 
+ "\007peque\361a\006marido\013referencial\012escenarios", 0, 
+ "\014garantizamos", "\011convertir", "\005todos\006migada\011asegurado", 
+ "\005firme", 0, "\015restricciones", 0, 0, 
+ "\007\355ntegra\010positivo\013similitudes", 
+ "\006abonan\012compilados\012obviamente", "\010positiva", 0, 
+ "\013iniciativas", "\011angostura", 
+ "\005luego\005tabla\006comida\006reuni\363", "\012documental\006trazas", 0, 0, 
+ 0, 0, "\007accesos", "\005buena\010exitosos", "\014dificultades", 0, 
+ "\007empiece", "\005fuere\005durar", "\010usuarios", 0, 0, 0, 0, 
+ "\005surge\012contaminar", "\010terminal", 0, "\010terminan", 0, "\004zona", 
+ "\011dise\361amos", 0, "\004cuba\005euros\012nacimiento", "\010perfumes", 
+ "\007p\341rrafo\010invadido\011atrasados\006surjan", 0, 
+ "\013implementar\016universitarias\006lanzar", "\006porqu\351\012cualidades", 
+ "\011embajador", "\010aumentar", "\010merengue\006llamas", 0, 0, 0, 0, 
+ "\012solucionar\005ojala", "\007ocurri\363", 0, "\012aumentando", 
+ "\007cl\341sico\012peligrosos\010salguero\006pulpas\014devastadoras\013cardiolog\355a", 
+ "\011comprende\004mana\006acabar", 0, 0, "\005hemos\007cebolla", 
+ "\007secreto", "\006ensayo\011expansi\363n\010terminar", "\013solicitamos", 
+ "\011recientes\014certificados\011pasant\355as", "\010sultanes", 
+ "\016secularizaci\363n", 0, "\012reemplazos", 
+ "\015asesoramiento\017decodificadores\007bermejo", 0, 
+ "\004mano\007sabores\006\341rabes", "\011mejoramos", "\011desprende", 0, 
+ "\011detective\011contratar\006acatar", "\012reemplazar", 0, "\006nausea", 
+ "\007vecinos", "\011escondido", "\011sacerdote", 0, "\012pron\363stico", 
+ "\011destinado\012inversores", "\010apertura", "\014intervenci\363n", 0, 
+ "\012imposici\363n", "\007broches", "\011refuerzan", "\014estructurado", 
+ "\007extrajo\012integridad", "\007crianza", "\014distribuidor\007lealtad", 
+ "\013lanzamiento\006jurado", 0, 0, "\006traigo", "\012colesterol", 
+ "\011almacenar", 0, "\015efectivamente\007delitos\007enlaces", 0, 0, 
+ "\007usuario", 0, "\006jugada\005pisco\012campamento", 
+ "\012exquisitas\012valoraci\363n", "\007chorros", 0, 
+ "\012conectarse\010detallar", "\004r\355os\005human", "\014indumentaria", 0, 
+ "\006opinar\011armaremos", 0, 0, 0, 0, "\014posicionarse\005icono", 
+ "\007perfume", "\010impulsar", 0, 
+ "\007aumenta\006endoso\006brutal\011superando", 0, "\005tumor", 
+ "\007m\372sicas\006expone", 0, 0, "\007liberar\013infiltrarte", 
+ "\010r\355tmicas", "\012programado\007aspiran", "\011aduaneros", 0, 0, 0, 0, 
+ "\005robot\012judiciales", 0, "\007termina", "\011vislumbra", "\010ascender", 
+ 0, "\014consumidores", "\004club", 0, 0, 0, "\013refrescante\006bonito", 
+ "\011retiramos", "\005mucho\005ruano", 0, "\010enviemos\015optativamente", 0, 
+ 0, "\005leche\005hecha\006madero", "\006letras", "\005ser\341n", "\005atrae", 
+ "\010sospecha", "\010combatir", 0, 0, 0, "\006aporte\010sudoeste", 0, 0, 
+ "\010adjuntos", "\013consumici\363n\012irracional", 0, "\010extended", 
+ "\014lanzamientos", "\010gigantes", 
+ "\011poblaci\363n\006franco\015perteneciente", "\004pavo\013facilitando", 
+ "\004afee", 0, 0, "\006frasco\006eligen", "\011pol\355ticos", 0, 
+ "\007pareci\363", "\006crisis\006alarma", "\016descubrimiento", "\006animal", 
+ "\006sabias", "\007dise\361ar", 0, "\014panamericana", 
+ "\011pol\355ticas\006invito", "\011consulado", 0, "\007oyentes\010extender", 
+ 0, "\007v\341lidas\005pista", "\010p\372blicos", 0, 0, 0, "\011candidato", 
+ "\006caviar", 0, "\007dispute", "\012realizadas", "\010indicios", 
+ "\011regalamos\011cat\341logos", 0, 0, 0, "\013inofensivos", 0, 
+ "\011tranquilo", 0, "\007parecen", 0, "\005atr\341s\013instrucci\363n", 0, 
+ "\013guardarropa\010risue\361as", "\004tren", "\010crecemos", 0, 
+ "\013garantizado\011diversi\363n\013solicitarme", "\006receso", 
+ "\004tres\010vainilla\010obstante\013utilizaci\363n\010revisada", 0, 0, 
+ "\005jamas", "\010sociedad", 0, "\013electr\363nico\013inal\341mbrica", 
+ "\013debidamente", "\012recibieron", "\012acreditado", 0, 0, "\011admitidos", 
+ "\012f\341cilmente", "\012vendedores\012cargadores", "\014inconsciente", 
+ "\012accediendo", "\015departamentos", "\010rebajada", "\010fusiones", 0, 0, 
+ "\010importar\014estad\355sticos", "\010preciada\013internaci\363n", 
+ "\011detallada\010contable\010fabricar", "\010fabricas", 0, 
+ "\014inauguraci\363n", "\007gigante", "\015declaraciones", "\003luz", 
+ "\010evidente", "\010rebajado", 
+ "\011destacado\011viviendas\010premiada\007conmigo", 0, 
+ "\005prado\007emoci\363n", 0, "\005pisos", "\012asequibles", 
+ "\005env\355o\010molestia\007validar\006emplea", 
+ "\016norteamericana\007contigo", 0, 
+ "\004pero\006manejo\006sue\361os\005listo", 0, 0, 0, 0, 0, 
+ "\007ficci\363n\012expusieron", "\007p\372blico\011polaridad\006exacta", 
+ "\013informativa\013fundamentos\010quedar\341n", 0, "\013profesiones", 0, 
+ "\013cristalizar\012catequesis", "\007adjunto\010car\341cter", 0, 
+ "\010criterio", "\005m\363vil", "\012cualquiera", "\010decidido\005barco", 
+ "\007an\341logo", "\010intentos\013notificados", 0, "\005larga", 0, 0, 
+ "\007regalar", "\006cuando\014nombramiento", "\013transmitido", 
+ "\014recopilaci\363n\013adelantando", "\013animaciones", "\012contratado", 
+ "\011provincia\007aprobar", 0, "\014garantizados\010sumarnos", 
+ "\016caracter\355stica\010arbolado", "\006cerrar", 0, 0, "\004zoom", 
+ "\005resto", 0, 0, "\007vestida\005tengo\005sepas\007asistir", 
+ "\011colaborar\014inal\341mbricas\010expresar", 0, "\007regular", 
+ "\007anuncio\010bombones\007trampas", 0, "\012muebler\355as", 
+ "\006condes\006vengan\006repito", "\006volcar\011directiva", 0, "\006logren", 
+ "\015impresionante\010infancia\012acad\351micas", 
+ "\007compras\007misario\013anticipar\341n", 
+ "\007estar\341s\016expl\355citamente", "\016implementaci\363n", "\007cuerpos", 
+ 0, "\012masculinos\014jubilaciones", "\013funcionando", 
+ "\017simult\341neamente", "\007importa\007marcial\007pagarle", 
+ "\005trace\010nocturna", "\006eficaz\006tomar\341", 
+ "\010explorar\012contrariar", "\014mentalidades\010variados", 0, 0, 0, 
+ "\007podamos\006luchas", "\006r\341pida\005facha", "\005suiza", 
+ "\006trance\011tur\355stico", "\007podemos\007valorar", 0, 
+ "\012democracia\014constructiva", "\014electr\363nicos\007indiana\006fogata", 
+ "\005tacos", 0, "\012encuentran", "\004nota", 0, 0, "\007florida\007apuesto", 
+ "\010variedad\005corto\004note\007basadas\014informativas", 0, 
+ "\014revolucionar", "\010realismo", "\010amenazas\005jam\341s", 
+ "\014constructivo", "\011auspician", "\005acaso\007legales\010retirado", 0, 
+ "\012votaciones\014renacimiento", "\011criterios\004noto", 
+ "\007fabrica\017extraterrestres", "\011congresos", 
+ "\011ejecutora\013financieras\015incorporaci\363n", 
+ "\007intento\017participaciones", "\014conservantes", 0, 
+ "\010aparezca\011ayudarnos", 0, "\007funci\363n\006muerte", 
+ "\005gusto\010maternal\010camiones", "\010redondos", 0, 0, 
+ "\005llene\005vivir", 0, "\012reglamento", 0, 0, 0, 0, 0, 
+ "\011estuviera\005lleve\014industriales", 0, "\007viajar\341\004irme", 
+ "\011ofrecemos\005bazar", "\005coche", "\007accedan", 
+ "\012alucinante\006nativa", 0, 0, 0, "\005hijas\007profeta\011se\361alando", 
+ 0, "\010corregir", "\011incapaces", "\007estirar", "\013organizaron", 0, 0, 0, 
+ "\014procesadores\005rubio\004boya", 
+ "\012activaci\363n\017administrativas\011describes", 
+ "\010servicio\014complacencia\012balanceada\006murcia", 0, "\010difusi\363n", 
+ "\005hijos", 0, 0, "\004vaca", "\005rubro\011expresado", 
+ "\010tel\351fono\005acaba", "\011atentados", 0, "\012vislumbrar", "\005frase", 
+ "\006campus\011enfadarse", "\005viejo\007opini\363n", 
+ "\010habilita\006misiva", "\011se\361aladas\007retorne", "\011proteicas", 
+ "\010renacer\341\006senior", "\011subyugado\007expresa\014adversidades", 0, 
+ "\007mejorar", "\014complementos", 0, "\013direcciones\005abuse\010siquiera", 
+ 0, "\010domingos", 0, "\005desee\007belleza\006sensor", "\010queremos", 0, 
+ "\010circuito", 0, 0, "\011alrededor", "\007injusto", 0, "\012influencia", 0, 
+ "\012canadiense\007criolla", 0, "\007expires", 
+ "\007legible\016iberoamericano", 0, "\010producir", 0, 0, 0, "\010desierto", 
+ "\006regalo\012desbloqueo\010patricio\013actuaciones", 0, "\007fiestas", 
+ "\005\341cido\006apogeo", "\006venoso\015determinaci\363n", "\010hincapi\351", 
+ "\012pertenecer\012participes", "\007homil\355a", 
+ "\013elaboraci\363n\011educativo\007mogotes", "\011argumento", "\010incendio", 
+ "\012presidenta", "\010contento", "\012cantidades", 0, 0, 
+ "\015actualizaci\363n\007usarlos\013desperdicie", "\010blindada", 0, 0, 0, 
+ "\015discogr\341ficas\005deber", 0, "\004\372til", "\007seguida", 
+ "\010pulsando\015observaciones", "\006modele\015arbitrariedad", 0, 
+ "\006logros", "\007modelos\006formal", "\004mora", "\006bajada", "\004hace", 
+ 0, "\004more", "\007retraso", "\007romance", 0, "\006dobles", 0, 0, 
+ "\011pasajeros", "\012contrastes", 0, "\005miras", 
+ "\012naturaleza\011confirmar\007invent\363", 0, 0, 
+ "\005nieve\006puntos\012semejantes", 
+ "\004edad\012generaci\363n\011completas\012declararse", "\006l\355mite", 0, 0, 
+ 0, 0, "\004pesa\005buque", "\014impedimentos", 0, 0, "\004pese", 0, 
+ "\006altura\006l\355neas", 0, "\011completos", "\005quedo\011transmita", 
+ "\013necesitamos", 0, "\005litro", 0, "\004peso\012participar", 
+ "\010pudieran\011formaci\363n\005veloz", "\012integrarse", "\007libreta", 
+ "\006espera\014convenciones", 0, "\011capitales", 0, 
+ "\012compatible\005leyes\012informador", 0, "\005padre\005queso", 0, 
+ "\015celebraciones", "\013\372ltimamente", "\007paradas", "\011brit\341nica", 
+ 0, 0, "\011pel\355culas\006debajo", "\010expertos", 0, "\012excusiones", 
+ "\005subir\006sitios\014oficialmente", "\013precedentes", 0, "\006famosa", 0, 
+ "\010liturgia", "\011din\341micas\010medallas\013encontrar\341s\011eligiendo", 
+ "\006cestos", "\006fibras\012progresiva", "\013estrategias", "\010monteros", 
+ "\007cuentas", "\005netos", "\010mentiras\005dando", 0, "\005retro", 
+ "\014transparente\011orgullosa", "\012rechazaran", "\004zopo", 
+ "\007visible\011femeninas", "\015emprendedores\011din\341micos\006listen", 
+ "\010opciones\011telefon\355a", 0, "\006barato\006cambi\351", 
+ "\013condiciones", "\004mapa\011femeninos", "\012enunciados", 0, 0, 
+ "\010penetrar", "\016procedimientos", "\011reconocen\007n\341utica", 
+ "\007comprar", "\006marcan", 0, 
+ "\013descripci\363n\004besa\010seguimos\007c\341maras", 
+ "\007maneras\010superare\011reconoci\363", "\007cuadros\013deliberante", 
+ "\006apunta\007obreros", "\006relevo", 0, "\013extranjeros\006romper", 
+ "\013grabaciones", "\010refuerza", 
+ "\017gubernamentales\005queda\012apicultura", 0, "\007menores\005mosca", 
+ "\005podr\341\006supone", "\010acelerar\014manifestando", 
+ "\004beso\012instructor", 0, "\007precisa", "\007remover\007resumen", 
+ "\005quema", 0, 0, 0, "\012despegable", "\012justamente", 0, 0, 
+ "\012considerar\007premisa", "\005estoy", "\005costo", 
+ "\012empresaria\004caza", "\007avenida", 0, "\007llegado\012asesinados", 0, 0, 
+ 0, 0, "\013balanceados\005apnea", 0, "\014especialidad\013s\372bitamente", 
+ "\006debido", "\005dicen\007experto", "\013actualizada\007pudiera", 0, 0, 
+ "\007correos\011venderlos\014californiana\010asamblea", "\007entidad", 
+ "\007llevado", "\010remover\341", "\007cabezal\010llevaran", "\007medalla", 0, 
+ "\007depurar\007entrena\007imprime\010coquetas", "\006matriz", 0, 0, 0, 
+ "\006tortas\007ocultos", 0, "\011consignar", "\014automotrices", 
+ "\006afines\012inventario", 0, "\012ecuaciones", 0, 
+ "\011colecci\363n\012campanario", "\005bel\351n", 0, 0, "\007difusor", 
+ "\013cooperaci\363n", "\011cambiarlo", "\003rey\010logotipo\014licenciatura", 
+ 0, "\011contienen", "\006tomara", "\011procesi\363n", 0, 0, "\006llegue", 0, 
+ "\011profesi\363n", "\007haremos\014protestantes", 0, "\007feriado", 0, 
+ "\007quieran\010celulosa\006exhibe", "\012gobernador\007fatales\007mentira", 
+ 0, "\015incertidumbre\012procedente", "\006mandar", 
+ "\010material\007acelera\007necedad\011prioridad", "\010clemente", 
+ "\006mes\355as", 0, 0, 0, "\010di\363cesis\011amoblados", 
+ "\006rivera\013desarrollan", "\011convierte\016comportamiento", 
+ "\011horquilla\011embajadas", "\005ostra\012aspirantes", 
+ "\005amigo\007mejoras\007superar\012conociendo", 
+ "\011corriente\012esperanzas", "\007fijamos", "\005metal", 
+ "\007explico\010francesa", "\005serio", 0, "\011ceremonia", 
+ "\013tramitaci\363n\007tirando\011concierne", "\005viaja", "\006clubes", 
+ "\005feroz\011expliquen\014recompensado", "\011felicidad", "\007frescas", 
+ "\005anime", "\010conector", 0, 
+ "\010incluyen\011capacidad\010vi\341ticos\005temen\005retos", 
+ "\013dispositivo", "\006formas", "\012terminales", 0, "\010sectores", 0, 0, 0, 
+ "\007conocer\007llegara\013represiones", 0, 0, 0, 0, 0, 0, 0, "\010viajeros", 
+ 0, 0, "\013tonificador", 0, 0, 0, 0, "\011explosi\363n", "\005ideal", 0, 
+ "\010recargan", "\006grasas\010dedicado\012sanitarios", "\010separada", 
+ "\010dedicada\011contratos", "\014actualizadas\011secciones", "\010envuelta", 
+ "\012intermedio", "\005cable\010pastoral", "\005decir", 0, 0, "\010envuelto", 
+ "\010conocer\341", "\004plus", "\007esperes", "\013registraron", 
+ "\010separado\007revisen", 0, 0, 0, "\007plantel", 0, 
+ "\012entendidos\012transcurso", 0, 0, "\012retirarlas", 
+ "\011inversi\363n\005amiga", 0, "\006madera", 0, 0, 0, 0, 0, 
+ "\012ontol\363gico", 0, "\007materia", "\006ladr\363n\011conversor", 0, 
+ "\015internaciones\005emite\011historial\020disponibilidades", 0, 
+ "\010instante", "\007pasaron", "\011quincenal", 0, "\012adquirir\341n", 
+ "\011inocentes", "\013iluminaci\363n", 0, "\013equivalente\011transitan", 
+ "\010terapias", 0, 0, 0, "\012liberaci\363n\007vincula", "\011parroquia", 
+ "\010portavoz", 0, 0, 0, "\013informativo", 0, 0, 0, 
+ "\013perif\351ricos\015identificarse", "\006tienen\010abordaje", 0, 
+ "\011propuesto\012humanistas", 0, "\012promocione", "\007viajero", 0, 
+ "\011publicada", "\014particulares\006sienta\007memento\007dejar\341n", 
+ "\007febrero\005apodo", "\012influyente", 0, "\010biliares", "\007incluye", 
+ "\005error\013transmitida", "\014prestigiosos\007tortura", "\007rodrigo", 
+ "\007recarga", "\011ejecutivo\011bombardeo", "\007carente", 0, "\010llevamos", 
+ 0, "\014dispositivos", "\012tempor\341neo\013consultando\012agron\363mico", 
+ "\011bendici\363n\015significativa", 0, "\007masajes", 
+ "\007apagada\012unificadas", "\005seras", "\005apoyo", "\010permiten", 
+ "\010muchacha\004tiza\006rosada", "\005seres", 
+ "\010trabajos\006cadera\011suburbano", 0, "\010llegamos", "\011intensiva", 
+ "\006radica", 0, 0, "\006copies\011directora\007serrano\013anotaciones", 
+ "\015explicaciones\011lit\372rgica\007recalc\363", 0, "\004beta", 0, 
+ "\013promocionar\007apoyado\010respetar", "\011sorpresas", 0, "\011israelita", 
+ "\006reduce", "\012diferentes\006lluvia\012verticales", 0, "\011filosof\355a", 
+ "\010estricta\015coordinadores", "\013recuperados", "\010mortales\007alcohol", 
+ "\006\341mbito", "\013convincente\004ve\355a", 0, "\006picada\007catal\341n", 
+ 0, 0, "\006buenos\010dictados", "\010estricto\007terapia\011sexolog\355a", 0, 
+ 0, 0, "\011americano", 0, "\011salesiana", "\011derretida\006rodear", 
+ "\005carey\014informativos", "\006maneja\006verlos", "\011variaci\363n", 
+ "\013socialistas", "\013seguramente\010recorrer", "\012recibiendo", 
+ "\013importantes", "\011equipados", 0, "\011equipadas", 
+ "\004novo\013inmoralidad", "\012constituye\013encontraras\010anotarte", 
+ "\014perfeccionar", "\006fallas", "\007cumplan", "\011gestiones\010enviarte", 
+ 0, "\011ejecutiva", "\007se\361ores\010extiende", 0, 
+ "\020especificaciones\007asumido", 0, "\012conceptual", "\012respetando", 
+ "\010estuches\006sirven", 0, 0, 0, "\014seleccionado", 0, 
+ "\007miradas\007valiosa", "\005quien", "\012zanahorias", 
+ "\007alhajas\010invitado", "\014seleccionada\011garant\355as", 0, 0, 
+ "\013excursiones", "\012fant\341stica\012dinosaurio", 0, 0, "\007sanidad", 
+ "\016verdaderamente", "\007trabajo", "\005mismo\007cameras\011entreguen", 0, 
+ "\010horarios", 0, "\010guerrero\005paced", "\010semillas\010fantasma", 
+ "\013implantando", 0, "\005usada\013desesperada", 
+ "\003m\341s\003vea\010remoci\363n", 0, "\007naranja", "\011regresar\341", 0, 
+ "\010org\341nica\010repuesto\011renovamos", 0, 0, 0, 
+ "\012simplifica\005sinos", 0, 0, "\007permite", 0, 0, 
+ "\011recibimos\011atractiva\011japoneses", "\005marca", "\010columnas", 0, 0, 
+ "\005cargo\006galp\363n", 0, "\013prioridades", 0, "\013permanecer\341", 
+ "\015transportamos\011fil\363sofos", 0, "\004eres\007renovar", 0, 
+ "\014consistentes", 0, "\007respeta", "\005v\355deo\007enteres\005ser\341s", 
+ "\010auxiliar", "\013combustible", "\013cuadraditos", 0, 
+ "\006hervir\007tomadas", 0, "\007recurre", 0, 
+ "\007anunci\363\006debate\006\363ptimo", "\011realmente", 
+ "\012prevenci\363n\012procesados", "\012sacramento", 0, 
+ "\015indispensable\010negaci\363n", "\007dictado\007conoces", 0, 0, 
+ "\003usa\012adquiridos", 0, "\013coordinador", 0, 0, "\011construir", 0, 0, 0, 
+ "\011preguntan\006polaca", 0, 0, 0, 0, "\013fundamental", 0, 
+ "\005hacen\010defender", "\010medicina", 
+ "\003mar\006muchas\006ma\361ana\010cancelar\005bot\355n", "\005cache", 
+ "\011ambicioso", "\010medicine\006asocia", "\007horario", "\007seguido", 
+ "\012totalmente\005bot\363n", "\007semilla", 0, 0, "\006hallan", 
+ "\005cloro\005capaz", 0, 0, "\005dorso", "\005norte", 
+ "\011visualiza\006marque\006atraer", 0, "\007planteo", "\013alrededores", 
+ "\014modificaci\363n\010ac\372stica", 0, 0, "\011requerida", 
+ "\011innovador\007guantes\007culture", "\006buscan\004moto\014agradecerles", 
+ 0, "\007armados", 0, 0, "\011disminuye\006demora", 
+ "\007poquito\007racismo\006aborda", 0, "\016exclusivamente", 
+ "\006listas\010aditivos\005junco\004rezo", "\012indigencia", 0, 
+ "\013preparaci\363n\017presentimientos", 
+ "\015arquidi\363cesis\014permanecer\341n\006movida\014morfol\363gicas\011afectados", 
+ 0, "\010pinturas\005junio", "\005libre\010acaricia", 
+ "\005lugar\007perdura\005qui\351n\004vean", "\012reposici\363n", "\006venido", 
+ 0, "\007secci\363n", "\004veas\010custodia", "\005voces", 
+ "\004cr\355a\013combinaci\363n", "\010dictarle", "\005junto", 0, 0, 
+ "\011puramente", "\010jardines\005censo", "\010llegando\006vargas", 0, 
+ "\007puertos\006varias\007suprema\007pasar\341n", "\007puestos", 
+ "\007columna", "\010internas\011espirales", 0, "\013hexadecimal", 0, 
+ "\005grade\012conectados", "\007debemos", "\011pantallas\005dique", 0, 
+ "\007circulo", 0, "\007prisi\363n\011describir", "\010campa\361as", 
+ "\011respuesta\010llevando", 0, "\013pensamiento\017complementarias", 
+ "\006medico\011acercamos", "\006unirse\010rentable", 0, 
+ "\014convergencia\014coordinadora", "\007nombres\011navegador", 
+ "\006rendir\011estimados\006llegas", "\007cancela\011bas\341ndose", 
+ "\005grave\010sentir\341s", "\004jefa", "\017actualizaciones", "\005mar\355a", 
+ "\006sexual\010suspende", "\010responda\004jefe\006llenas", 0, 0, 
+ "\007empleos\007tuvimos", "\004usan\010responde", "\011simulador", 
+ "\010catalana", "\011dirigirse", "\004usar\005citas\007envases", "\004usas", 
+ "\011dirigirte", "\012impresoras", 0, 0, "\011instalado\011valorados", 
+ "\011visitante", "\003paz\012dificultad", "\010avanzado\006tesoro", 
+ "\011extensi\363n", 0, 0, 0, "\007ofertas", 0, 0, "\011empleados", 
+ "\012presentado", 0, "\013presidencia", "\010primeros\011contrario", 
+ "\006gentil", "\010avanzada", "\007soltero", 
+ "\011confiando\015funcionalidad\005razas", "\006breves", 
+ "\011empleadas\012entregarse", "\010defensas\004toca", "\014mundialmente", 0, 
+ "\011eliminado\010lavadero", "\006textos", "\010mensajes\011tendremos", 
+ "\013vencimiento", "\006flores\005tasan", 
+ "\011encuentro\007ansiosa\006sigues\007guardia", "\010original\006recova", 
+ "\011requieren\011agasajado", "\012maliciosos", 0, 0, 0, 0, 
+ "\006abonar\007interna", 0, 0, 0, 0, 0, "\013nominalismo", "\006vengas", 
+ "\013experiencia\004pude\007plateas\006crespo", "\007pintura\012significar", 
+ "\004lord\006capote", 0, "\011municipal", "\006m\355nimo\015pasionalmente", 0, 
+ "\010negocios", 0, "\006sucede", "\004pudo", "\004liga", "\006ventas", 
+ "\004loro\007tendr\341n", "\007salimos", "\011ganadores", 0, 
+ "\011conseguir\011bicicleta\005ajena", "\005mujer\004rez\363", "\010limpieza", 
+ 0, "\007cientos", "\011empezar\341s\015coleccionista", 0, 0, "\007ciertos", 0, 
+ 0, "\011presidir\341", "\005llam\363", "\011feminista", 0, 
+ "\007termine\013continuidad", "\007campa\361a\006sol\355as", "\004diva", 0, 
+ "\005ruego", 0, "\017aproximadamente", "\006p\341nico", 0, 0, 
+ "\016constitucional\010pesquera\010tratadas\012telescopio", 
+ "\015absolutamente", "\010elegidos", "\013gastronom\355a", 0, 0, 
+ "\007primero", 0, "\010t\351rminos", "\013responsable", 
+ "\004alta\006deseos\006sorteo\010descarga\006esposa\014presidencial", 
+ "\006visado", "\010relaci\363n", "\011encuentra\007defensa\011referirse", 
+ "\005casco", "\010tremendo", 0, "\012contenidos", 0, "\004foco", 0, 0, 0, 
+ "\005dices\007trastos", "\004alto", "\010tremenda\011convocado", 
+ "\013patol\363gicos\016congelamientos", "\007anchoas", "\015beneficiarios", 
+ "\010sucursal\007tomarse", "\010proteger", "\007tomates", 
+ "\012empresario\010tirolesa", 0, "\007filtrar\012distinci\363n", 
+ "\010b\372squeda\011cerrillos", "\006visi\363n", "\010termales", 
+ "\012manifiesta\010destreza", "\015continuaremos\006tocado", "\006ancora", 0, 
+ "\006arroyo\015consideraci\363n", "\004lapa\011sencillez", 0, 0, 0, 
+ "\006recibe", "\007negocio\006estar\341", "\006mill\363n", 
+ "\006trucos\013sustituci\363n", "\013prohibici\363n\014vencimientos", 
+ "\010brindara", "\007cursiva", "\007mensaje", 0, "\013llamamiento", 
+ "\005maine", "\006cari\361o", "\011religioso", 
+ "\012patolog\355as\010consenso", "\013inmovilidad\010vers\341til", 
+ "\012emigrantes", 0, "\007molinos", "\010cerradas", "\003mas\007d\351cadas", 
+ "\014experiencias", 0, 0, "\007parezco\004trio", 0, 
+ "\012balnearios\006juegan", 0, 0, "\010opresi\363n", 
+ "\006socios\012directores", "\012americanas", "\005zonas\006remate", 
+ "\013empresarial", "\005nueva\015agropecuarias\005\341rbol\010relativa", 
+ "\005fondo\007licores", "\010superior\011emocional\007impulse", "\006eterno", 
+ "\012ordinarios\012secundaria", "\007elegido", "\007natural\006remite", 
+ "\007docenas\007t\351rmino\006espuma", 0, "\006seguro", 
+ "\014responsables\013manteniendo", 0, "\007matrona", 
+ "\007combate\007drivers\012exigencias", 
+ "\012encabezado\010piscinas\010relativo", "\013crecimiento", 
+ "\007extremo\011le\361adores", 0, 0, "\012institutos\011estancias", 
+ "\012visualizar\007cumplir\006diodos", "\006mundos", 
+ "\005andan\015supuestamente", "\011necesaria\012deportivas\007divinas", 
+ "\010impuesto\007bloques\005valla", "\011seud\363nimo", 0, "\011universal", 0, 
+ 0, "\011masculina", 0, "\006dilema\012compresi\363n", "\005salud", 
+ "\011colocamos\005logr\363", 0, "\010mediante", 0, 0, 
+ "\010ascendi\363\005dejan", 0, 0, "\015contactaremos", 
+ "\007termin\363\005dejen", "\007neblina\005preso\006odisea", 
+ "\011esperamos\012frecuencia\006volvi\363\012llamaremos", 
+ "\005clase\006tejido\011pertenece", "\006enorme\004ajeo", 
+ "\004cita\015productividad\016transformaci\363n\007rotonda", 
+ "\005clave\005pague\013reflexionar", "\010rejillas", "\010pr\355ncipe", 0, 
+ "\011lecciones\006v\341lido\010cumplir\341", 
+ "\006evento\011calabresa\013comunicarle\006in\372til", 
+ "\006tandas\011estatales\012desenfund\363", 0, 0, "\006brutos\007debatir", 
+ "\011localidad\012culturales\011profundas", "\007protege", 
+ "\010agradece\014amortizaci\363n\013intercambia", "\006bienes", "\005dolar", 
+ 0, 0, "\010rechazos\010hacernos", 
+ "\010deseamos\007brindar\011oficiales\015colaboradores", 0, 0, 
+ "\014intolerancia", "\005nuevo\006tantas\004men\372\006placer", 0, 
+ "\007campe\363n", 0, "\007tampoco\006vitola", 0, "\005dolor", 
+ "\007alemana\005rev\351s", "\011urgencias", 
+ "\010abogados\007oficial\007agentes\012remanentes", "\005furor", 
+ "\010policial", "\004masa", "\005termo", 0, "\012componente", 
+ "\013conoceremos", "\011envejecer", 0, "\013canadienses\013visibilidad", 0, 
+ "\006expire", "\010habitual", "\011presencia\011anunciado\011relativos", 
+ "\005fallo\010editoras", 0, "\005crear\007piscina", 
+ "\007importe\007cerrada\012peligrosas", 0, "\010brindar\341", 
+ "\006cortes\005creer", 0, 0, 
+ "\012petroleras\016investigadores\005optar\013migraciones", 0, 
+ "\014competitivos\013desigualdad", "\013contraataca\007gravita", 
+ "\011conocidos", "\011participa", "\004toda\011delinquir", 
+ "\011invitando\010derechas\010remiendo", "\012contribuy\363\012curr\355culum", 
+ "\006sufri\363\010terrenos", "\012movimiento\006virtud\011fragmento", 
+ "\005bucal", "\012caballeros\011tonicidad", "\011informado", 
+ "\011diputados\014optimizaci\363n\006copado\010peruanos", 
+ "\011aclarando\006l\363gico\012pedaleando", "\013plataformas\011invitarte", 0, 
+ "\014negligencias", "\010hotelera", "\004todo", "\014subscripci\363n", 
+ "\006pierda\014crecimientos\011pasaporte", 
+ "\011naturales\013escribiendo\005acero", "\006viv\355an", "\010hotelero", 0, 
+ 0, "\011permitir\341", "\010borrando\004losa", 0, "\004anal\006asueto", 
+ "\012luminarias", "\004lose\012radiofon\355a\005molde", "\012propuestas", 0, 
+ "\003ido\011estaremos", "\011comisar\355a", "\007cabezas", 
+ "\012estructura\007rechazo", "\006asunto\010postdata\012encuentres", 0, 0, 
+ "\015ensambladores", "\004pues", 0, "\014motocicletas\006perico", 0, 
+ "\013reconocidas", "\010regresar", "\005abono", "\005\341ngel\005acoge", 
+ "\013fant\341sticos\006dulces\010biolog\355a", "\012encargados", 0, 
+ "\012agradecer\341", "\013intendentes\010redactar", "\011sorprenda", 0, 
+ "\007abogado", "\014asesoraremos\011pareciera", "\010ortodoxo", 
+ "\012acompa\361ado", 0, "\014period\355stico", "\007pistola", "\007abonado", 
+ "\011aranceles", 0, "\012nacionales\006compre\011ofrecidos", 
+ "\014period\355stica", 
+ "\013vocabulario\014intercambiar\006quedar\006reside\007urbanos", 
+ "\010actuales", "\015perfectamente\014problem\341tica", 
+ "\006podr\341n\017respectivamente", "\010ortodoxa\007explore", 0, 
+ "\006hierro", "\011mezclador\010fabulosa", "\007tractor", "\006quemar", 0, 0, 
+ 0, "\010fabuloso\015concretamente", 0, "\007derecha\007arancel\012devastaron", 
+ "\007estable\014regularmente", "\011considera\006saques", 0, "\010homenaje", 
+ 0, "\006viajar\012escogiendo", "\006tierra\011efectivos\004ergo", 
+ "\005fecha\005hecho\006cocine\013nacimientos", 0, "\020representaciones", 0, 
+ "\007ingenio\014sorprendidos", "\003eco", "\013diariamente", 0, 
+ "\011evitarlos", 0, 0, 0, "\011abundante", 0, 0, 0, 
+ "\004buen\010bomberos\010moderado", 
+ "\010mendigar\014misericordia\015esencialmente\011concursos", 0, 0, 0, 
+ "\010sesiones", "\007titular\011facilitan", "\010pedestal", 0, 0, 0, 
+ "\005abona", "\007terreno\006gritos\016experimentales", 0, 0, 
+ "\006porque\012pastorales\010accionar", 0, 0, "\015planificaci\363n", 
+ "\012automotriz", "\012publicidad\004suma", 0, "\006exceso", "\006alguno", 
+ "\005linea", "\006unidas", "\010multitud", 0, "\012religiones", 
+ "\013legislaci\363n", 0, 0, 0, 0, "\004sumo", 0, "\010aspectos", 
+ "\004haga\007cheques", "\010justicia", 0, "\014traducciones", 0, 
+ "\011cuadrados", 0, "\016planteamientos", 
+ "\006viajes\007dibujar\012adherentes\012pensadores", 
+ "\006propia\016frecuentemente", "\010utilidad\010parrilla\010l\341mparas", 0, 
+ "\012infusiones\011celebrar\341", "\013aniversario\012ciudadan\355a", 
+ "\004hago\005abren", 0, "\007regresa\005rival", "\011fabulosos", "\005damas", 
+ "\012asistencia\006calles\011silvestre\007sexuada", "\007pedirlo\010definido", 
+ 0, "\006olivos\012requisitos", 0, 0, "\011gestionar", 0, 
+ "\011construye\007integr\363", 0, "\007fundada", 0, "\010bas\355lica", 
+ "\005damos", "\011congelado", "\012diferencia\007ocupado", 
+ "\010residuos\010elaborar", "\010imprimir", "\006sabina", "\005risas", 
+ "\005a\351reo\011altamente\007\355ntegro", "\013programados", 
+ "\014cumplimiento", "\006cuotas", "\013disposici\363n\007picados", 
+ "\007\341ngulos\012provocarle", "\013equilibrada\007produce", 
+ "\007borrado\005quino", 0, 0, "\012evaluaci\363n\010selector", 
+ "\006\363ptima\005untar", "\012servidores\011recibirlo\005quiso", 
+ "\011introduce\014convencional\013revalidando\005quito", 0, 
+ "\012necesarios\007n\372meros\011iniciarse\011quincenas", 
+ "\013diplom\341tica", 0, "\010llamados\006minute\006cabras\013totalitario", 0, 
+ "\004lema", "\010unidades", 0, "\007glucosa", 
+ "\010sencilla\007dejando\005exige", "\012desempe\361an", 0, "\007franc\351s", 
+ "\010estaci\363n\010adicione\010ficheros\010deber\355an\015considerables", 
+ "\006polaco", "\012levantando", 0, "\007recibir\012art\355sticos\010habernos", 
+ "\015trascendencia", "\010sencillo\016contempor\341neos\010tramites", 0, 
+ "\011realizar\341\011rom\341ntica", "\005dardo", "\004\341rea", "\007aspecto", 
+ "\006aplica", 0, "\011protocolo", "\007asfixia", "\006alonso", 
+ "\011venturoso", "\013aconsejamos", "\010obsesi\363n", 
+ "\011pr\341cticas\012afiliaci\363n\007aceptar", "\004mata\012diversidad", 0, 
+ "\007encarta\011titulares\012ingresando\014aniversarios", 
+ "\012impositiva\012invitaci\363n", "\004mate", "\007prestar\007l\355deres", 
+ "\017administradores", 0, "\005vendo\005testo", 
+ "\006creada\011pr\341cticos\015tribulaciones", "\012septiembre", "\005vengo", 
+ "\012convertido\014considerarse", 0, 
+ "\010recibir\341\011definidas\007comento", 0, "\007avances", "\012claramente", 
+ "\006tengan\007hiciera", 0, "\010primeras", 0, "\005a\351rea", 
+ "\010abandono\006navega", 0, "\006chicos", 0, 0, 0, 0, 0, "\007l\341mpara", 0, 
+ "\015participantes\006\363ptica", 
+ "\010sociales\010cartucho\011recibirla\005reine", "\013programarlo\005quita", 
+ 0, "\007caseros\010abandona", "\006valido", "\012escuchamos", "\005audio", 
+ "\004sum\363", "\011tur\355stica\005milla", "\007faltaba\010archives", 
+ "\004piso", 0, 0, 0, "\007llamado", "\010revolver", 
+ "\011situaci\363n\012peninsular", 0, 0, "\007versi\363n\010enviarse", 
+ "\004lote", 0, "\005fruto\007fichero", 0, 0, 0, 0, "\006\355ndice", 
+ "\007paredes", "\007minutos", 0, 0, "\006mismas", 0, "\011inclusive", 
+ "\007deber\355a\006lavado\006cuente", 0, 0, "\012pretend\355an", 
+ "\013redescubrir\007liderar", "\005nivel\013actividades\005gusta", 0, 
+ "\005ritmo\010denomina", "\014contracturas", "\010absoluto\011mantendr\341", 
+ "\014especulaci\363n", "\007sombras", "\011testeados\012configuran", 
+ "\005ancho", "\005pulso", "\014beneficiarse\011descubri\363", "\003mil", 
+ "\007piernas", "\012facultades\013sufrimiento", 0, 0, "\007jugando", 0, 
+ "\010absoluta", "\012violencias", "\006centro\011constante", 0, 0, 
+ "\014construyendo", "\006apenas\007humilde", 
+ "\007primera\010criadero\006marcar\010reunir\341n", 0, 0, 
+ "\011sumamente\015investigadora", 0, "\012literatura\015ilustraciones", 
+ "\006colega", "\011descifrar\013recomiendan\007deseado", 0, "\013comunidades", 
+ "\012habitaci\363n", "\014federaciones\012registrado", 0, "\010adjuntar", 
+ "\004taca", "\007cadenas", 0, "\014felicitaci\363n", 0, 
+ "\007valores\011algarrobo", 0, "\007archive", "\005vieja\007adoptar", 
+ "\005maura", "\006pu\361ado", 0, "\014congregaci\363n", "\012alimentada", 
+ "\004taco\010descansa", "\012desarrollo\011interesar", "\015desarrollamos", 0, 
+ 0, 0, "\012reacciones", "\012esculturas\005arias", "\007escenas", 
+ "\006sentir", "\007hoteles\010descanso", "\015universitaria", 
+ "\007lectura\006sab\355an", "\006apoyar", 0, "\010mostrara", 
+ "\014destinatario", "\013sugerencias\006duerme", 0, 
+ "\014cotizaciones\012entrecruza\012incentives", "\010pastores\007ap\363stol", 
+ "\006triple", 0, 0, 0, "\006ten\355an", 
+ "\013reproductor\012sucursales\005ancha\007tiendas", "\005pulsa", 
+ "\005reloj\012ignorancia", 0, 0, 0, "\011novedades", "\003iba\011criticado", 
+ 0, "\006objeto", 0, "\005raz\363n\006cuarta", "\013garantizada", 
+ "\007calidad", 0, "\010im\341genes\004\363leo\017especializaci\363n", 
+ "\006bolsas", 0, 0, "\005vasco", "\007s\341bados\010din\341mica", 
+ "\013apareciendo", 0, 0, 0, 0, "\006martes", "\007novedad", 0, 
+ "\012comentario\011preciados", "\011municipio", "\007humanos", 0, 0, 
+ "\013mencionados\010hablando\011portuaria", 0, "\005urnas", 
+ "\012exposici\363n", 0, 0, "\007solanas", "\015pol\355ticamente", 
+ "\013retenciones", "\012reaccionar\007ponerlo", "\010terrazas\007tr\341fico", 
+ "\007tr\341gico", 0, "\010cableado", 
+ "\006naci\363n\010decorado\014considerable", 0, "\006divide\011colectivo", 
+ "\007ajedrez\012reconocido", 0, 0, "\012matrimonio", 0, "\007comunes", 
+ "\005extra", "\007vacunas", 0, 0, "\007piedras", "\010decorada", 
+ "\010ofensiva", "\010tapiales", "\007boleros\007emisi\363n\006traiga", 
+ "\006enlace\006delito", "\012visitarnos", 0, "\012castellana\007adjunta", 0, 
+ "\011tradici\363n\011dominical", "\007estoque", 0, "\004lena", "\005suelo", 
+ "\010insultos\015universalidad", "\004once\007colombo\007surtido", 0, 0, 
+ "\012apremiante", "\013desocupados", 0, "\005donar\010medicare", 
+ "\007mostrar\012condensada\007torture", 0, 0, 0, 0, 0, 0, 0, 0, "\007cartero", 
+ "\007tarifas", 0, "\007macizos", "\011excelente", 0, "\006regi\363n", 
+ "\014investigador\007auditar", 0, "\005\372nica", 0, 
+ "\006p\341gina\011escritura\010estrenar", 0, 
+ "\010comicios\011aprovecho\017unilateralmente", 0, "\007divisas\005ganen", 
+ "\007guerras", "\010t\351cnicas\012impregnado\017correspondieron", 0, 
+ "\012artificial", "\011concurrir", "\012distribuya", 0, "\011necesiten", 
+ "\004iban", "\011sirviendo", 0, "\011necesitan\005opine", "\015administrador", 
+ "\011comprarlo", "\007regres\363\010recurrir", 0, "\012calentitos", 0, 0, 
+ "\010permitir\005dejes", 0, "\007ponemos\012pronostico", "\005baile", 
+ "\010liquidar\012extracci\363n", "\012policiales", "\012aparecer\341n", 0, 
+ "\013competencia", "\006jard\355n\007chistes\015supervivencia", 
+ "\004rule\007escasos", "\006imagen", "\011aportados", "\006franca", 
+ "\011narrativa", "\007moscada", "\012corrupci\363n", 0, "\010amoblado", 
+ "\006trav\351s", 0, "\006figura\014electricidad", 0, 
+ "\010est\351tica\010te\363ricas", 0, "\005\372tero\007bengala", 0, 
+ "\011animaci\363n\016significativos", "\012definici\363n", 
+ "\007terraza\005monde\007negarlo\010grabadas", 0, "\006v\341lida", 
+ "\006excite\012registrar\341\006buscas", 0, "\006genera", 0, 
+ "\013fotograf\355as", 0, 0, 0, "\006siente\010rastreen", "\005muri\363", 
+ "\010misterio", "\007porotos", "\005tutor", "\005monte", 0, "\004irse", 0, 
+ "\010culturas\007limpias", "\010cuarenta\007liviano", 0, "\005salle\005letra", 
+ "\007g\363ticos", 0, "\012paranoicos", "\010hebraica", "\010antiguas", 0, 0, 
+ 0, "\012realizar\341n", "\006raci\363n\005salve\006reglas", "\007t\351cnica", 
+ 0, 0, "\010cultural\012existentes\005lobos\010romanina", "\012introducir", 
+ "\013consagrados", 0, 0, 
+ "\012destacados\011provenzal\006a\361adi\363\010traslado", "\011responder", 0, 
+ 0, "\011descuento\007sumando", "\014organizaci\363n", "\006correa", 
+ "\011autoridad", "\011recorrido\007crecido", "\010espacios\005sue\361o", 
+ "\005sabia", 0, "\006se\361ala\007butacas\012perjudican\015gastron\363micos", 
+ 0, 0, "\012molestarlo", "\014competencias\007marinos\006vivido\010elegante", 
+ 0, 0, 0, "\007aspirar\011trimestre\006castor", 0, 0, "\005reyes\004bits", 0, 
+ 0, "\007respete", 0, "\006ignore\011escolares", "\011significa", 
+ "\006reci\351n\012pesimistas", "\006empleo\012naturistas\010sensores", 
+ "\012necesitado", "\012aplicaci\363n\005actor", 0, 0, 0, "\014abiertamente", 
+ "\011sensuales\006actriz", "\006sumado\013atenderemos", 0, 0, "\010tonifica", 
+ 0, "\010certamen", "\003use", 0, "\006sumido\011pondremos", "\010celebran", 0, 
+ "\007adultos", "\007sensual\007granada", "\005gallo", 0, "\007materno", 
+ "\007sacarle", "\011hist\363rico\006bombas\010permiti\363", 
+ "\011patacones\005hagan", "\005crees", "\011apuntando\010amanecer", 
+ "\010celebrar", "\012cient\355fico\012comentarle", 0, 0, 
+ "\011nutritiva\012vulnerable", "\007antigua\007te\363rica", "\007publico", 
+ "\007estabas\004soda\011delantero", 0, "\010interino", 0, 0, 0, 
+ "\010recorri\363", 0, 0, "\005buceo", "\013suscripci\363n", "\010sostiene", 
+ "\005lucha\011much\355simo", "\014consultarnos", 0, "\013extensiones", 
+ "\006gastos\006repite", "\010tabletas", 0, 0, "\007cultura", 
+ "\007espacio\007regreso\010cortando", 0, 0, 0, 0, 
+ "\007ponerte\014imperialismo", "\013preferencia\010adhesi\363n", 
+ "\010realizan\006repute", "\011elementos\010tengamos", 0, "\007cr\355tico", 0, 
+ "\011cabezales\011costumbre\011derivados\015destacamentos", "\011propender", 
+ "\007parecer", "\010precisos", "\011visitados", "\010escuelas", 
+ "\010vidriera", 0, 0, "\010sorpresa\011debilidad", "\007atiende", "\005pumas", 
+ "\010hardware", "\010llamando\013beneficioso\006rodean\016concesionarios", 
+ "\006ahorro", 0, "\005yemas", "\005cerro\012publicarse", 0, 
+ "\014desaproveche", 0, 0, 0, "\010realizar", "\005total", "\007refleja", 0, 0, 
+ "\013secundarios\010bailable\005voten", "\012abecedario\004neto", 
+ "\012corregidos\013telescopios\004onda\013intangibles", "\007grandes", 
+ "\006vuelos\011actualice\007emerger", "\013suscribirme", 
+ "\006nevada\007presi\363n", "\010procesos\006niegan", 
+ "\013procedentes\012magisterio", "\005crema", "\007celebra", 
+ "\010profesor\015seleccionando", "\011quisieron", "\010descubra", 
+ "\005sab\355a", "\007carisma", 0, "\004aire\013transformar", "\012optimizada", 
+ "\005obras\011populista\010contin\372e", 0, 
+ "\011publicado\007edici\363n\012aleatorias\006dichas\004flan", 0, 
+ "\010contin\372a", "\010contexto", "\012prendarios", 0, 
+ "\004usen\010festival", "\015mantenimiento\010cercanos\006valles", "\004sabe", 
+ "\005armar\006terror", 0, "\011selecci\363n\004uses\011dedicadas", 
+ "\007tableta\011cu\341druple\016discriminaci\363n", 
+ "\015seleccionados\007leyendo", "\005d\363lar", 0, "\010densidad", 
+ "\010cap\355tulo", 0, "\010garant\355a\015seleccionadas\012vocacional", 0, 
+ "\013entrevistas", 0, 0, "\007castigo", "\006pasado\006posada", 
+ "\011aberturas\006kernel\011guerrilla", "\011reflejado", "\012mayoristas", 0, 
+ "\013comunicarse\007mantuvo", "\013encontraron", "\010cocheras", 
+ "\010traseras\012espiritual\005oto\361o", 0, 0, "\011interfase", 
+ "\010limitado\007preciso", "\007devenir\007creamos", 
+ "\010limitada\007t\355picas", "\007morales", "\011vinculada", 
+ "\007creemos\011huracanes", 0, "\005cinco", "\004vial\010primaria\006nueces", 
+ 0, "\004yema", "\011partici\363n", 0, "\011propiedad", 0, 
+ "\007realiza\004anda", 0, "\006duelos", 
+ "\010llegaron\005naci\363\012adventista", "\006se\361al\363", 
+ "\007batidas\007afectan", 0, "\010sobrinos\010fetiches\007habr\355an", 
+ "\014preferencias\007alertas", "\012ministerio\011deportivo", 
+ "\006duetos\006ganaba\020interpretaciones", "\005luc\355a", 
+ "\010cr\351ditos\006mancha", "\012gacetillas", 
+ "\006verlas\011peri\363dica\007decirse", "\007escuela", 
+ "\010esp\355ritu\004robo\012denominada", "\007proceso", 0, "\013construirlo", 
+ "\006regale", "\011traslados\011ontolog\355a", "\010temprano", 
+ "\013prestadoras", "\010manejada\012consciente", "\012cotizaci\363n", 0, 0, 
+ "\006pagina\005lunes\013perspectiva", 0, 0, "\011cumplir\341n", "\007medicas", 
+ "\007limpiar\005lleg\363", 0, "\007gastado", 0, "\010volverle", "\007cercano", 
+ "\012candelaria\010aluminio\011locutoras", 0, "\004hola\011operaci\363n", 0, 
+ "\005punto\005ruido", "\006basado", "\006pongas\010camarero", 
+ "\006modelo\011reenviado\007festiva\006prenda", "\010aquellas\010bater\355as", 
+ "\005llev\363", 0, "\004mera\005locos", 0, "\006pasivo\010tem\341tica", 
+ "\011inspector\010cet\341ceos\012casamiento", "\011preguntar\013frecuencias", 
+ "\007cortado", "\007servir\341", "\007cochera\013preventivas", "\010expensas", 
+ 0, "\011llamarnos", "\006sobran\011compensar", "\013promociones\006modulo", 
+ "\010quisiste", "\010ministro\004mero\005cre\355a\012contemplan", 0, 0, 
+ "\007trovare\011diocesano", 0, "\012operativas", 
+ "\014orientadores\011reproduce", 0, 0, 0, "\007reducir", 
+ "\004crea\011creciente\007ofrecen\011filipinas", 0, 0, 0, 
+ "\004cree\013ampliamente", "\005chile", 0, "\007caseras", "\006valida", 0, 0, 
+ "\004opte\007hablara\010cobramos", 0, 0, "\004creo\007cr\351dito", 
+ "\010cartilla", "\017consideraciones", "\014computadores", 0, 0, "\004lata", 
+ "\007trasera", "\006vistos", "\020constitucionales", "\004late\010verduras", 
+ "\007energ\355a\007motores", 0, "\005civil", "\014secuenciador", 
+ "\007tercera", 0, 0, 0, 0, 0, 0, 0, "\007ajustar\007indican\007padilla", 
+ "\011callejero", "\005punta", 0, 0, "\010morrones\013intenciones", 
+ "\012est\341ndares\006t\341cita\010nosotras", 0, 0, 0, 0, 
+ "\011episcopal\011salvaci\363n", "\004supo\010desigual\010proveer\341", 
+ "\010adquiere\007accedes", "\007aquella\007bater\355a", "\006casual", 0, 
+ "\013perseguidos\010adquiera\007hicimos", 0, 0, 
+ "\014emancipaci\363n\010afiladas", "\003tus\010deportes", 
+ "\010brevedad\013imperdibles", "\006due\361os", "\010estimula", 
+ "\003fea\010interior\005l\355nea\014perspectivas", 0, 0, 
+ "\006partir\012operativos\012integradas", "\003r\372a\004mina\007gerente", 
+ "\010miembros\012contribuya\011fantasmas", "\006espese", "\010ser\355amos", 
+ "\012financiero\004mine\012realizarse\014agrimensores", "\005tomas", 
+ "\005grupo\010pesebres", "\011determine", 0, "\005comed\005tomes", 
+ "\006discos\004ocho\010reconoce", "\014desarrollado\006faltan", "\004apta", 
+ "\007tributo\011disfrutes\014codificaci\363n", 
+ "\012registrada\012\372nicamente\011oscuridad\005var\363n", 
+ "\006tendr\341\010visiones", 
+ "\012resoluci\363n\007dirigir\010asociado\010festejos", 0, 0, 
+ "\012extranjera\006valora", "\012siguientes\007ganando\011autom\363vil", 
+ "\014desarrollada", "\013legionarios\005cesio", 0, 0, "\015proporcionada", 
+ "\005sitio\004apto", "\004obra", "\005justo", "\007celof\341n", 0, 
+ "\012pontificio", 0, "\007caracol", "\005fibra\012imbatibles", 
+ "\012espectador\007verdura", "\011reemplazo", "\006tenido\005pocas", 
+ "\010almuerzo", "\012procurador", "\016desbloquearlos\006oscura", 
+ "\011seminario", 0, "\006da\361ado\010molestar\005cazan\012negociamos", 
+ "\007mayor\355a\011compartir\005\351tica", 0, "\003esa", 
+ "\006tareas\007colocar\010obtendr\341\007medidor\017individualmente", 
+ "\011recibir\341n", "\003amo\011semanario\006reciba", "\007combine\006exista", 
+ "\005pocos", "\007merecen", "\007utilice", 0, "\006solapa", 0, 0, 0, 
+ "\007acogida", 0, "\014carpinter\355as\016pertenecientes", 
+ "\006partes\010disponer\007ocultas\006tartas\011verificar", 0, 
+ "\006ayudan\004ra\355z\011acercarse", "\011esfuerzos", "\011limitados", 
+ "\005dar\341n\004saca\017fortalecimiento", 
+ "\003mes\011limitadas\013discriminan", 0, "\012integrados", "\010ayudarle", 0, 
+ "\007hombres\010sabiendo\005erase\007v\355ctima", 0, "\007deporte", 
+ "\011ofrecer\341n", "\012integramos\011dieciocho", 
+ "\011intereses\012conocernos", 0, "\007miembro\012conocerlos\004cre\363", 
+ "\010ayudarlo\006dise\361a\004saco", "\010agilizar", 
+ "\012corrientes\005sexos", "\010descuido", "\005siete\007pesebre", 
+ "\010editores", 
+ "\010enroques\007volver\341\004opt\363\007garanta\012did\341cticos", 
+ "\007navidad\005criar", 0, "\010disponen", "\006cobrar", "\012secuestran", 
+ "\011acercarle", "\006remota", "\010encuesta", 0, 0, "\004abra", 
+ "\007parques", "\005prima", 0, "\004abre", 0, 0, 
+ "\007salinas\006escoja\006noci\363n", "\014informaremos\010cuidados\005prisa", 
+ "\012promotoras", "\015relativamente", 0, "\011izquierdo", "\006mental", 0, 0, 
+ "\012vinculados", 0, "\007dif\355cil\010espinaca", "\004roca", 
+ "\012duplicados\011recoletos", "\012chequeados\011cincuenta", 0, 
+ "\007molesta", "\006f\372tbol", "\010resolver", 0, 
+ "\012permitimos\007festejo", 
+ "\013diagnostico\010rellenar\011colateral\012justifican", 
+ "\011invitados\010rellenas\011esperemos", "\007quitado\010revender\005edita", 
+ "\011invitadas", 0, 0, 0, 0, 0, 0, "\012equivocado", "\007ilustre", 0, 0, 
+ "\006tengas\013representan", "\010gr\341ficos", 0, "\012imprimirse", 
+ "\010deslinde", 0, "\011feminismo\007urgente\004oral", "\010muscular", 
+ "\010deslinda", 0, "\006env\355os", "\010albergar\012conocerlas", "\004orar", 
+ 0, "\014respondernos", 0, "\011monterrey", 0, 
+ "\006estuvo\016reconstrucci\363n", "\010europeos\005podio\010vallejos", 0, 
+ "\004esas", "\010cantante", "\004amor", "\010decisi\363n", "\004mesa", 0, 
+ "\012cristianos\007riesgos\014consideramos\006batido\006ratito", 0, 
+ "\005pulir\012integrante", "\013tradiciones\007ofendas", 0, "\007dispone", 
+ "\006susana", "\006parada", 0, "\012inoxidable\005culpa", 0, 0, "\005orden", 
+ 0, "\011izquierda", "\006quitar\007sierras\005dosis", "\007cuidado", 
+ "\013recomendado\005adi\363s", "\006ocurra", 0, "\007extra\361a", 
+ "\011cambiando", "\011mostradas", "\011ejecuci\363n\007palique", 0, 
+ "\004doce\006fiebre", 0, 0, 0, 0, 0, 0, "\012celebrando", "\011comodidad", 
+ "\010pl\341stica\007pa\361ales", "\010borrarme", "\007dejaban", 
+ "\007robledo\007rellena", "\010castilla", "\007torneos", 0, 
+ "\006pierde\005autor", "\011homog\351nea", 0, "\010pl\341stico\010castillo", 
+ "\011despejada", "\012maternidad\004sac\363", "\005mande\014aprovechamos", 
+ "\015personalmente", 0, "\012decisiones", 0, "\010acciones", 0, 
+ "\010respaldo\011activados", "\013codificados\007gr\341fico", "\011monumento", 
+ 0, "\010diplomas\011consiguen", "\010cervezas", "\010anatom\355a", 
+ "\011dirigimos", "\006rinc\363n", 0, "\005dejar", 0, 
+ "\014presentaci\363n\007penales", 
+ "\011alimentos\012rapid\355sima\011biol\363gico\012lit\372rgicas", 0, 0, 
+ "\006cuidar\016probabilidades", "\013agron\363micos", "\013respectivos", 
+ "\007acceder", 0, "\012kil\363metros\007europeo\010profunda\011cantantes", 
+ "\011molestado", "\012conectando\010profundo", 0, 0, "\006trampa", 0, 
+ "\013maravillosa\006torero\007soriano", 0, 0, 0, "\006harina\007conocen", 
+ "\011operarlos", "\006compra\005bueno\017predeterminados", 0, 0, "\006cuerpo", 
+ 0, "\012americanos", 0, "\006correo\004vega\010maestros\007fluidez", 
+ "\007p\355ldora", 0, 0, "\006fueron\004ocio", 0, 0, "\011violentos\006museos", 
+ 0, "\010historia", "\011edilicios\012continuada", 0, 
+ "\010arreglos\006ten\355as", "\005tonos\010vaqueros\007lamento", 
+ "\007genital", "\006m\372sica", "\011edificios", "\007\363rdenes", 
+ "\006cocina\012publicadas", "\013promoviendo", 0, 0, "\006radios", 0, 
+ "\012plenamente", 0, 0, "\004neve\006detuvo", 0, "\012ejecutivos", 0, 
+ "\011masculino\014recomendados", "\010internos", "\011planteado\007reflejo", 
+ 0, "\005podes", "\013inmigrantes", "\013legitimidad", 
+ "\006sujeto\010farmacia", "\006decide\006tacita\006pulsar\006var\355an", 
+ "\007diploma", 0, 0, "\014provinciales\011biol\363gica\012voluntario", 0, 
+ "\010evitando\007doblada\011dirigente", "\014administrado", "\012infrarrojo", 
+ 0, 0, 0, 0, "\011anonimato", 0, 0, "\013tecnol\363gico\007expres\363", 
+ "\006d\351cimo\011destinada", "\007traduce", "\013oportunidad", 
+ "\011promoci\363n\006averno\010varadero\015estableciendo", 
+ "\005otras\010causadas", 0, "\012reparaci\363n\013dinosaurios", 
+ "\012perecedero", 0, 0, 0, 0, 
+ "\007lechuga\014restauraci\363n\005goles\011cobranzas", "\006dietas", 
+ "\007leyenda\006m\341gico", 0, 0, "\013computaci\363n\014irresistible", 
+ "\005otros\010estudiar\010humildad\005verbo", 
+ "\007certeza\014agropecuario\010copiador", "\005deb\355a", 
+ "\007cerveza\005actos\015renacentistas", "\011pesadilla\010preferir", 
+ "\012organizada", "\012sexualidad", 
+ "\013diccionario\012conexiones\014agropecuaria\007amorosa", 
+ "\013confluyendo\011saludamos", 0, "\005verlo", 0, "\012pluralidad", 
+ "\006acept\363", "\005falda", "\005tales\007estarlo", "\007remitir", 0, 0, 
+ "\011versiones", "\010estudian", 0, "\005falla", 0, "\016extraordinario", 0, 
+ "\012solicitado\005pod\355a\012ejecutivas", 0, "\013continental\007vecinal", 
+ "\005demos\012micr\363fonos\005falsa", "\005falta", "\007f\341ciles\004isla", 
+ 0, 0, "\016extremadamente", "\006evitar", "\006dejado", 
+ "\010controla\007interno\010llamarme", "\007enteros\006sill\363n", 
+ "\015transacciones\010alt\355sima", 0, "\007maestro", "\011invertido", 
+ "\010recaudar", "\007rapidez", "\010emitidos", "\011documento", "\007diarias", 
+ "\003sur\006frases", "\011retenidos\005mucha", 0, 0, "\011brillante", 0, 
+ "\006fiesta\007b\341sicos\012finalizado", 0, "\012regulaci\363n\004lima", 
+ "\011generales\007fuentes", "\007quiebra", "\010mantiene", "\004lime", 
+ "\011fundaci\363n\014establecidas\006te\361ida\007fuertes", 0, 
+ "\010cat\363lica\013recolecci\363n", 0, "\007coraz\363n\010destacar\005atrio", 
+ "\007favores", "\007porci\363n", "\006laicos\006usarlo\014consiguiente", 
+ "\010pol\355tico", "\012vacaciones", "\012cumpliendo", "\012interesada", 0, 
+ "\007guiados", "\006danzas", "\007juicios", "\007termino", 
+ "\005joyas\012resguardar", "\010pol\355tica\007copiado\012depuraci\363n", 0, 
+ 0, "\010promedio\004nuez", "\011editorial\017semiconductores", 0, 
+ "\010destacan\013dise\361adores\010salieron", "\010presenta", 
+ "\014implementado", "\010enfocada", 0, "\010presente", "\010ausencia", 
+ "\006gasc\363n\004hice", "\011afectuoso", "\006pasada\014tecnol\363gicos", 
+ "\004vale", "\012napolitana", "\004alza", 0, 0, 
+ "\010jornadas\014patrocinador", 0, 0, "\012utilizando", "\011renovadas", 
+ "\012garantizan", "\011asignaron", 0, "\007hispana", 0, 
+ "\005cobra\007estudia", 0, "\005est\341n", "\012paraguayos", 
+ "\014considerando\010orientar", "\003gol\005fatal", "\010petici\363n", 
+ "\010esconder", "\014diccionarios", 0, "\005est\351n", 0, "\005veces", 0, 
+ "\011gen\351ticos", 0, "\007aliento", "\014adolescencia\006emitir", 
+ "\005armas", "\005\351poca\006aprob\363", "\010monse\361or\007recorta", 0, 
+ "\007control\007tenemos\006servir", "\006salsas", 0, 0, "\010vestidos", 0, 0, 
+ 0, 0, "\010dep\363sito\012autom\341tica", 0, 
+ "\005dem\341s\007pesetas\014revelaciones\010remedios", 
+ "\007errores\004\351sta", "\006ciudad", "\011asesinato", "\016comunicaciones", 
+ "\004\351ste\010monturas", "\012cultivares", "\006fluida", 
+ "\015acondicionado\012instaurado", "\011categor\355a\011prescribe", 
+ "\007romanos", "\006cuenta\011enfocados\005poema", 0, 0, 
+ "\010raptores\011excluidos\004soga", 0, 
+ "\006cuesta\010poseemos\011desearles\007hacerla", "\012instalamos", 0, 
+ "\011asesinado", "\013solicitando", "\005lista", "\006basada", 0, 
+ "\013inal\341mbrico", 0, "\005usual", "\011olvidarse\007nadador", 0, 
+ "\015incrementando\011ortodoxos", "\006pasiva\013conteniendo", 
+ "\006conf\355a", "\007destaca\012conquistar", 
+ "\012eliminados\011personaje\005pacto", 0, "\006c\341mara\010decirnos", 
+ "\006contra", "\010admisi\363n\006cuadro", "\007jornada", 
+ "\006series\012distancias", "\004rod\363", "\010seguiste", 0, 
+ "\011digesti\363n\014determinarse", "\007ap\355cola\004puj\363", 
+ "\011imaginado", 0, 0, 0, 0, "\006virgen\007piletas", 
+ "\010reservas\012rect\341ngulo", 
+ "\010reservar\006mirada\011descubren\006serves", 
+ "\012tecnolog\355a\006cuatro\010present\363\005abril", 0, 0, 
+ "\003v\355a\007esconde\011feminidad", "\007gracias", "\015descomponerse", 
+ "\014construcci\363n\010logramos", 0, "\014beneficiar\341n", 
+ "\010creaci\363n", "\012transmutar", 0, 
+ "\004otra\011cabalgata\007imagina\012capacitado\010globales", 0, "\006veinte", 
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "\007rotundo", "\004otro\007remedio", 
+ "\010realicen", "\011redactada", "\011preguntas", "\011cartulina", 
+ "\012festivales", "\013intercambio\007deber\341n\014posiblemente\007pasamos", 
+ 0, "\004euro", "\006ganado\005trago", 0, "\006estopa", 0, 
+ "\006butaca\017relacion\341ndonos", 0, "\003sea", 0, 
+ "\012inaugurar\341\012optimistas", "\005kilos", 0, "\011herederos\006luchar", 
+ 0, "\006balc\363n\013operaciones\005trato\006reunir\007vicioso", 
+ "\011subscribe\005rezar\007vestido", 0, "\007f\363rmula", "\007opuesto", 
+ "\010carnaval\007besares\010saliendo", "\012especiales\004maya", 0, 
+ "\012incipiente", "\011regresada\011reservado\011suscritas", 0, 0, 0, 0, 
+ "\005texto\005dec\355a", 0, "\011necesidad\006coloro\012diputaci\363n", 
+ "\012estudiante", "\005sigue\012residentes", "\017presupuestarios", 
+ "\004mayo\012exclusivos\013productoras\010reafirma", "\013legislatura", 0, 
+ "\010aplicada", 0, "\007ataques\006perch\351", "\012disponible", 0, 
+ "\011visitarme\010cer\341mica", "\004vida\007reserva", 
+ "\005ca\355da\005venda\014indiferencia", 0, "\010factores\011zool\363gico", 
+ "\005venga", 0, "\010ventanas\011veracidad\011meriendas", "\012mortalidad", 
+ "\006venden\013ultrasonido", "\007caminos", 0, 
+ "\013cient\355ficos\007evaluar\007sumario\015pronunciaci\363n", 
+ "\010temporal", "\006cabina\006rollos", "\010llamarlo", 0, 0, 
+ "\005venta\014aplicaciones\006estaba", 0, 0, "\010modulada", "\007comunas", 
+ "\012diab\351ticos", 0, 0, "\007dispare", "\007pescado\013catolicismo", 
+ "\007ampliar\006montos\013prism\341ticos", 0, 0, 0, "\006volver\005gordo", 
+ "\012horizontal", "\010silencio", "\006escoge\007encanto", 0, "\005evita", 0, 
+ 0, "\010embarque", 0, "\010desaf\355os", 0, "\010estampar", "\005tomar", 0, 0, 
+ "\007decidir\006textil", "\005brazo", "\013instituci\363n\006sedujo", 
+ "\011interesan", "\011reservada", 0, "\005abuso", "\007vicar\355a", 0, 
+ "\011disfrutar\012anteriores\007realice", "\005deseo\014p\372blicamente", 0, 
+ "\010auditiva\014sorprendente\007m\372sicos", 
+ "\015investigaci\363n\007pintada", "\007menci\363n", 
+ "\012exclusivas\011regalarte", 0, "\007idiomas", 0, 0, "\005mundo", 
+ "\013efectividad\015instalaciones\011descubrir\006entera", 
+ "\017estadounidenses\015desarrollar\341n", "\007asesino", 
+ "\005v\351ase\013compatibles\013dimensiones", "\011aplicados", 
+ "\007m\351dicas", 0, 0, "\012informarte", "\006toques\011invierten", 0, 
+ "\004seas\012distribuye\011cer\341micas", "\013confirmando", 0, "\007rodajas", 
+ "\006lucida\014confirmaci\363n", "\007retorno", 0, "\007cuidada", 
+ "\017telef\363nicamente", "\010renuncia\005lente", 
+ "\010pisteros\011rebotados", 0, 0, "\012organismos", 0, 
+ "\010familias\012provocados\010renuncio", "\010familiar", 0, "\010detectar", 
+ "\007elabor\363", "\012superiores", "\013alimentador", 0, 0, 0, 0, 
+ "\006callao", "\014participante", 0, 0, "\010academia\006envase", 
+ "\013sustentable\010concedi\363", "\012droguer\355as\007orillas", 0, 0, 
+ "\006portal\006figure", "\007ventana\014inteligencia\006almeno\011viceversa", 
+ "\006misi\363n", "\006ingl\351s\012infantiles", "\010pariente\005har\355a", 0, 
+ "\007desaf\355o\011prestador", 0, 0, "\004arco", "\011concedida", 0, 0, 
+ "\005triad", "\012territorio\011regulares", 
+ "\010insertar\013maquinarias\004jara", "\007maligna", "\012bienvenida", 
+ "\016medioambiental", "\011pont\355fice", "\011a\361adidura", 
+ "\014recuperarlos\014masculinidad", 0, "\006suerte\011parrillas", 
+ "\005ven\355a", 0, "\013secuestrado", "\007lideres", "\004odas", 
+ "\015constructoras", "\014individuales\007valijas\012etiquetado", 
+ "\007choques", 0, "\015constructores", 0, "\012patriarcal\015quimioterapia", 
+ "\011purpurado", "\010quedamos", 0, "\006verdad", "\003oye\005lados", 
+ "\012participas", 0, "\012consultado", 0, 0, "\013ministerios\012incremento", 
+ "\010ej\351rcito", "\007canales\011brit\341nico\006diablo\012candidatas", 0, 
+ "\010alquilas", "\010alquilar\016reconciliaci\363n", "\006carnes", 
+ "\012liberarlos\010concluye", "\005topes\013respondemos", 
+ "\012inmediatos\014significante", 0, "\007milenio", 
+ "\010agregado\011criaderos\011bromeando\015certificaci\363n", 
+ "\007familia\007exist\355a", 0, "\007detecta\013conscientes", 
+ "\012perjudicar", 0, "\011reflexi\363n\007asisten", "\005ganar\010femenina", 
+ "\013subtitulada", "\011molestias", "\011compramos\013movimientos", 0, 0, 
+ "\010femenino", 0, "\005hielo", 0, "\004kilo", "\006l\363gica\010soldados", 
+ "\004ir\341n\013automotores", "\011utilizado\006ignora", 
+ "\014credenciales\005tiran\006mostr\363\005viral", 0, 
+ "\006r\355gido\007pudimos\004goma", 0, "\007afirmar", 
+ "\005tanta\007dejamos\013decodificar", 0, 
+ "\007espejos\011merecemos\016extraterrestre", "\005plano", 
+ "\005dulce\013denunciados", "\012suficiente", "\005rules\005modos", 
+ "\012telef\363nico", "\015psicoan\341lisis", "\005plato\007inserta", 
+ "\011adherimos", "\010esquemas", "\007cotizan", 
+ "\014coordinaci\363n\013inexistente", "\005prior", 
+ "\005plazo\014trabajadores", 0, "\007bloqueo\007enviar\341\012conseguir\341", 
+ 0, "\007periodo", 0, "\013terminaci\363n", 0, "\011presidida\007ajustes", 0, 
+ 0, "\010confiere\011produzcan", 0, 0, "\012entregados\004coca", 
+ "\012escarpados", "\011conocedor", 0, 
+ "\003a\361o\006recib\355\017acontecimientos", 0, "\003van\006camera\004maza", 
+ 0, "\013comerciales", "\012entregamos", "\012acompa\361ada", 
+ "\012contrase\361a", 0, 0, "\004coco", "\010dignidad", 0, "\006compro", 
+ "\007plantas\013medicinales", "\017internacionales\011declarado\007urbanas", 
+ "\005autos\006gestos", "\006libros\010consiste\012aprobaci\363n", 
+ "\007tanques", "\014purificaci\363n", "\011favoritos", "\011aprovecha", 
+ "\010indiques", "\012esp\341rragos", "\010gratuita", 0, 
+ "\010preparar\007m\351ritos", "\011afiliarte", 0, 0, "\005placa\010gratuito", 
+ "\013trayectoria\007olvides\004tuvo\012millonario", 0, 
+ "\012corporales\014aguardientes", "\006entrar\011psic\363logo\011favoritas", 
+ "\010exterior\005circo", "\007soldado\007juliano", 0, "\010compa\361\355a", 0, 
+ "\011reconocer", "\011analistas\005cirio", 0, "\013frustraci\363n", 0, 
+ "\010archivos\011cartelera\011inscribir", "\006margen", "\005plata", 
+ "\006editar\006tomada", 0, "\015comercializar", 0, 
+ "\013computadora\007amplias\005playa\011tensiones", 
+ "\006medios\005plaza\015publicaciones", "\012provincias\010preparan", 
+ "\006marcha", "\011impecable", 0, "\006c\355vico", "\005labor", 0, 
+ "\014inexistentes", 0, 0, 0, "\013financieros", "\006amulen", 
+ "\005cuero\006arcano\010peligros", 0, "\010galpones", 
+ "\011encendido\007vivamos", 0, "\005conde", "\003han", 
+ "\010concluy\363\006mueren", "\016desvinculaci\363n\006divina\007editado", 
+ "\013interpretes", "\016reconocimiento", "\007vivimos\010operador", 0, 
+ "\010epifan\355a", 0, "\007cocidas", "\005suman\015desarrollando", 
+ "\013conservando\005rumbo\010ejercita\011sentencia", 0, "\005ba\361os", 
+ "\010mantenga\011expresi\363n\011procesado\010generada", 0, "\007esquema", 
+ "\013composici\363n", "\011servicios\013herramienta\007infante\011infinidad", 
+ 0, 0, "\010ejercito", 0, "\007virtual\014aportaciones", 0, "\006usados", 
+ "\011tel\351fonos\006torres", 0, "\007indique\010expenses\013persecuci\363n", 
+ "\007abuelos\006formar\014protecciones", "\007ayudado\007uniones", 0, 
+ "\005poder\016distribuidoras", "\006armado\010autoriza\011adjuntado", 
+ "\006horror\011instancia", "\005cuota\004ayer", "\011provisi\363n", 
+ "\003m\355o\007threads\012ordenaci\363n", "\005llamo", "\011ocupaci\363n", 0, 
+ 0, "\004a\361os", "\007semanas\006n\372cleo", 0, 
+ "\011compa\361\355as\007per\355odo\010intrusos\012introduces", 
+ "\016modificaciones", 0, 0, "\007archivo\011circuitos", "\012orientador", 0, 
+ "\006ajenos", 0, 0, "\006masaje", "\011removerse", 0, 
+ "\012apost\363lico\013programadas", 0, "\013habilitados", "\010indeciso", 
+ "\014computadoras", "\012convierten", "\006noches\006puerto", 
+ "\006puesto\010episodio\007teatros", "\006postal\006acceso\012orientales", 
+ "\012noticiario\012fragancias", "\011ambientes", 0, 
+ "\010atacante\011convierta", "\010impacten\005yerba", 
+ "\010mientras\007ordenar\015permiti\351ndote", "\007refiere\007peligro", 
+ "\010billetes", "\007b\341sicas", 0, "\011jugadores", 0, "\010combinar", 0, 
+ "\006regala\010demandas", "\005viaje\005talar", 0, 
+ "\013prestadores\010interfaz", "\017computacionales", "\006nombre", 
+ "\006brinda", "\012incre\355bles", "\006pagine\010medieval\010recargos", 
+ "\007escolar\007jugador", "\011pastillas", "\007veremos\015autoservicios", 
+ "\005calle\006agosto", 0, "\014herramientas\007brindis", 
+ "\013restringido\010suicidio", "\012incorporar\011sentirnos", 0, 0, 0, 
+ "\010agencias\010animales\006regula", 0, "\011individuo", 
+ "\005moved\005oreja", 0, "\016administraci\363n\012actualidad", "\007expense", 
+ 0, "\011marruecos", "\004sois", "\004mira", 0, 0, "\006manera", "\004mire", 
+ "\006oferta", 0, "\012representa", "\005dates", 0, "\006libido", "\006graves", 
+ "\007tequila\013defendiendo\012avellaneda\006ajenas", 
+ "\006cierra\013experimento", "\010reunidas\004miro\012contemplar\006vecino", 
+ "\007similar\010conmueve", "\010senderos", "\011mud\351jares", 
+ "\005datos\004dada", 0, "\007abrazar", 0, "\005pixel", 
+ "\007celular\011certifica", "\010formatos\012ofrecerles", 
+ "\007robados\015acostumbrados", "\005secta", "\007simular", 0, 
+ "\010vendemos\015consolidaci\363n", 0, "\013empresarias", 
+ "\013int\351rpretes\004dado", 0, "\012educativos\011episodios", 
+ "\010quiniela", 0, 0, 0, "\005puros", "\007billete\011ralladura", "\005sumar", 
+ "\012familiares\010potentes\016rehabilitaci\363n", 0, "\006chorro", 
+ "\010empresas\010econom\355a", "\003ese", "\012comenzar\341n\007centran", 0, 
+ "\007demanda\006perd\363n\011conductor", "\007combina", "\010ilustran", 0, 0, 
+ "\012resultados\005dotar", 0, 0, 0, 0, 0, 0, "\006rutina\006tribal", 0, 0, 
+ "\005doble\006ciento\006siendo", 0, "\015procedimiento\007dossier", 0, 
+ "\011compilado\006cierto\011fotocopia\005arena", 
+ "\007lugares\016presentaciones", 0, 
+ "\007agencia\011depresi\363n\014desconfianza", 0, "\014manipulaci\363n", 
+ "\013localidades", "\006sufren", "\010estrenos", "\011controlan", 
+ "\012importados", 0, "\006piensa\017institucionales\013pretendemos", 0, 0, 0, 
+ 0, 0, "\012frecuentes", "\006palmas\005ver\341n", "\013jugueter\355as", 0, 
+ "\011discusi\363n", "\005opone", "\010inversor", 0, 
+ "\007teor\355as\014insostenible", "\005igual\006poseer\006detr\341s", 0, 
+ "\012cognitivas", "\007cuadras\006se\361ora", "\007equidad\012comprometo", 0, 
+ 0, 0, "\007formato", "\006mejora\005cupos\006ajusta", 0, "\006cadena", 
+ "\005cisco", 0, 0, "\006pelota\006rondar", "\015perseverancia", "\007quedado", 
+ "\006copias", "\004da\361e", 0, "\006tomate\011actuaci\363n", "\005misma", 0, 
+ "\013purificador\011celebrada", 0, "\005antes\005final\007potente\004puma", 0, 
+ 0, "\004da\361o", 0, 0, "\010creyente", "\006fuegos\016certificadores", 0, 0, 
+ 0, 0, 0, 0, "\011contenido\012convenci\363n", "\007federal\010japonesa", 
+ "\015municipalidad", "\012referencia\010suscitar", 
+ "\012elaboradas\010proponen\011agregando\007remueve", "\011deportiva", 
+ "\006existe\011atractivo\006estar\351", "\014consultorios", 0, 
+ "\007celebr\363\006\341nimos", "\006esp\355as\010restante", "\006termas", 0, 
+ 0, 0, 0, 0, "\010pulgadas", 0, 0, "\016interferencias", "\010antiguos", 
+ "\011inyecci\363n\004luce\005avala", "\007empresa\004cabe", "\003mis", 
+ "\010peque\361os", 0, 0, "\011provienen\015clasificaci\363n", "\010eliminan", 
+ "\011reembolso\007pueblos\012fabricados", "\013agradecemos", 
+ "\007naranjo\011exquisita", "\004cabo\006nombr\363\006sirvan", 0, 
+ "\007activos\005pidi\363\012terminando", "\007granjas", 
+ "\006dise\361e\004seco\013convivencia", 0, 0, "\011ahondando", "\004dura", 
+ "\012procesador\011restringe\011creativas", 
+ "\006driver\011reducci\363n\012ejemplares", "\006pod\355an", "\011reactores", 
+ "\011entregar\341", "\007podr\355an\011realizada", 0, 
+ "\012industrial\006sobrio", "\011expuestos", 0, 0, 0, "\007estreno", 
+ "\004duro\011redacci\363n", "\011creativos", "\010eliminar\012aprovechar", 
+ "\007tumores\007estad\355a\011reducirlo", 0, "\011aprobaron", 
+ "\012explorador\007respeto", "\005villa\006atrapa", "\007engrase", 
+ "\011finalizar\015anteriormente\011acompa\361ar", "\011fallecido", 0, 
+ "\007salones", "\006manual", "\005foros", "\005entra", "\007revenda", 
+ "\007realiz\363\011creyentes", "\010compleja", "\005torta", "\006bolero", 0, 
+ "\007triunfo\012terrorista", "\015desarrolladas\006ferias", 0, 0, 0, 
+ "\012castellano", "\004bobo\011parecer\355a", 
+ "\011controlar\004aren\015sucesivamente", 
+ "\011profesora\007enfriar\007suscita", "\007propone\015violentamente", 
+ "\010complejo", 0, "\012delegaci\363n", 0, 0, 
+ "\013restrictiva\013comunicarte", "\005nunca\006vistas", "\007rastreo", 0, 0, 
+ "\007propuso", "\015desinteresado\007mitades\007japones", "\007ocurrir", 
+ "\007antiguo", "\006repaso", "\004soja\004oval", 0, "\005gamma", 0, 
+ "\010traducir", 0, 0, "\006tintas\010artritis", 
+ "\011intenci\363n\006agente\012comprarlos", 0, "\014acostumbrado\006acepta", 
+ 0, "\013confeccione", 0, "\006entero", "\014conspiraci\363n\010descifra", 
+ "\007volumen", 0, "\007reenv\355e", "\004misa", "\017preferentemente", 
+ "\004faja", "\012conciencia", "\010vuestras", 0, "\010percibir", 
+ "\011vendiendo\012residencia\012transmitir\006cobran", "\011log\355stico", 0, 
+ "\010estimado\010colorado\005macho\010anticipo", 
+ "\013c\363modamente\006pod\351is", "\011presentar\010dictamen\010estimada", 0, 
+ "\007elimina", "\006afuera\007peque\361o\006abonos", "\011abriremos", 
+ "\005forma\010necesite", "\005horno", "\010aburrir\341", 
+ "\006ocurre\005macro", "\010necesita\013subtitulado", "\006tropas", 
+ "\012directorio", "\013odont\363logos\006gusano", "\010ex\341menes", 
+ "\015fundamentales\005gruta", "\005busca\010necesito", 0, "\013interesadas", 
+ "\007treinta\010ansiosos", "\006vendr\341", 0, 0, 
+ "\015participaci\363n\005vital\005titan", 0, "\010reclamar", "\007visitan", 0, 
+ 0, "\007encarga\010compacta", "\006mejor\363\011cotidiana\005runas", 
+ "\010empleado\015documentaci\363n\006nuncio", 0, 0, "\012puntuaci\363n", 0, 
+ "\007obtenga\012recomienda", 0, 
+ "\004real\007efectos\005oeste\006impone\010ocuparse", 0, 0, "\004aval", 0, 0, 
+ "\013alojamiento\010overflow", "\005eleva", "\007escocia\006presas", 
+ "\011caballero", "\010leg\355timo", "\006quieto", 
+ "\010panorama\013configurado", "\013posibilidad\007novelas", 0, 
+ "\011comunicar", "\005mente\011explicado\011eliminada\010provocar", 
+ "\013emocionante", "\010cohesi\363n", "\006elipse", "\006quinto", "\004cupo", 
+ "\006probar\010vendedor\013inesperados", "\011conquista", 
+ "\004pa\355s\017desconocimiento", "\010objetivo\013restaurante\007evasi\363n", 
+ "\007sembrar", 0, "\006pedazo", "\011cataratas\014reproductiva", 
+ "\012habitantes", "\011primarios", 0, 0, "\012eliminarlo", "\011radicados", 
+ "\011sugerimos\010provocan", "\006abonas\014restrictivas", 
+ "\007alumnos\011eliminara\012comenzamos", "\013providencia", 0, "\006pampas", 
+ "\005tapas", "\013proporciona\006acotar", "\007rastros", 
+ "\013relacionada\010empiezan", "\005tapes\015profundamente\010nefastos", 0, 
+ "\006enviar", "\013pontificado\012parroquias\010ayudando", "\011encargado", 
+ "\016financiamiento", "\004toma", "\014coincidiendo", 0, "\014rentabilidad", 
+ "\004rusa\007armon\355a\004tome\010forenses", "\007arrugas\012provocando", 
+ "\017trascendentales\007marines", 0, 0, 0, "\011dif\355ciles\014personalidad", 
+ "\003ojo\015configurarlos", "\010despedir", "\011aterrador\010adopci\363n", 
+ "\004tomo", 0, 0, "\012verificado", 
+ "\006postre\007vuestra\005lanz\363\004ruso", 
+ "\013necesitados\020administraciones", 0, "\004gama", 0, 0, 0, 
+ "\011comprando", "\007relieve\010recortes", 0, 0, "\010licencia\006conoce", 0, 
+ "\006siglas", "\004poca", 0, "\010avisarte", "\006decida", 0, 
+ "\010impulsos\005regla", "\014participar\341n", "\012triplicado\010embalaje", 
+ 0, "\010destinos", 0, "\010mantener", 
+ "\006examen\013instant\341nea\012much\355simos\013controlador", 
+ "\011compuesto\012conceptuar", "\004poco\007editada", 
+ "\010enemigos\010aut\363noma", "\005copie\012equilibrar\013tributarias", 0, 0, 
+ "\007evitado", "\011nocturnal\011exactitud", 0, 
+ "\014restaurantes\012mezcladora", "\004hora\005ganas", 
+ "\013preguntarte\010milagros", "\007cabello\007citadas\010aut\363nomo", 
+ "\012autorizada", "\006cheque\005ganes", 0, "\010aquellos\004sede", 0, 
+ "\012incidencia\014alojamientos\014bibliograf\355a", "\010consigas", 
+ "\012costumbres", 0, "\013redise\361amos", 
+ "\011exclusive\006rentas\006ventes\010rehogada", 0, 0, "\017hispanohablante", 
+ "\010proceden", 0, 0, 0, "\012calendario", "\010aseguran\014consecuencia", 
+ "\007provoca\010balances", "\011diciembre", 0, "\010orienten\005telas", 
+ "\003ajo\012cristianas\007guardar", "\010monta\361as", "\012peticiones", 
+ "\014proporcionan\013comparaci\363n", 0, 0, 0, "\010asegurar", 0, 
+ "\007empieza\007caminar", 0, "\005enano\005posee", "\006oculta", 0, 
+ "\006pueden\010paquetes", "\005vuelo\005vegas\006tarifa", 
+ "\011referente\012asegurando\007c\355trico", "\006merced", 
+ "\014reemplazar\341n", "\017infraestructura", "\014relacionadas", 
+ "\004boca\010colorear\012productiva", 0, "\006tomado", "\012consumidor", 
+ "\010posici\363n\007ruptura", "\012aut\351nticos\010probados", 
+ "\013componentes\007recorte\011licencias", "\013estabilidad", 
+ "\010cortes\355a\014anticipaci\363n", "\014recuperaci\363n", "\006n\372mero", 
+ 0, "\012much\355simas", "\010cerquita", "\005checa\012realidades", 0, 0, 
+ "\007hubiese", 0, "\011trabajado", 0, 0, 0, 
+ "\004ojos\011remitente\007enemigo", "\007meditar\010santidad", 0, 
+ "\005gasto\011resuelven", "\015disposiciones\010salte\361os", 
+ "\010cerrados\006abrazo", "\013dependencia", 0, 0, "\005canje\007milagro", 0, 
+ "\010arranque\010sediento", "\007hacerle", 0, 0, 0, "\010borrarse", 0, 
+ "\011inscribas\011parecidos", 0, "\004tom\363", "\007dirigen\012entusiasmo", 
+ "\007agendas", "\010sucesi\363n", "\007destino\005lagos\012inviolable", 
+ "\011camarones", "\007recrear", "\012actualizar\013inteligente", 0, 0, 
+ "\005saben", "\007balance", "\007procede", "\011convirti\363", 
+ "\005gimen\013interesar\355a", "\012asesorarlo\005emule\004vela", 0, 
+ "\011chocolate\006camilo\006avance\005juega", 
+ "\005estar\006agarre\013presentados", 0, 0, 0, "\007tribute\011aparecer\341", 
+ "\003che\006tienes\011liderazgo\007adentro", "\010ciudades", 
+ "\007oriente\007asegura", 0, "\010reserves", "\006poroto\005rural", 
+ "\004velo", 0, 0, "\007colecta\005nueve\005beb\351s\007t\355picos", 
+ "\007consiga\010prevista", 0, "\006piezas\010previsto\006fueran", 
+ "\007solista", "\011sustituir", 0, 0, "\011renunciar", 
+ "\007imagine\011detecci\363n", "\012publicamos", "\007probado", 0, 
+ "\012mutuamente", "\012respuestas\007monta\361a", "\013intervienen", 
+ "\013significado", "\005dicha\012publicados", 
+ "\006acci\363n\010comprado\007huellas", "\006planes\011portugu\351s", 
+ "\012confrontar\013anunciantes", 0, "\012profesores\012pidi\351ndome", 0, 
+ "\007central\007puertas\005valle", 0, 0, 
+ "\010contacto\010p\341rpados\007mudamos", 
+ "\007dise\361os\010conocido\012compa\361eros\012esc\341ndalos", 
+ "\007paquete\012denominado\007le\361ador", "\013inoxidables", 0, 0, 
+ "\016complementario", 0, 0, "\006correr", 0, "\013activamente", 
+ "\012pertenecen", "\010esperado\006marino", 
+ "\015configuraci\363n\011identidad\014generaciones", "\006medica", 
+ "\011razonable", "\012visitantes", "\015instituciones\011sensaci\363n", 0, 
+ "\010asunci\363n", 0, "\010agr\355cola\006sombra\004aula", 0, 
+ "\010extra\361os", 0, 0, 0, "\013penetraci\363n\014configurarlo", 
+ "\013trascendido", "\007charlas", "\006frutos", 0, "\006fieles", 
+ "\007salte\361o", "\007g\351neros\012sumisiones", "\014dependencias", 0, 0, 
+ "\007cerrado\004tuya", "\003tal", 0, "\006saldr\341", 
+ "\006rese\361a\013preventivos", "\006serano", "\007gesti\363n", 
+ "\012encuentros", "\007reserve\010precioso", "\010infinito\006sereno", 0, 0, 
+ "\010contador", "\010derechos\010triunfar", "\007palacio\004tuyo\006facial", 
+ "\005uni\363n\007singles", "\004ruta", "\010receptor\005verla", 
+ "\011confluyen", 0, "\007tribuno", "\005adobe\010valencia\010ofrecida", 
+ "\011historias", "\012angustiosa", "\004debe\014inteligentes", 0, "\004tono", 
+ "\010ofrecido\010previene\015manifestaci\363n", 
+ "\013empresarios\012suplemento", 0, 0, "\010titulado", 
+ "\011ganancias\010restarle", "\003ah\355\004gana\010triunfan", 
+ "\005pagas\004debo", "\005vayan\006cortas\005creen", 0, 
+ "\005adore\004gane\011productor", 0, "\014deficiencias\007chasque", 
+ "\011inscrib\355s", "\011contactos\010abogac\355a", 0, 
+ "\007destac\363\010publique", "\011brindarle\012mentalidad", "\011brindando", 
+ 0, "\011caramelos", "\005pagos\004pode", 0, "\012abandonado", 
+ "\003sol\011farmacias\013consumiendo\011relajarse", "\013instrumento", 
+ "\007removed\012feministas", "\005becas", 0, "\006tienda\011ilimitado", 
+ "\005due\361o", 0, 0, 0, 0, "\011ciudadela", 0, "\005sabor\013diferencias", 
+ "\004cada\007reuni\363n", "\007desvela", "\010dulceros\011demuestra", 
+ "\011apellidos\005volar", "\007escriba\012convicci\363n", "\006recreo", 
+ "\011ambiental\005aguas\010haberles\014criminolog\355a", 
+ "\016individualidad\006aftosa", "\006perfil", 0, 0, "\014espectacular", 0, 
+ "\012encuentras", "\014sobrenatural", "\011enviarnos", "\007caba\361as", 
+ "\013interesaste\012colocarlos", "\010animados\007d\351ficit", 
+ "\016jurisprudencia", "\006espejo", "\007recoger\010desearle", "\006humano", 
+ "\012cotidianos", 0, "\013productores\013encabezados", "\013edulcorante", 
+ "\007derecho\005acoja", "\005temas", "\007vigilar\010negativo\005ferro", 
+ "\007precios\007expreso", 0, 0, "\007extra\361o\013consecuci\363n", 0, 
+ "\005visto\005robar\007firmado", "\006pasaba\011inserci\363n\013insistencia", 
+ "\004tale", 0, "\007mujeres", 
+ "\010ventajas\007premios\011reintegra\007olvidar", 0, 0, "\010hermanas", 
+ "\011actividad\010abiertos\013caracteriza", 
+ "\010l\355quidos\011siciliana\006\372nicas", 0, "\010aventura", 
+ "\013hipotecario", "\007previos", 
+ "\005fotos\004boda\010esencial\011valientes", "\007unieron", "\006piedra", 
+ "\010informes\007navarra", "\007popular\012consideran", 0, 0, "\007contado", 
+ "\007triunfa", "\012religiosos", "\007remeras", 0, 
+ "\011cobijar\341n\011palestina", "\007capilar\010gimnasia\011vivencias", 
+ "\005peces", 0, "\011centenera\012remitiendo\006emerge", 0, 
+ "\006emisor\012terrorismo\014instrumentos", "\010heladera", 0, 0, "\004java", 
+ "\011potencial", 0, "\012buscadores", 
+ "\012destruidas\010artistas\007t\355teres", "\007ofrecer\007gaviota", 
+ "\007tallado", "\014civilizaci\363n\006andina", "\011incorpora", 0, 0, 
+ "\004sola\010pregunte", "\007embalse", 
+ "\007haberle\013interpretar\011cat\363licas", 0, "\010pregunta", 0, 
+ "\005disco\012vacunaci\363n\010pregunto", 0, 0, 0, 0, "\016complicaciones", 
+ "\011cat\363licos", "\010hicieron", "\004solo", "\007\372ltimas", 
+ "\010presunta\016ocasionalmente", "\006guerra", "\010frontera", 0, 
+ "\006amplia\010alegr\355as\012necesarias\011receptora", 0, "\006castro", 
+ "\007iniciar", 0, 
+ "\010alquiler\010adecuada\010ofrecer\341\012realizamos\006eterna", 0, 0, 0, 
+ "\010comentar", "\011generador\011ausencias\020entretenimientos", 
+ "\006segura\011orientado\004gan\363", "\012realizados\010suscrita", 0, 
+ "\006solano", "\010adecuado\004falo", 0, "\011comunique", 0, "\007hermana", 
+ "\007abierto\011tranquila\012utilitario", "\012resentidos", 
+ "\005tarda\010suscrito\010postular", "\005tarea", 
+ "\010pr\341ctica\012individual", "\010gaseosas\006chiste\012pensadoras", 
+ "\011gimnasios", 0, "\006paloma\006caldas", 0, "\010formales", "\006turnos", 
+ "\005vidas\007inmensa", "\007informe", 
+ "\010iniciar\341\011existente\010pr\341ctico\006bailan", 0, "\010vertical", 
+ "\013decorativos", "\005parte\005tarta", 0, 0, "\005pleno", 0, 0, 0, 0, 
+ "\007ventaja", 
+ "\007ahorrar\014amplificador\006cupido\007inserte\011guarder\355a", 0, 
+ "\010conozcan", "\006cabeza", "\014colaboradora", "\007artista", 
+ "\012ocasionada\007piratas", "\005socio", "\007esencia\004cura", 0, 
+ "\006afirm\363", 0, "\004cure", 0, 0, 
+};
+
+
+
+#define word_tbl2_LEN 2003
+static const char *word_tbl2[2003] = {
+ "\007agencja\010bachorem", "\003sex", "\012komputerze\014przyjedziesz", 
+ "\007mroczny\007obecnie\010pokrywa\346", "\007rodzice", 
+ "\005bycie\002\277e\011wirtualny\010wykonany", "\004data", "\006latach", 
+ "\010zje\277d\277a\346", "\002de\007gwiazda", "\004biga\007wyra\277a\346", 0, 
+ "\006zdanie", 0, 0, "\012szyfrujemy", "\007musical\007posiada", "\006utraty", 
+ "\003moi", "\005pow\363d", "\005roman", 0, "\010godzinie\004plac", 
+ "\006dzia\263o\005pisz\352", "\010programu", 0, 
+ "\004ca\263e\006klasie\005place", "\003kit\007obawiam\004ocen", 
+ "\011internetu\011komputera", 0, "\005darek\004daty", 
+ "\006berlin\006gor\241ce", "\010kowalska\006s\263odki\006sprawa\006wyjazd", 
+ "\010indianin\004plan", "\014informacjami\006jedyne\007mieszka\003tym", 0, 
+ "\010konspekt\005rzecz", 0, "\003eto\011kolejnych\004papa", 
+ "\003cen\010pozwoli\346", "\010kowalski", "\011przyjazny", "\007egzamin", 
+ "\006legend\007naszymi\005serca", 0, "\005narta", 
+ "\004ca\263y\005pe\263n\261\010pracowa\346", 0, "\006ciebie\004jest\005wiesz", 
+ "\004nich\005seria\005wole\346", 0, "\007jedynej\010znacznie", 
+ "\006handel\006ochot\352", "\005ekran\012zapowiada\346", 
+ "\007gwiazdy\013ostatecznie\010posiada\346", 0, 0, 0, "\005twist", 
+ "\006blisko\013zaproszenie", "\011odwiedzi\346", "\012transakcja", 
+ "\005kart\352\004miss", 0, "\004hela", 
+ "\010problemu\007przemek\010tygodnie\005tylko", 0, "\007rozmowa", 
+ "\007godzina\004hele\010mieszka\346", 
+ "\003m\363j\007pracuje\010problemy\010tygodnia", "\006zakres", "\003pop", 
+ "\007podawa\346", "\005kinga", "\006niemcy", "\006domen\352\006mojego", 
+ "\007wyrazi\346", "\011wyj\261tkowy\007zakupie", "\005efekt\011szyfrowa\346", 
+ "\012powiedzia\263\007przej\266\346\005przez", "\007profilu", 
+ "\006pi\261tek\010tygodniu", "\011licytowa\346", "\010polecamy", 
+ "\007pytanie", 0, 0, "\007collage\003von\012wymieniony", "\006dajcie", 
+ "\010nadziej\352", "\005samym", "\004dola", "\010cz\352\266ciej\004jazz", 
+ "\005bywa\346\006muzyki\005nazwa", "\002z\263", "\004dole\006poczet", 0, 
+ "\007odpisa\346\007stycze\361", 0, "\007przyj\266\346", 
+ "\005nauki\013wystawienia", "\011m\352\277czyzna", 
+ "\004ca\263\261\004mo\277e", "\007cudowny\006lutego\007wyra\277am", 
+ "\010historia\006napisz", "\013listopadzie", "\004nikt", 0, 
+ "\006chodzi\010zasadzie", "\006kuchni\007program\006wygody", "\006swojej", 0, 
+ "\006chcie\346\010historii", "\014u\277ytkownik\363w", "\006przede", 0, 
+ "\006babcia\005je\266li\006ludzk\261\011po\266wi\352ci\346", 0, 
+ "\005ca\263ej\004mai\346\012nowoczesna", 0, "\003dam", "\007natalie", 
+ "\011bezp\263atny", "\006gad\277et\004leci", "\004kita", 
+ "\005order\006wys\263a\346", "\006kredyt", "\003den\004moim\011pistoletu", 
+ "\013elektronika\005sumie\015wykorzystania", 
+ "\005idzie\004kart\011nadrabia\346\006zasada", "\006bogdan\005wiemy", 
+ "\010zachowa\346", 0, "\005m\363wi\346\004show", "\004anka\007mn\363stwo", 
+ "\011potrzebny", "\004cena\003jak", "\005biuro\005jedn\261\003via", 
+ "\006kiedy\266", "\004tymi", "\004lecz\007mariusz\007problem", "\005sport", 
+ "\012informacji", "\010jedziesz\007miejsca\010niekiedy", "\003maj", 
+ "\014automatyczny\006cz\352sto\006market\004og\363\263\005stado", 0, 0, 0, 0, 
+ 0, "\006dziwny", 0, 0, "\014akceptowa\263e\266\005jerzy", 0, "\005story", 
+ "\003ich", "\010upominek", 0, "\004ceny", "\006liczba\004want", 
+ "\017administratorem\002el", "\007zmieni\346", "\007autobus", 
+ "\002oj\006radzi\346", "\012biblioteka", 0, "\010wybieraj", 0, 
+ "\011strasznie", "\007powrotu", "\012przyjaciel\002ty", 0, 
+ "\005kt\363r\261\007numerze", "\011krzysztof", "\007wymiana", "\011regulamin", 
+ 0, "\006dniach\006dobrze\007zapewne", "\006drodze", "\006prawie\010waldemar", 
+ "\005ko\361ca\007wakacji", 0, "\003ina\006udzia\263", 0, "\005konto", 
+ "\004my\266l\006talent", 0, "\006autora", 0, "\002za", 0, 0, "\007nazywa\346", 
+ "\003sum\006tysi\261c", "\010zdarzaj\261", 0, "\010wychodzi", 
+ "\005chora\006single", "\005opera", "\010nied\263ugo", "\006system", 
+ "\007godziny\003mit\010zak\263ada\346", "\003per", "\006afryka", 
+ "\006zdawa\346", 0, 0, "\013proletariat", "\013potrzebowa\346\006zosta\263", 
+ 0, "\002bo", "\011czynienie\006dziale", "\013najbardziej\010obejrzyj", 
+ "\006jeste\266\010naprawd\352", 0, "\004kraj", "\005swoim", 0, 0, 
+ "\007fajnych\012mieszkanie\007wymiany", "\010sposob\363w", 
+ "\006drugie\004less", "\006polska\005sta\263o", 0, 0, "\002es", 0, 
+ "\010napisane", 0, 0, "\006okazji\007wynosi\346", "\004jaka\010napisany", 0, 
+ "\010czwartek", "\003r\363\277\007stronie", "\005alina\006resume", 
+ "\003kim\010rodzic\363w\003te\277", "\010z\263o\277enie", 
+ "\007drugiej\012wsp\363\263praca", "\004jaki\004maja", 0, 0, 0, 
+ "\005jakie\007wszyscy", 0, "\004jako\005liczy", "\004chuj", 
+ "\005kt\363re\005ni\277ej", 
+ "\003dom\005jaka\266\006swoimi\005tobie\006\266wi\352ty", 0, 0, 
+ "\004czu\346\011usuni\352cie", "\005ci\261gu\007obs\263uga", 
+ "\006jakby\266\016korespondencja\004mila\005wejd\274", 0, 
+ "\007lu\274niej\010rachunku", "\002da\006empire\005jaki\266", 
+ "\003kup\004mile", "\005czasu\011dzia\263anie\005list\352", 0, 
+ "\006b\352dzie\015internetowych", "\006serwer\007terminu", 
+ "\005jako\266\006tr\363jka", "\010zaprosi\346", "\006stref\352", 
+ "\011nadrabiam\010sleeping\010stycznia", 0, "\004milo\006twarzy", 
+ "\006\263\261czy\346", 0, "\011doskona\263y", 0, "\011spokojnie\007sprawie", 
+ "\006fajnie\005nawet", "\005pytam", 0, 0, "\007fajnego\006pa\263acu", 
+ "\003m\363c", "\010poczucie\001u", "\010wynikiem", "\011najgorszy", 
+ "\005matka", "\010hipoteza\006zawsze\007zdaniem\010znajduje", 
+ "\004mara\007niczego", "\010styczniu", "\006filmie\005tomek", "\011niedziela", 
+ "\005iwona\003ono\006zabawa", "\005mo\277ny", "\006czytam", 0, "\006jedyny", 
+ "\005jasne", 0, 0, "\004byle", 0, "\004band\002co\005czech", "\006naszej", 
+ "\004byli\011otrzymasz\011pier\266cie\361\005praca", "\003got", "\004mars", 
+ "\006szkoda", "\013przypadkiem\005sesja", "\004bank\005nasza", 
+ "\002ad\005chory\010wybiera\346", "\005karty", 
+ "\004mary\005pada\346\011przyczyna", 0, 0, 
+ "\011chorwacja\004jak\261\010zasadami", "\007powinno\005start", 
+ "\006li\266cie", "\007kaliber", 0, "\013sprzedanych", 0, 
+ "\006grupie\011wyj\261tkowe", "\006handle\004maj\261", "\005prawa", 
+ "\013potwierdzi\346", "\010instytut", 0, "\010wskazany", 
+ "\011niedziel\352\006pogoda", "\005halle", "\012otrzymanie", 
+ "\003bon\005jak\261\266\006odpisz\006samych", "\003ile\005listy\004lord", 
+ "\007ogarnia", "\006jestem\004pisz", "\005skala", 0, "\003b\363g", 
+ "\005pisze\004r\363\277e", 0, 0, "\005prac\352", 0, 0, "\006sylwka", 
+ "\004cafe\011cholernie", "\012sprawdzian", "\003by\263\006czacie\003hol", 
+ "\013maksymalnie", "\006finale", 0, "\011kazimierz\005r\363\277ne", 
+ "\005d\263ugo\006europy\005ludzi", "\006godzin\010kilkuset", 
+ "\010podobnie\007uczucie", "\006d\263ugim", 
+ "\016elektronicznej\013rejestracji\005stylu", 
+ "\005gonna\006kinach\004kupa\007skr\363ci\346", 
+ "\010\266ci\261gn\261\346\007kafejka\003las", 0, 
+ "\005filmu\010wszystek\005wyraz", 0, "\007buziaki\005drzwi\007koleg\363w", 
+ "\003aby\006czasie", "\003run\005tytus", "\005druga", 
+ "\003ju\277\015niespodziank\352\013\246wi\261tecznej", 
+ "\004kasa\005kurwa\010ogarnia\346\010zaczynam\007zar\363wno", "\004domu", 0, 
+ "\007nigdzie\006oferta", "\006indian", "\004moja\012zak\263adaj\261c", 
+ "\005dw\363ch", "\006jakbym", "\016automatycznego\004kasi", 
+ "\004moje\006tamtym\011wspania\263y", 
+ "\011napoleona\012powodzenia\013przepraszam", "\013niezmiernie", 
+ "\015podpowiedzie\346", 0, "\010wiedzia\263\006wyrazy", 0, "\012pozdrawiam", 
+ 0, 0, "\010jeste\266my", "\007u\277ywany\006wielki", "\007kt\363rych", 
+ "\002do", "\005mogli", "\004kasy", "\012informacje", "\005czego\011uwielbiam", 
+ "\011niezgodne", "\006ramach", "\005rynek\005serce\010sierpie\361", 0, 
+ "\005beata\007inaczej", "\007kreator\003oka", 0, "\013rejestracja\007znajomy", 
+ "\007przybi\346", "\005oko\263o\010poniewa\277", "\010operacja\007pisa\263am", 
+ "\006czeka\346\005nowym", 0, 0, 0, "\005ilo\266\346\011wychodzi\346", 
+ "\005dalej", "\012dziewczyna", "\006dziwne\010operacji", 0, "\004koch", 0, 
+ "\006france\006kt\363rzy\014odpowiednich", "\006stara\346", "\005wok\363\263", 
+ "\004mi\263o\011przypadek\012rozumienia\006takich", "\011wycieczek", 
+ "\004kim\266", "\007podawaj\006stawa\346\005takie\002tu\012wydarzenie", 0, 
+ "\003top", "\006numeru", "\010urodziny", "\005jazdy", 
+ "\010argument\004bona\005kinie", "\004mi\263y", "\010zajmowa\346", 0, 0, 
+ "\007geniusz", "\005jutro\011koncercie", 0, "\006pojawi", 
+ "\011serdeczny\002wy\012zobaczenia", "\006staram", 
+ "\006mog\263am\007zale\277e\346", "\006leszek\010warszawa", "\005poda\346", 
+ "\007ciekawa\011pracujemy", "\006dobrej", "\004by\263a\005domen\011warszawie", 
+ 0, "\010skrzynce", 0, "\004hole\010rozdzia\263", 0, "\007dotrze\346\005musze", 
+ "\010promocji\004rejs\006zmieni", "\011animowany\004moj\261", 
+ "\003met\006ochoty", "\003bym\003par\005widz\352", "\010zawalona", 0, 
+ "\013przyjaciela\006szybki", 
+ "\004by\263o\013korzystanie\003nie\013przesy\263anie\001w", 
+ "\010b\352dziesz\010promocja\006swoich", 0, "\012dziedzinie\003eta\005facet", 
+ "\005grupa\011szczeg\363\263y", "\005uwagi", 0, 
+ "\014niesamowicie\007planeta\007szukasz", "\005gonny\007zaczyna", 0, 
+ "\004by\263y\006wydaje", "\003z\263o", "\010niebawem", 0, 0, 
+ "\005my\266l\352\005nowej\010zapisane", "\010brakowa\346\007dobrych\005kiedy", 
+ "\004runo\010zostanie", "\006chcia\263\006domeny\005pewno", 
+ "\011dzi\352kowa\346\005passa\010zapisany", 
+ "\004dane\010dost\352pny\005pasta\013zapobiegnie", "\010skrzynki\010zawalony", 
+ "\010edytowa\346\011najnowsza\002om", "\015wykorzystanie", "\005wiele", 
+ "\011odpowied\274\007relacja\006w\263adzy", "\005buntu\015potwierdzenia", 0, 
+ "\010dost\352pna", "\005humor\010skrzynka", 
+ "\006ciesz\352\003los\007pa\361stwo\006studia", "\010telefonu", 0, 
+ "\010sklepach", "\003but\012oczekiwana", "\006strony", 
+ "\006maciek\015przyjemno\266ci\261\013zapobiegn\261\346", "\006renata", 0, 
+ "\006odbi\363r", "\004dany", "\012bezpieczne\007kapitan", 
+ "\007napisa\263\010zaczyna\346", 0, 0, "\003nim", 
+ "\007kliknij\015niespodzianka", "\005czata\012uprawniony", 0, 0, 
+ "\012oryginalny\005tak\277e", "\006czasem\005zaj\352\346", 
+ "\006girlsa\010najpierw\007prosimy", "\005wobec", "\007czekamy\003tej", 0, 0, 
+ 0, "\007grzesiu\011osobowych", 0, 0, 0, "\006sylwia", "\007wygodny", 
+ "\006adresy", "\011najlepsze", 0, "\001a\011przesy\263ki", "\003kat\004mimo", 
+ "\011najgorsze", "\005d\263ugi", "\007niezbyt", "\010dokonuje", 
+ "\010kosztuje\006s\263odka\006sprawy", "\007allegro\011spotykamy", 0, 
+ "\003nas", "\006chwile", "\011dziedzina\007machina", 0, "\006edward\002je", 
+ "\007barbara\011gimnazjum\006prosto", "\017przys\263uguj\261cego\006trzeba", 
+ "\007imprezy\007kt\363rego\003net", "\005dosy\346\010momencie", "\005pewny", 
+ "\006rodzaj", "\002ot\007podobny\007zgodnie\011zje\277d\277amy", "\006wojtek", 
+ "\005\277ycie", 0, "\005wolny", "\004meta\007wiecz\363r", "\004para", 0, 
+ "\002mi", "\010odebrana", "\013zastanawia\346", 
+ "\015elektroniczny\007komedi\352", 0, "\006mo\277emy", "\006jednej", 
+ "\006ludzki", "\006lecie\346\004park", 0, "\011gwarancj\261", 
+ "\006dost\352p\004niej", 0, "\003kod\006wpisa\346", "\003kto", "\004masz", 
+ "\010jedziemy", "\007piotrek\013praktycznie\007wielkie", 
+ "\005grupy\005uczy\346\005ziemi\007zwi\261zek", 
+ "\010gazowego\005wasze\003wir", "\004etap", 
+ "\005\263atwo\003noc\007prezent\011rodzicami\007telefon", "\007zrobisz", 
+ "\013komunikator\011wcze\266niej", "\007ciekawy", "\010odebrany", 
+ "\005ca\263ym\005stary", 0, "\010gabriela", "\004jad\352\006nale\277y", 
+ "\006edytuj", "\006kamila\003por\005umie\346\005wirus", 
+ "\005misiu\005nasz\261", "\007impreza\004oraz", "\006wy\266lij", "\004eden", 
+ "\011najnowszy", "\006dobrym\004oka\277", "\005jakby\005stron", 
+ "\002by\006jakiej\004kina\006muzyka", 0, 0, "\007jolanta\010szybkiej", 
+ "\006akurat\010kilkaset\006kt\363rej", 0, "\004king\007zabawne", 
+ "\006alicja\016bezpiecze\361stwo", "\011doskona\263\261\004mie\346", 
+ "\002\257e\005rafa\263", "\002i\263\005kt\363ry\003tez", 0, "\010skrzynk\352", 
+ "\002s\261", "\006gdybym\004kino\011usuni\352cia", 
+ "\003ala\007twojego\011uwielbia\346", "\004nimi", "\005adres", "\007instant", 
+ "\002ha\006miasta", "\003mad\012pierwszego", "\004buty\011dzia\263ania", 
+ "\003lat\005tutaj", "\007wielkim", "\005bilet\007przybij", "\012nowoczesny", 
+ 0, "\006wygoda", "\004jola\006list\363w", "\004lada", 0, "\005willa", 
+ "\006ba\263tyk\005chata\004hurt\004trwa\010upominki\007zajrzyj", 
+ "\006ilo\266ci", "\010lotnisko\012powodzenie", 0, "\012wystarczy\346", 
+ "\006porady\011przela\263em\005spraw", 0, "\005swoj\261", "\007przejd\274", 
+ "\006reklam", 0, "\011pierwszym\010przecie\277", "\005laura", 0, 0, 
+ "\004nasi\006zach\363d\006zasady", "\005czyta", "\012informacja\002ni", 
+ "\010szczeg\363\263", 0, "\006wtorek", "\002iz\004lady", "\011umo\277liwia", 
+ "\005innej\015skonfigurowa\346", 
+ "\006bilety\005jasny\006szko\263y\006wektor\013zastanawiam", 
+ "\003jam\006propos", "\013kontaktowa\346\002t\352", "\004prac", 
+ "\006dzia\263a\010lotnisku", "\002go\005numer", "\013dodatkowych", 
+ "\005prace", "\004nasz\010sytuacja", "\006innymi", "\006prosty", 
+ "\004bogu\010zbigniew", 0, 0, "\011sprawdza\346", "\004koda", 
+ "\010sytuacji\005wynik", "\014komunikatory", "\007dzia\263a\361", "\005witka", 
+ "\012rozumienie\006szukam", "\013samochodowe", 
+ "\011autorstwa\006op\263ata\011sprawdzi\346", "\007machiny\003min\003\243zy", 
+ 0, 0, "\011katamaran", "\006podane\003raz", "\007gabriel\004zbyt", 
+ "\010wreszcie", "\006aukcji", "\006lepsze\010pojecha\346\015ubezpieczenie", 0, 
+ 0, "\005palma", "\004mnie", "\005listu\011odpowiedz", "\011najni\277szy", 
+ "\012zobaczenie", "\003dwa\007p\363\274niej", 
+ "\006format\004par\352\006stereo\010wyjazdem", "\011zaprasza\346", 
+ "\005tekst", "\006buziak\005sobie", "\004most", 
+ "\011pozostaje\002t\261\006trzech", "\005mucha\012rekrutacji", 
+ "\007dziecko\012marketingu\004nocy", "\006chcesz\004klub", 0, 0, "\004teza", 
+ "\003nad\007pid\257ama\010wype\263nij", 0, "\005wahaj", "\011elizabeth", 0, 
+ "\004pory", "\011katarzyna", "\006je\277eli", "\004kola", 
+ "\006koniec\004mada", "\003pas\006siedzi\005zaraz", "\004lata", 
+ "\013pozdrawiamy", "\003ona\006polowe", "\004homo", "\005konta\003si\312", 
+ "\014otrzymywanie\007pid\277ama", "\004alan\003pet", "\003dog", 0, 
+ "\014otrzymywania", "\007postawa", "\007dzwonek\004kolo\001z", 
+ "\006mi\263o\266\346\006polski", "\013przychodzi\346", "\010aktualny\004lato", 
+ "\004poza\010pozna\263am", "\011ciekawego\006pewnie", "\010wystawie", 
+ "\010aktualne\007markowy\011rozdzia\263u", 0, "\010kontrola", "\006tapeta", 
+ "\005ferie", 0, "\011aktywacja", "\006jecha\346", "\010internet", 
+ "\007ofercie", "\010kontroli\007wygl\261da", 0, "\005chyba", "\005banan", 
+ "\005wraca", "\004gang\003lot\005tre\266\346", 0, 
+ "\004beat\006dzieje\004jama\012korzystasz\011niezgodny\011przynie\266\346", 
+ "\010protok\363\263\005wideo", "\011agnieszka", "\004kto\266\004pies", 
+ "\005narty\012wsp\363\263pracy", "\007zamiast", "\011francisco", 0, 
+ "\016korespondencji", "\005innym\005manna", "\007chodzi\346", "\006musimy", 
+ "\003oni", "\007odezwij", 0, "\006kartki\006szkole", 0, 
+ "\007bankowy\006chwil\352", 0, 0, "\005kwiat", 0, "\004mina\006takiej", 
+ "\010wygl\261da\346\010zrobi\263am", "\006jednak\003lub", "\006tomasz", 
+ "\005maili", "\006koncie", 0, "\003ewa\004robi", 0, 
+ "\010czerwony\007wroc\263aw\011wyrazi\263e\266", 0, "\004kuba", 0, 
+ "\010formacie\005za\263\363\277", "\006dotrze\013wszystkiego\007wystawa", 
+ "\016prawdopodobnie", "\006chwila\005razie", "\011buziaczek", 
+ "\006skaner\010wszystko", "\002ja\013natychmiast", 0, 
+ "\006dorota\011serdeczne", 0, "\005blues\002er\011normalnie\014uproszczonej", 
+ "\007licytuj\004razu\007wniosek", 
+ "\011magdalena\012nawi\261zanie\011orkiestra\005zimno", "\006wed\263ug", 
+ "\006naszym\013odpowiednie\004seks\012zapraszamy", "\004razy", 0, 0, 
+ "\004font\010pami\352taj\005skoro\007szykuje", "\010pami\352tam\006zabawy", 
+ "\007projekt", "\005jarek\002me\005musza\010przes\263a\346", 
+ "\003red\003typ\007wstawi\346", 0, "\007cieszy\346\005nauka", 
+ "\003ann\005punkt", "\004b\352d\261\013uprawnienie\006w\263asne", 
+ "\005us\263ug", "\010przyk\263ad", "\013kosmetyczne\011przyjdzie", 
+ "\011pocztowej\010\277yczenia", "\010przycisk\005ultra", 0, 
+ "\011przeboj\363w", "\010\277yczenie", "\010wype\263ni\346", 0, 
+ "\006opr\363cz\005trakt", 0, 0, "\005rynku", 0, "\005zdj\352\346", 
+ "\003co\266\010drugiego\006pewnym", "\004oslo\004pass", 
+ "\014dostarczenie\004past\004step", 
+ "\007grudnia\003id\352\012odczytywa\346\011zapraszam", 0, 
+ "\010listopad\005mowie\007trwania\006witold", "\007pozwala\005roni\346", 
+ "\010zaprasza", "\005droga\011konieczny", "\004dwie\007praktyk\005wcale", 
+ "\006farmer\006twarz\261", "\007udzia\263u\005video", 
+ "\011kontaktuj\015potwierdzenie", "\011wspania\263e", 
+ "\007decyzja\007t\352skni\346\005warto", "\005braku\006okresu\010serwisie", 
+ "\004cho\346\012faktycznie\005firma\013najlepszych\011planujesz\006us\263ugi", 
+ "\003rak", "\007czerwca\007napisz\352", "\006marzec\006morzem", 0, 
+ "\007napisze\012skorzystaj", 
+ "\013autoryzacji\006jednym\005lipca\006pomoc\261", 0, 
+ "\007dzisiaj\005\277ebym\013wirtualnych", "\004zr\363b", 0, 0, 0, 
+ "\007w\263a\266nie\006zwykle", 0, "\005dobre\010wierszyk", 
+ "\003aha\004nina\010pozwala\346", "\011marketing\006wyboru\006zakupy", 0, 
+ "\011kontakcie\004skal", "\007otrzyma", 
+ "\004b\261d\274\004pi\352\346\005stora", "\003uda", "\010polskich\007wyjazdu", 
+ "\004b\352d\352", "\007klikn\261\346", "\011znajdowa\346", 
+ "\006powr\363t\011przesy\263am", "\011dodatkowe", "\007idziemy", 0, 
+ "\003dla\004ko\263o\006zosta\346", "\010przelewu", 0, 0, 
+ "\004lubi\011najlepszy\007naszego", "\005nagle\007odwied\274\010zupe\263nie", 
+ "\006dzieci", "\012codziennie\005\277ycz\352", "\006oferty", 0, 
+ "\012dowiedzie\346\014pozdrowienie", "\006monika\006nikogo", "\007decyzji", 
+ "\010b\352dziemy\005domek\012odpowiada\346", "\006okaza\346\014pozdrowienia", 
+ "\013przeprasza\346", "\006liczy\346\007zupe\263ny", "\010kochanie", 
+ "\006innego", 0, "\013autoryzacja\004luby\013sprzedaj\261cy", "\006poziom", 0, 
+ "\006kolega\010otrzyma\346\010potrzeby\010przelane", 
+ "\003si\352\005twoj\261\005waha\346", 0, "\011programie", "\007country", 
+ "\012propozycja\005\277ycia", "\004reda", "\003m\261\277\005wolne", 
+ "\010potrzeba", "\004anna\010kwiatowa\005moimi", "\005point", "\003jan", 
+ "\004skin", "\004ma\263e\006prostu\011rozmawia\346", "\007odezwa\346", 
+ "\005bole\346\006pobytu", "\007pa\361stwa\005pyta\346", "\011pierwszej", 
+ "\007badanie", "\003mam", 0, "\011chcia\263bym\012mo\277liwo\266ci", 
+ "\011niezb\352dny", "\004ma\263o", 0, "\003oto", 
+ "\003not\013pozostawiam\004typu\010wygrywaj", 0, "\010przelany", 
+ "\010siedzie\346", 0, "\010poprawka\007\277yczymy", "\007kobieta", 
+ "\004idea\010kwiatowy\004ma\263y\012materia\263\363w", "\007wypadku", 
+ "\006konrad\004test", "\005serio\007wypadek", 
+ "\011kosztowa\346\007stanowi\012za\263\261czeniu", 0, 
+ "\010poprawki\004si\263a", "\007kochana\007ponadto", "\006rzeczy\004sob\261", 
+ 0, "\012przyjaci\363\263", "\006dosta\346\007romanem", 
+ "\006lepszy\010pami\352ta\346\005wci\261\277", 
+ "\006miejsc\014skontaktowa\346\012u\277ytkownik", 0, "\007chcieli\005fajne", 
+ "\006mi\352dzy", "\012dost\352pnego\006zrobi\346", 0, 
+ "\006miedzy\014wygenerowana\005zgoda", "\012specjalnie", 
+ "\007cudowne\006formie\006m\363wimy\013wychodz\241cej", "\005firmy", 
+ "\011wieczorem", "\011buziaczki", 
+ "\012akceptowa\346\007kolejna\011wszystkie\011wy\263\261cznie", 
+ "\003ind\010piosenka", "\014wygenerowany\017zainteresowanie", 0, "\005magda", 
+ "\007przelew", "\007replika", "\003ela\007zdj\352cie", 
+ "\004alba\006niemen\005przed", "\010piosenki\006zemsta", "\006\266rodek", 
+ "\005kurcz\002la\005my\266li", 0, 0, "\005ofert", "\013internetowe", 
+ "\005dawno\005jacek\005okiem\007pewnego", 0, "\006spodni\011terminach", 
+ "\007mieszek\003rok", "\006wzgl\261d", "\010ochronie\004pada\011pami\352tasz", 
+ "\004albo\006domena", "\006wysoki", 0, 0, "\004jego", 
+ "\005niebo\012szale\361stwo", "\005nieco", "\005moich\003set\006stron\352", 0, 
+ "\003bis\007wstawi\306", "\005niego\012\266rodowiska", "\005plany", 
+ "\006garden", "\003mn\261", "\007oznacza", "\007podczas", "\010podajemy", 
+ "\006faceta\005sercu\006weso\263y", 0, 0, 0, 
+ "\005pa\263ac\003p\363\263\006wygra\346", "\010szanowni", 
+ "\003ago\010bierzesz\007kolejny\015kontrahentowi\007potrzeb", 
+ "\007napisa\346\005parys", 0, "\014sprzedaj\261cym", "\003nam", 0, 0, 
+ "\005cia\263o\011interfejs\005mia\263a", "\006daniel", "\003pod", 0, 0, 
+ "\005pr\363cz\005wieko\005witam", "\004mama\011poprawnej\012przeczyta\346", 
+ "\005agata\005sesji", "\006sobot\352\010szanowny", "\002id", 
+ "\005martw\012uprawniona", 
+ "\016oprocentowanie\006poczt\352\005szuka\012wszystkich", 0, 
+ "\004nota\012oryginalna\003tak", "\011chorwacji\011produkt\363w", 
+ "\007tydzie\361", "\012chcia\263abym", "\003bez", "\004ci\261g", 
+ "\010eternity\004tob\261", 0, 0, "\010oznacza\346", "\004imi\352", 
+ "\004czas\010leonardo\003swe", "\004czat", 
+ "\005\345rets\007dobrego\004palm\007wej\266cie", "\015ubezpieczenia", 0, 
+ "\004rock", "\004mamy", 0, "\003nur\007tomkiem", "\005coraz\004menu", 0, 
+ "\013zaproszenia", 0, "\006witamy", "\010czytanie\010leonarda\006tamten", 0, 
+ "\010wygrywa\346", "\005uda\263o", 
+ "\010czytania\011dzieciaki\014niepowo\263anym", 
+ "\007adresem\011chroniony\007swojego", "\003czy\007ogl\261da\346\005super", 
+ "\010pieni\261dz\007pozwol\261", 0, "\002ma\006moment", "\003da\346\005limit", 
+ 0, 0, "\007podoba\346", "\005osoba\005wyra\274", "\011przeciwko", 0, 0, 
+ "\007francja\004trio\006wersji", "\006import\007kobiety\005metal", 0, 
+ "\012podpowiedz", "\007ochroni\004przy\007s\263ycha\346", "\007istnie\346", 
+ "\005domku\006portal\011przegapi\346", "\013niepowo\263any", "\006siedz\352", 
+ "\005drugi\007kochany\010serwerze\006zwraca", 
+ "\011cieplutko\004si\263\352\003war", 0, "\010register", "\014internetowej", 
+ "\011komentarz", "\004bunt\007chcecie\007jakiego\006twoich\005znasz", 
+ "\003mi\266\005wieku", "\005klara\005wielu", "\005klasa\004seta", 
+ "\010spotkamy\005twoje", "\007zajmuje", 0, 0, "\006chcemy", 0, 
+ "\005grand\010standard\010za\277\261daj\261", "\007r\363\277nych", 0, 
+ "\004roku\011zamierza\346", "\007chocia\277\005\266wiat", 0, "\005brata", 
+ "\004p\363\263a\007powiedz\004uda\346", "\012ma\263\277e\361stwo", 0, 
+ "\006lepiej", "\010czerwiec\003pot", 0, "\010ochroni\346\010stronach", 0, 
+ "\005p\352dz\261", "\012skontaktuj\006ziemia", 
+ "\005cz\352\266\346\005prz\363d", "\005wstaw", "\007dzwonki", 
+ "\003ada\003b\363j\005cze\266\346\007francji", "\004nami", 0, 
+ "\004bywa\002os", "\007ochrona\007pytania", 0, 0, "\007dancing\007jeszcze", 0, 
+ "\004taka", "\003by\266\005klika\012realizacja", "\007komedia\006studio", 
+ "\007pantera", "\007jednego", "\006kartk\352\004zn\363w", 
+ "\012bibliotece\005ostro\005walka", "\006przodu", "\006ponosi\004taki", 
+ "\014europejskiej\007miejsce", "\006klubie", 
+ "\010dlaczego\006dublin\013wystawienie", "\007eternit\005pobyt\005takim", 
+ "\005audio\006porada", "\007ka\277dego\007pojawi\346\011prawdziwy\004sie\346", 
+ 0, "\012kontaktowe", "\006ludzie\010platform\011problem\363w", 0, 
+ "\006pokoju\007siostra\013specjalista", "\004swej", 0, 0, 
+ "\007leonard\006p\352dzi\346", "\006ryzyko\012skorzysta\346", "\002na", 0, 0, 
+ "\005\277yciu\010zakresie", "\011polskiego", "\003boy", "\004obok", 
+ "\012koniecznie\007natalia\004wilk\010wyjecha\346", 
+ "\007dominik\004will\011zapomnie\346", "\004chat\010przysz\263y", "\004rada", 
+ 0, 0, "\005by\263am\003der", "\004plus\007spodnie", "\002a\277\007ochrony", 
+ "\005ekipa", "\005by\263em\010sprawdza", 
+ "\004czym\006szko\263a\014wcze\266niejszy", "\006daleko", "\011logowania", 
+ "\011darmowych\005lubi\352", 
+ "\006marcin\016niewiarygodnie\007ods\263ona\010premiera", "\006dzikim", 0, 
+ "\007dokona\346\007radzimy", "\005mog\263a", 0, "\006kocham\005swoja", 0, 0, 
+ 0, 0, 0, "\006czekam\016funkcjonalno\266\346\004rady", 0, 0, "\006aukcja", 
+ "\011listopada\005sieci\005wzi\261\346", "\004lana\007poleca\346", 
+ "\004\277eby\005figur\006serwis", 0, "\004land", "\004lane\007spotkam", 
+ "\014angielskiego\011zbigniewa", "\005edyta\012najszybszy\005temat", 0, 0, 
+ "\005\277eby\266", "\004firm\012ma\243\257e\321stwo", 
+ "\012dzi\352kujemy\005witek", "\006jakich", "\011dotycz\261ce", 
+ "\006b\352d\261cy\012rekrutacja", "\010jakiego\266", "\004tak\261", 
+ "\005mocno\011specjalny\007warto\266\346", "\004zero", 0, "\006pomog\261", 
+ "\014kiedykolwiek\010obejrze\346", 0, 0, 
+ "\004lany\005og\363le\012wychodz\261cy", "\006niczym\007wysokie", 
+ "\012najwi\352kszy", "\005pismo", "\006cowboy", "\005dobra\007dziecka", 0, 0, 
+ "\006kobiet", "\006figury\007justyna", 0, "\006gazowy\007miejscu\007rodzina", 
+ "\004adam", 0, "\012europejski\005kopia\014po\266wi\352ci\263by\266", 0, 
+ "\004much", 0, "\013niezb\352dnych\011ostatnich", "\007kolejki", 
+ "\016administratora\006wartki", "\007madonna", 0, "\003bar\007cyfrowy", 
+ "\011zamierzam", "\005zgod\352", "\004dzi\266\011w\263a\266ciwie", 
+ "\001i\012mieszkaniu", "\015postanowienia", 0, "\004baba", "\006ojciec", 
+ "\013gospodarczy\013skonfiguruj", 0, 0, "\007otwiera", "\006przeze", 
+ "\003bit\005jeden\004kont\013wykorzysta\346", "\006zaraza", "\006tapety", 
+ "\005marek", "\005wej\266\346", 0, "\007obs\263ug\261\005twarz\007widzie\346", 
+ "\005pomoc", "\005pewna\006powiem\005zatem\006zesp\363\263", 0, "\004dera", 
+ "\012bezp\263atnie\003wie", "\007legenda\006musia\263\007takiego", 
+ "\007ch\352tnie\007marzena\007wakacje", "\005gdzie\005mieli\005m\363wi\261", 
+ 0, 0, "\004baby\006janusz", "\012bezpieczny\007przeb\363j", 0, 
+ "\006kartka\010wygodnej", 0, "\005biura\006planet\005trwa\346", 0, 
+ "\011korzysta\346", "\005akcja\014odpowiedzie\346", "\003bob\006rolach", 
+ "\007kolejka\005op\263at", "\007germany", "\006raczej", "\006adresu\002no", 
+ "\005drawa", "\003sto", 0, "\011wakacjach\005witaj", 0, 
+ "\004girl\010otwiera\346\010wiedzie\346", "\006kocha\346\007wpisany", 
+ "\010terminie", "\010\266ci\261gnij\006pomocy", "\005czemu\010niedawno", 
+ "\010kontaktu", "\006medium\013wydawnictwo", "\006chwili\004sala", 
+ "\005libra\005stara", "\006strefa", "\007r\363wnie\277", 
+ "\007pewno\266\346\004sale", "\004\266lub", 0, 
+ "\015automatycznie\002ej\011przypadku\006rzadko", "\004wraz\007znale\274\346", 
+ "\013konsultanci", "\010karolina\010kontakty", "\011republika", 
+ "\005fajna\014przynajmniej\004\277ywo", "\006\243ukasz", 0, 0, 
+ "\005lenny\007odzywa\346\005r\363\277ny", 0, 0, 0, "\010rozumiem", 0, 
+ "\006gor\261cy", 0, "\005kciuk", "\007ogl\261daj\010spotykam", "\005nadal", 
+ "\004musi\003new", 0, 0, "\016wcze\266niejszego", "\006nowego\006rodzic", 
+ "\012pozostawa\346\010straszny", "\010istnieje", "\005dobry", "\007kuchnia", 
+ 0, "\007okresie\010produkty", 0, "\007napada\346\004os\363b", 0, 0, 
+ "\007momentu\011przesy\263a\346\006roboty\004rola\012wiadomo\266ci", 0, 
+ "\003dno\005kilku\012otrzymaniu\003was\005wilki", 0, "\010polskiej\004role", 
+ "\007rodzaju\010wojciech", 0, 0, "\013rejestracj\352", 
+ "\007element\007uczucia", "\010pocz\261tek\006szybko", 0, 
+ "\005czyli\012elektronik\005jedno", "\007zmieni\263", 
+ "\006kciuki\006us\263uga", 0, "\005karol\011stanis\263aw", "\007odporny", 0, 
+ 0, 0, 0, "\006singel\004wi\352c", "\010najmniej", "\007rodziny", "\004wiec", 
+ 0, 0, "\006faceci\013odpowiednio\011zapowiada", 0, 
+ "\005kupi\346\005model\011znajomymi", "\005artur\004kult", "\003daj\005kotek", 
+ "\011ciechan\363w", "\011natomiast\003ode", "\010podstawa\004wiem", 0, 0, 0, 
+ "\014przychodz\261cy\011spotkania", 0, "\006mi\263ego", 0, 
+ "\006kabura\003rio", 0, "\006franka", 0, "\003hop", 0, 
+ "\007polskim\011pozdrowi\346", 0, 0, "\004bobo\004stoi", 0, "\005pom\363c", 
+ "\007chicago", "\004talk", "\014przygotowany", "\005kubek\003law", 
+ "\004stop\004unia", 0, "\003by\346\007centrum", 0, 
+ "\006biurze\007dziwnie\006podoba", 0, 
+ "\006joanna\012katowickim\007podobne\014przygotowana", 
+ "\012kontaktowy\007produkt\011sprzedany", "\004unii", "\002in\005wenta", 0, 
+ "\010dosta\263am", "\007kontakt\010ryszarda", 0, 
+ "\012propozycji\004temu\010zwr\363cimy", 0, "\011animowane\005kolej", 0, 0, 0, 
+ "\005fajny\007musieli\013przyjemno\266\346\006wracam", "\005major", 0, 
+ "\004newa", 0, "\003des\005minut", "\007czasami\004oczy\007zwr\363ci\346", 
+ "\006period", "\004boje\007czujesz", "\005dawa\346\005zdaje", 0, 0, 
+ "\014antywirusowy\005mojej\006poczty\004tata", 
+ "\005filmy\006gdzie\266\004trzy", 0, 
+ "\007polskie\010sierpnia\002te\005tomka\007zosta\263a", "\003aga\002ex", 
+ "\005niech", "\007ciekawe", "\003log\005zmian", 0, 
+ "\010odbiera\346\016szwajcarskiego", "\006aukcje\010rozumie\346\011zachodzie", 
+ "\005letni\005nigdy\015przychodz\241cej\007wyrazem", "\012kontrahent", 
+ "\013interesowa\346\010spotyka\346", "\005ciele\013pozostawia\346\005wolno", 
+ "\004brak", "\004koga\006mia\263em\010szykowa\346\005twoja\010zobaczy\346", 
+ "\004sw\363j", "\003pan", "\006pointa", "\011rozumiesz", 0, 
+ "\013przesy\263ania\005urban", 0, "\011aktywacji\004brat", "\014skorzystanie", 
+ "\003iza\005micha\006robisz\003sam", 0, 0, 
+ "\006hunter\006miedza\014skorzystania\004szef\011znajomych", "\004kogo\002po", 
+ "\012identyczny\012pocztowego\006\277yczy\346", 0, "\004city\004wasz", 0, 
+ "\006krak\363w\006lubisz\004rama", "\005gdyby", 0, 
+ "\005dzie\361\012najwi\352ksze", "\007\277adnych\005kraju\006zmiany", 0, 
+ "\010znajdzie", "\004daje\012pojecha\263am\006siebie", 0, "\003hej", 
+ "\010b\352d\261cych\007mi\263o\266ci\011zawarto\266\346", "\011generator", 
+ "\005kogo\266\007relacje", "\007szalony", 0, 
+ "\017administrator\363w\003ni\277", "\010wiktoria\005wita\346", "\003kei", 
+ "\006august\005forma", "\004olga\011powa\277anie", "\010pocztowy", 
+ "\005czuj\352\007obawia\346", 0, 0, 0, 0, "\007osobowy", 0, "\011wieczorek", 
+ "\004lawa", "\005odpad\011prawdziwe", "\007j\352zyk\363w\006usun\261\346", 
+ "\007ba\263tyku\006bardzo\007czekaj\261\013korzystania\005pok\363j", 
+ "\004york", "\004m\363g\263\005razem", "\011szcz\352\266cie", "\010ostatnia", 
+ "\007ca\263kiem\005kolei\013podszywaniu\005swego", "\006troch\352", 
+ "\010kampania\006okazje\006prosi\346", "\003ci\352\010ostatnie\007ryszard", 
+ "\006hotelu\007waszego", 0, "\004luty\012najnowszej", "\010podstaw\352", 
+ "\004idol\007nale\277e\346", 0, "\010kampanii", "\010ostatnim\010pocztowe", 
+ "\006pakiet\007wst\261pi\346", "\004m\363wi\010ostatnio", "\005\277aden", 0, 
+ 0, "\003her", 0, "\013bezpiecznej\005muszy\006nazywa\006status", "\005ocena", 
+ "\010bardziej\010komputer", "\005dziki", "\003emu", "\011logowanie", 
+ "\005album\005lubi\346", "\007postaci", 0, "\007niemiec\007persona\006robimy", 
+ "\010taks\363wka\007trzyma\346", "\006postaw\015przezwyci\352\277y\346", 
+ "\005kolor", "\011kontakt\363w\013najlepszego\005swoje\007wczoraj", 
+ "\005czeka\004tw\363j", "\005party", "\013przekazanie", 
+ "\007darmowy\006utrata\007wysy\263am", 0, 0, 0, "\012atrakcyjne", 
+ "\006czyta\346\007dariusz\004mia\263", "\003ani\003tam\006zale\277y", 
+ "\004pana\007polega\346", "\003cel", 0, "\004daj\261\004logo\007udanych", 0, 
+ "\011gwarancja\011logowa\263e\266\012odpowiedni", 
+ "\005drogi\007powiesz\003ten", "\006master", "\004pani\004sama", "\006edycja", 
+ "\010pistolet", "\005denne\002od", "\005panie\004same\007wiadomo\006zobacz", 
+ "\006sylwek", "\011dokonywa\346\007sklepie", "\005jakim", "\004sami", 
+ "\005podaj\011zobaczymy", 0, 0, "\011autorstwo", "\003win", "\004samo", 
+ "\004bra\346\014konfiguracja", "\005okres\006w\263asny", "\005prawo", 0, 
+ "\003\277y\346", "\013bezpiecznie\006wersja", 0, "\002my", 
+ "\014konfiguracji\006\370nsker\012regionalny", 
+ "\012przyniesie\004samy\011wygodnych", "\006cztery\004sens", "\012serdecznie", 
+ 0, "\007za\277\261da\346", "\014identycznego\012transakcji\011zapomnisz", 
+ "\005grze\266", "\005kilka\005morze", "\006musie\346\007numerem", 
+ "\011angielski\010horoskop", "\004pyta", 0, 0, "\007henryka", "\005ponad", 
+ "\003far\005formy\013podszywanie", "\005porno\006wystaw", 0, 
+ "\010odpadami\010poni\277szy\010wpisa\263e\266", 0, 
+ "\010chcia\263am\006gratis\015postanowienie\010weso\263ych", "\007ostatni", 
+ "\005matki", "\007grzesia\004tych", 0, "\003era", "\005macie", "\006wynosi", 
+ "\005babci", "\012bezp\263atnej\007logowa\346\005mo\277na", 0, 0, 
+ "\012internecie", 0, "\006german", "\007zwi\261zku", 0, "\005pewne", 0, 
+ "\004hera\004klar\013przeszkadza", "\007dlatego\005pracy", "\007dodatku", 0, 
+ "\010odzywasz\006ofert\352", "\002ok", "\004inna\005radon", "\006napada", 
+ "\005karta", "\006osobom", "\004inne\005kasiu\005musz\352\006teresa", 
+ "\010grudzie\361\012konsultant\012\266rodowisko", "\012realizacji", 
+ "\006gdyby\266", "\004inni\012niniejszym", "\007zadanie", 
+ "\011aktualnie\010oferowa\346\006termin", 
+ "\011ciekawych\004draw\012pozdrawia\346", 
+ "\007podanie\013uproszczony\006\266wiata", 
+ "\011cyfrowego\007odbioru\006powoli\006spos\363b", 0, "\005prawy", 
+ "\014przekazaniem", 0, "\006mobile\010zwi\261zany", "\004ania\004tama", 
+ "\003\274le\005staje", "\007imieniu", 0, 0, 
+ "\003ale\004inny\005lista\006wraca\346", "\005gosia\013komercyjnym", "\002ku", 
+ "\006leczy\346", "\003jej\005stare", "\007kochasz\006stanie", 
+ "\005wa\277ny\010zwi\261zane", 0, "\010dziennie\004klik\004le\277e", 
+ "\011widowisko", "\005wanta\007wybierz", "\005piotr", 
+ "\011niedaleko\007wzgl\352du", "\006danuta\004walk", "\004chce", 0, 0, 
+ "\004celu\004wina", "\012krzysztofa", "\005pisa\346\005umbra", "\007krakowa", 
+ "\011dotycz\261cy", 0, 0, "\004film\007piszesz\011platforma\007zajrze\346", 
+ "\010dzi\352kuj\352\011wszystkim", "\006brutto\012wirtualnej", "\007zmienia", 
+ "\004le\277y\003tom\006\246wi\352ty", "\007za\263o\277y\346", 0, 
+ "\007brakuje\006zamiar", "\005autor\005golec", "\007faktura\005has\263o", 0, 
+ 0, "\002ta", "\002et", 0, "\011katowicki\012komentarza\012potrzebuje", 
+ "\011arkadiusz", "\013regulaminem\004winy", "\007mateusz\010powrocie", 
+ "\004fara\006powodu\006twojej\010wieczoru", "\010rodzinka\007tw\363rc\363w", 
+ "\002ci\007koncert\007student", "\005detal\011wystarczy", "\010wieczory", 
+ "\005\263\261czy", 0, "\003map\006numery\003zna\007znaczy\346", 
+ "\010dor\352cza\346\002we", "\007chcia\263a", "\011odpowiada", 
+ "\010planowa\346\010poprawny", "\012powa\277aniem", "\004g\363ra\004mog\261", 
+ "\005jarka\010zmienia\346", "\005radio", 0, "\007spotka\346", 0, 
+ "\012najszybsza\005teraz", "\012przyjedzie", "\003mir", 0, 
+ "\007bankowe\004mowa\003san\007zapyta\346", 
+ "\010dostawa\346\006kinowy\011napisa\263am\007poni\277ej\006wygraj", 0, 0, 
+ "\011kapitanem", "\007katalog", 0, "\012przesy\263asz", "\012oczywi\266cie", 
+ "\004rana\010sprzeda\346", "\003for\011godzinach\006letnim", 
+ "\005hotel\006musisz", "\007faktury\006figura\003i\266\346", "\005fasty", 
+ "\004g\363ry", "\012protoko\263em\007zabawny", "\012przeczytaj", 
+ "\006dzi\352ki\013szwajcarski", 
+ "\011doskonale\013katamaranem\011spotkanie\005wyb\363r", "\007portalu", 
+ "\012przedmiotu\012zam\363wienie", "\006wybra\346\005zanim", 
+ "\007dodatek\004woda", "\005banku\004rano\006wartka", "\004chc\261", 
+ "\005mniej", "\010rachunek", 0, "\012\266wi\261teczny", "\005jazda", 0, 
+ "\006zapro\266", "\006drugim", "\011doko\361czy\346\006szansa\007trzymaj", 0, 
+ "\007krajach", 0, "\006filmem", 0, "\013u\277ytkownika", 
+ "\003bat\006miasto\003one", "\007martwi\346\006okazja", "\012przyjecha\346", 
+ "\011pouk\263ada\346", "\010wszelkie", "\006kurcze\007tadeusz\007wys\263any", 
+ "\004wody", "\004mog\352", "\007edwarda", "\011najlepiej", "\005udany", 
+ "\011dodatkowo\006innych", "\011pieni\261dze", "\006nowych\004opis", 
+ "\011dok\263adnie\003kun\012narodzenie\014poniedzia\263ek", 0, "\010uczelnia", 
+ "\006afryce\012wszystkimi", 0, "\011mo\277liwo\266\346", 
+ "\010m\352\277czyzn\001o", "\006mo\277esz", 0, 
+ "\006kt\363rym\005osoby\007przela\346", "\007zwraca\346", 
+ "\006ka\277dym\005uwaga", "\003gra\006police", 
+ "\005robi\346\005style\007weekend", 0, "\011niniejszy", "\003hit\006zdania", 
+ "\003gdy\005marca", "\007ponosi\346", "\010thriller\006tr\363jki", 
+ "\006serial\007zaj\352cie", "\004woli", "\006polsce", "\005maria", 
+ "\010powy\277szy", "\012atrakcyjny\005marka", 
+ "\003bod\010grzegorz\010polityki\006pozna\346", 
+ "\004chc\352\004mapa\012odpowiedzi\011pouk\263adam", "\011konieczne", 
+ "\003cal\005dzia\346\010konkursu", "\010centrala", 
+ "\005klasy\006strefy\005twoim", 
+ "\010aukcjach\006mia\263am\013pa\274dziernik\013samochodowy", 
+ "\013najszybciej\004usu\361", "\005marta\004nowa\010polityka", 
+ "\010cz\263owiek\005lidia\013uprawnienia", "\011potrzebne\007wys\263ana", 
+ "\004gest\006niebie\004znak", "\006jedyna\012nawi\261zaniu\004nowe", 
+ "\004znam", 0, 0, "\003dni\004stad\003wam", "\006kwiaty", 0, 0, 
+ "\011pewno\266ci\261\002to", "\006danych\007markowe", 
+ "\006person\005wst\261p", "\006reszta", 0, 0, "\004stan", "\012znajdziesz", 0, 
+ 0, "\006kra\361cu\010roztoczu\004star", "\007trwanie", "\002mu\004nowy", 
+ "\010wirusami", "\006profil", "\010materia\263\006wi\352cej", 
+ "\010zawiera\346", "\002al\004niby\005swing\007widzisz", 
+ "\011dodatkowy\012komercyjny\007zdarza\346", 
+ "\004form\002j\261\012transakcje", "\011zobaczysz", 
+ "\015administrator\007dopiero\011produkcja", 0, "\011cokolwiek\004keja", 
+ "\010postaram\005zakup", "\007ekranie\006robota", 
+ "\014przeszkadza\346\010roztocze", "\012kazimierza\011przesy\263ka\005sklep", 
+ "\006firmie", 0, 0, "\010festiwal", "\007serwisu", 0, 0, 0, 0, 
+ "\007dowolny\013kosmetyczny\006punkty", "\007kafejce", 0, 
+ "\002be\012najni\277szej", 0, "\003ola\010wsparcia\007wydawa\346", 
+ "\005has\263a\007pozycji\007reklama", "\007przelot", 
+ "\016tera\274niejszo\266\346\007wszelki", "\010wsparcie", 
+ "\010pocz\261tku\004roni\013specjali\266ci", "\005arena", "\005koszt", 0, 
+ "\007herbert\004kuna", "\012umo\277liwia\346", "\007uczelni", 
+ "\005chaos\012przysz\263ych", 0, "\005j\352zyk\011w\263\261czenie", 
+ "\005kasia\006samego", 0, "\010napoleon\010ponownie\004wod\352", 
+ "\006europa\007konkurs\005p\363j\266\346", "\002i\277\004tego", 
+ "\006henryk\011katatonia", 0, "\010szczerze", 
+ "\007decyzje\006polega\007t\352skni\352\007urszula\011wiadomo\266\346", 0, 
+ "\005jedna\007walenty", "\011czynienia\007przykro", "\007sprawd\274", 0, 0, 0, 
+ "\006by\263oby", "\007kraniec", "\004gram", "\010\246ci\261gnij", 0, 
+ "\006bachor", "\007ameryka\007central", "\004cala", "\006koszty", 
+ "\002as\007dowolna\011przedmiot", "\014pa\274dziernika", "\004cale", 
+ "\005pe\263ny", "\004tyle", 0, 
+ "\007andrzej\007pozycja\015przys\263uguj\261cy\007reklamy\011wycieczka", 
+ "\005czuj\261\006tw\363rca", "\003ni\261", "\007manager", "\004\257ywo", 
+ "\004dnia\012dostaniesz", 0, "\006wielka", "\012aleksander\005kocha", 0, 0, 0, 
+ 0, "\004body\005dniem\003gry\010niestety\005pom\363\277\004wami", "\004tylu", 
+ "\011wirtualna", "\012interesuje\013internetowy\005kt\363ra", 0, 
+ "\006szuka\346", "\007dor\352cza\003osa", "\012dziewczyny\005potem", 
+ "\007pozdr\363w", "\006lipiec", "\005fina\263\013wymienionym", 0, 
+ "\004dniu\006sobota", "\004zna\346", 0, "\006poczta", 
+ "\006jakie\266\010muzyczny\006proste", 0, "\004went", "\003big", 
+ "\006micha\263\005vinci", "\007przeciw", "\002on", "\007ameryki", 0, 
+ "\006ci\261gle\006prosz\352\004sta\346", "\011konkursie\007naszych", 
+ "\017administratorzy\010rozmowie", 0, "\004boli", 0, "\003jar", 
+ "\005opisy\004song", "\007kolejne\006podany\007poprzez", 
+ "\005klubu\007tysi\352cy", "\007za\263o\277y\263", "\006maciej", 
+ "\012otrzymywa\346", 
+ "\007artyku\263\007badania\004luna\007rozmowy\012stanis\263awa\004styl", 
+ "\012powiedzie\346\004uczy", 
+ "\015jakiejkolwiek\006minuta\010pierwszy\012za\263\261czenie\007zdj\352cia", 
+ 0, "\005trust", "\011niedziele\005wtedy", 
+ "\004du\277e\010muzyczne\010postara\346\007wysy\263a\346", "\005ko\361cu", 
+ "\003\263za\003idy", 0, "\010pierwsza\007podobno\002ze", 
+ "\005media\006ochota", 0, "\004fast\003nic\014regionalnych", 
+ "\010pierwsze\004umie", "\007beatles\006jedzie", "\004du\277o\006zmiana", 
+ "\010mieszkam", "\006my\266le\346", "\002im", 
+ "\005banda\006milion\007zupe\263ne", "\006ods\263on", "\006szans\352", 
+ "\004gdy\277\005nasze", 0, "\005czuje\006\266rodku", "\004du\277y\006szybka", 
+ "\012ca\263kowicie", 0, "\004mich\007potrafi\007wolnego", "\012zam\363wienia", 
+ "\006koleg\261", "\010skrzynek\006znaczy", "\006polowy", 0, 
+ "\007nadzia\346\007odpad\363w", 0, "\006ka\277dej\007pokrywa\005powie", 0, 
+ "\007zosta\263o", "\005cenie\007trakcie", "\006robert\005znowu", 
+ "\006damian\007gad\277ety\017zainteresowania", 
+ "\005ka\277dy\004list\012przychodzi", "\010nadzieje\003oko", 0, "\006poznaj", 
+ "\011dostajesz\005front\005girls", 0, "\007czadowy", 
+ "\007jedynie\005sensu\013szczeg\363lnie", 0, 
+ "\003cyc\011oferujemy\006w\263adza\010zapomnij", "\006nikomu\005wa\277ne", 
+ "\005sporo", 0, 0, "\004gra\346\006trudno", 
+ "\011chronione\006posta\346\006prawda\007profile", 0, "\013motywacyjny", 
+ "\010dzieciak\012oczekiwany\010wykonane", "\005dawid\006strona", 
+ "\005kotku\010potrafi\346", "\012narodzenia", "\007przegap\007zapytaj", 
+ "\005denny", "\010potrafi\352", 
+};
+
+
+
+#define cref_tbl_LEN 131
+static const char *cref_tbl[131] = {
+ "\006hellip\040", "\006Ccedil\307\004emsp\040", "\005Icirc\316\003ETH\320", 
+ "\006yacute\375", 0, 0, "\006Uacute\332\004auml\344\006oslash\370", 0, 
+ "\004copy\251", "\003reg\256\006Aacute\301\004uuml\374", 0, 
+ "\006Ograve\322\006thinsp\040", "\003uml\250", 0, 0, 0, "\006ugrave\371", 0, 
+ "\005acute\264\005acute\264", "\004nbsp\240\006agrave\340\004nbsp\240", 0, 
+ "\005AElig\306", 0, "\006Oacute\323", 0, "\006Igrave\314", "\006ntilde\361", 
+ "\005Acirc\302", "\004Euml\313", 0, "\005Ucirc\333", 0, 
+ "\005cedil\270\005cedil\270", "\005ldquo\040", "\006Yacute\335", 
+ "\004macr\257", 0, "\006Oslash\330\004ensp\040", 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+ "\006Ugrave\331", 0, "\005ecirc\352\005lsquo\040", 
+ "\006Agrave\300\005rdquo\040", 0, 0, "\005szlig\337", 0, 
+ "\003shy\255\003shy\255", "\005laquo\253\006eacute\351", "\006Ntilde\321", 
+ "\006atilde\343\004bull\040", "\004euml\353", 0, "\005aring\345", 
+ "\004yuml\377", "\005ocirc\364\005sbquo\040", 0, "\005thorn\376", 
+ "\005rsquo\040", 0, 0, 0, 0, 0, "\006otilde\365", "\005raquo\273", 0, 
+ "\006rsaquo\040", "\003eth\360", 0, 0, 0, "\005Ecirc\312", "\004Iuml\317", 0, 
+ "\004quot\042\004quot\042", "\006iacute\355\006dagger\040", 0, 0, 
+ "\006Eacute\311", 0, "\006Atilde\303", "\003zwj\040", 0, "\005Aring\305", 0, 
+ "\005Ocirc\324\005aelig\346", "\004Ouml\326", 0, "\006egrave\350", 0, 0, 
+ "\005bdquo\040", "\005iexcl\241\006ccedil\347", "\005icirc\356", 
+ "\006Otilde\325", 0, 0, "\004Auml\304\006uacute\372", 0, 0, 
+ "\004Uuml\334\006aacute\341", 0, "\006ograve\362", "\004iuml\357", 0, 0, 
+ "\006Iacute\315", 0, "\006lsaquo\040", 0, 0, "\006iquest\277", 
+ "\005THORN\336", 0, "\006oacute\363", 0, "\006igrave\354", "\004ouml\366", 
+ "\005acirc\342", "\006Egrave\310", 0, "\005ucirc\373", 
+};
+
diff -r 000000000000 -r c7f6b056b673 dcclib/ckmime.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/ckmime.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,829 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * decode MIME for checksums
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.39 $Revision$
+ */
+
+#include "dcc_ck.h"
+
+/* Notice MIME headers */
+void
+dcc_ck_mime_hdr(DCC_GOT_CKS *cks,
+		  const char *hdr,	/* entire header line or name only */
+		  const char *str)	/* header value if not after name */
+{
+	/* parse at least the header name */
+	cks->mhdr_st = CK_MHDR_ST_CE_CT;
+	cks->mhdr_pos = 0;
+	parse_mime_hdr(cks, hdr, strlen(hdr), 1);
+
+	/* parse the header value if present and we care about the header */
+	if (str
+	    && cks->mhdr_st != CK_MHDR_ST_IDLE) {
+		parse_mime_hdr(cks, ":", 1, 1);
+		parse_mime_hdr(cks, str, strlen(str), 1);
+	}
+
+	/* force the end of the line */
+	if (cks->mhdr_st != CK_MHDR_ST_IDLE)
+		parse_mime_hdr(cks, "\n", 1, 1);
+
+	if (cks->mime_nest != 0)
+		cks->mp_st = CK_MP_ST_PREAMBLE;
+
+	cks->flags |= DCC_CKS_MIME_BOL;
+}
+
+
+
+static u_char				/* 1=matched */
+match(DCC_GOT_CKS *cks,
+      enum CK_MHDR_ST ok, enum CK_MHDR_ST fail,
+      const char *tgt_str, u_int tgt_len,
+      const char **bp, u_int *bp_len)
+{
+	u_int len;
+
+	len = min(tgt_len - cks->mhdr_pos, *bp_len);
+	if (strncasecmp(tgt_str + cks->mhdr_pos, *bp, len)) {
+		/* switch to failure state if there is enough of the
+		 * string to know it does not match */
+		cks->mhdr_st = fail;
+		return 0;
+	}
+
+	*bp += len;
+	*bp_len -= len;
+	if ((u_int)(cks->mhdr_pos += len) >= tgt_len) {
+		/* switch to the success state on a match */
+		cks->mhdr_st = ok;
+		cks->mhdr_pos = 0;
+		return 1;
+	}
+
+	/* wait for more input */
+	return 0;
+}
+
+
+
+/* ignore white space */
+static u_char				/* 0=buffer empty */
+span_ws(const char **bp, u_int *bp_len)
+{
+	char c;
+	while ((c = **bp) == ' ' || c == '\t' || c == '\r' || c == '\n') {
+		++*bp;
+		if (--*bp_len == 0)
+			return 0;
+	}
+	return 1;
+}
+
+
+
+/* skip to white space or after semicolon that precedes the next parameter */
+static u_char				/* 0=buffer empty */
+skip_param(const char **bp, u_int *bp_len)
+{
+	char c;
+	while ((c = **bp) != ' ' && c != '\t' && c != '\r' && c != '\n') {
+		++*bp;
+		if (c == ';') {
+			--*bp_len;
+			return 1;
+		}
+		if (--*bp_len == 0)
+			return 0;
+	}
+	return 1;
+}
+
+
+
+/* Parse MIME headers
+ *	Look for (parts of) Content-Type and Content-Transfer-Encoding
+ *	headers in a buffer.  There can be at most one significant (not part of
+ *	folded whitespace) '\n' in the buffer and only as the last byte */
+u_char					/* 1=blank line */
+parse_mime_hdr(DCC_GOT_CKS *cks,
+	       const char *bp, u_int bp_len,
+	       u_char in_hdrs)		/* 1=in RFC 822 headers */
+{
+#define MMATCH(str,ok,fail) match(cks,CK_MHDR_ST_##ok,CK_MHDR_ST_##fail,    \
+				  str,sizeof(str)-1, &bp, &bp_len)
+	char c;
+	DCC_CK_BND *bndp;
+
+	if ((cks->flags & DCC_CKS_MIME_BOL)
+	    && !in_hdrs) {
+		c = *bp;
+		if (c == '\r') {
+			/* ignore CR to ease detecting blank line */
+			if (--bp_len == 0)
+				return 0;
+			c = *++bp;
+		}
+		if (c == '\n')
+			return 1;	/* this line is blank */
+
+		/* reset parser line without folded whitespace */
+		if (c != ' ' && c != '\t') {
+			cks->mhdr_st = CK_MHDR_ST_CE_CT;
+			cks->mhdr_pos = 0;
+		}
+		cks->flags &= ~DCC_CKS_MIME_BOL;
+	}
+
+	do {
+		switch (cks->mhdr_st) {
+		case CK_MHDR_ST_IDLE:
+			return 0;
+
+		case CK_MHDR_ST_CE_CT:
+			/* This state always preceeds the following states */
+			if (MMATCH("Content-T", CT_WS, IDLE)) {
+				switch (*bp) {
+				case 'r':
+				case 'R':
+					cks->mhdr_st = CK_MHDR_ST_CE;
+					break;
+				case 'y':
+				case 'Y':
+					cks->mhdr_st = CK_MHDR_ST_CT;
+					break;
+				default:
+					cks->mhdr_st = CK_MHDR_ST_IDLE;
+					return 0;
+				}
+			}
+			break;
+
+		case CK_MHDR_ST_CE:
+			MMATCH("ransfer-Encoding:", CE_WS, IDLE);
+			break;
+		case CK_MHDR_ST_CE_WS:
+			if (!span_ws(&bp, &bp_len))
+				return 0;
+			switch (*bp) {
+			case 'b':
+			case 'B':
+				cks->mhdr_st = CK_MHDR_ST_B64;
+				break;
+			case 'q':
+			case 'Q':
+				cks->mhdr_st = CK_MHDR_ST_QP;
+				break;
+			default:
+				cks->mhdr_st = CK_MHDR_ST_IDLE;
+				return 0;
+			}
+			break;
+		case CK_MHDR_ST_QP:
+			if (MMATCH("quoted-printable", IDLE, IDLE))
+				cks->mime_ce = DCC_CK_CE_QP;
+			break;
+		case CK_MHDR_ST_B64:
+			if (MMATCH("base64", IDLE, IDLE))
+				cks->mime_ce = DCC_CK_CE_B64;
+			break;
+
+		case CK_MHDR_ST_CT:
+			MMATCH("ype:", CT_WS, IDLE);
+			break;
+		case CK_MHDR_ST_CT_WS:
+			/* We have matched "Content-type:" */
+			if (!span_ws(&bp, &bp_len))
+				return 0;
+			switch (*bp) {
+			case 't':
+			case 'T':
+				cks->mhdr_st = CK_MHDR_ST_TEXT;
+				break;
+			case 'm':
+			case 'M':
+				/* do not nest too deeply */
+				if (in_hdrs
+				    || cks->mime_nest < DIM(cks->mime_bnd)) {
+					cks->mhdr_st = CK_MHDR_ST_MULTIPART;
+				} else {
+					cks->mhdr_st = CK_MHDR_ST_TEXT;
+					cks->mhdr_st = CK_MHDR_ST_IDLE;
+				}
+				break;
+			default:
+				/* assume it is binary noise if it does
+				 * not match "Content-type: [tTmM]" */
+				cks->mime_ct = DCC_CK_CT_BINARY;
+				cks->mhdr_st = CK_MHDR_ST_IDLE;
+				return 0;
+			}
+			break;
+		case CK_MHDR_ST_TEXT:
+			/* we are looking for "Text" in "Content-type: Text" */
+			if (MMATCH("text", HTML, IDLE))
+				cks->mime_ct = DCC_CK_CT_TEXT;
+			break;
+		case CK_MHDR_ST_HTML:
+			/* look for "Content-type: Text/html" */
+			if (MMATCH("/html", CSET_SKIP_PARAM, CSET_SKIP_PARAM))
+				cks->mime_ct = DCC_CK_CT_HTML;
+			break;
+		case CK_MHDR_ST_CSET_SKIP_PARAM:
+			/* Look for semicolon or whitespace preceding next
+			 * parameter after "Content-type: Text/html" */
+			if (skip_param(&bp, &bp_len))
+				cks->mhdr_st = CK_MHDR_ST_CSET_SPAN_WS;
+			break;
+		case CK_MHDR_ST_CSET_SPAN_WS:
+			/* skip optional whitespace before next parameter */
+			if (span_ws(&bp, &bp_len))
+				cks->mhdr_st = CK_MHDR_ST_CSET;
+			break;
+		case CK_MHDR_ST_CSET:
+			/* have matched "Content-Type: text...;"
+			 * and are looking for a "charset=" parameter */
+			MMATCH("charset=", CSET_ISO_8859, CSET_SKIP_PARAM);
+			break;
+		case CK_MHDR_ST_CSET_ISO_8859:
+			/* We have matched "Content-Type: text...charset="
+			 * and are looking for "ISO-8859-*".
+			 * Ignore leading '"' */
+			if (cks->mhdr_pos == 0
+			    && bp_len > 0 && *bp == '"') {
+				++bp;
+				--bp_len;
+			}
+			MMATCH("iso-8859-", CSET_ISO_X, IDLE);
+			break;
+		case CK_MHDR_ST_CSET_ISO_X:
+			for (;;) {
+				if (bp_len == 0)
+					return 0;
+				--bp_len;
+				c = *bp++;
+				if (c < '0' || c > '9') {
+					if ((c == '"' || c == ' ' || c == '\t'
+					     || c == ';'
+					     || c == '\r' || c == '\n')
+					    && cks->mhdr_pos == 2)
+					    cks->mime_cset = dcc_cset_2;
+					else
+					    cks->mime_cset = dcc_cset_1;
+					cks->mhdr_st = CK_MHDR_ST_IDLE;
+					return 0;
+				}
+				cks->mhdr_pos = cks->mhdr_pos*10 + c - '0';
+				if (cks->mhdr_pos > 99) {
+					cks->mhdr_st = CK_MHDR_ST_IDLE;
+					return 0;
+				}
+			}
+		case CK_MHDR_ST_MULTIPART:
+			/* We are looking for "Content-type: Multipart"
+			 * after having seen "Content-type: M".
+			 * If it is not "ultipart", assume "essage" and that
+			 * it is text. */
+			cks->mhdr_st = CK_MHDR_ST_TEXT;
+			MMATCH("multipart", BND_SKIP_PARAM, IDLE);
+			break;
+		case CK_MHDR_ST_BND_SKIP_PARAM:
+			/* Look for semicolon or whitespace preceding next
+			 * parameter after "Content-type: M" */
+			if (skip_param(&bp, &bp_len))
+				cks->mhdr_st = CK_MHDR_ST_BND_SPAN_WS;
+			break;
+		case CK_MHDR_ST_BND_SPAN_WS:
+			/* skip optional whitespace before next parameter */
+			if (span_ws(&bp, &bp_len))
+				cks->mhdr_st = CK_MHDR_ST_BND;
+			break;
+		case CK_MHDR_ST_BND:
+			/* we have matched "Content-type: multipart"
+			 * and are looking for the "boundary" parameter */
+			if (MMATCH("boundary=", BND_VALUE, BND_SKIP_PARAM)) {
+				if (in_hdrs) {
+					cks->mime_nest = 0;
+					/* allow missing initial blank line */
+					cks->mime_bnd_matches = 1;
+				}
+				bndp = &cks->mime_bnd[cks->mime_nest];
+				cks->flags &= ~DCC_CKS_MIME_QUOTED;
+				bndp->bnd[0] = '-';
+				bndp->bnd[1] = '-';
+				cks->mhdr_pos = 2;
+			}
+			break;
+		case CK_MHDR_ST_BND_VALUE:
+			/* collect the bounary string */
+			bndp = &cks->mime_bnd[cks->mime_nest];
+			/* this accepts a lot more than RFC 2046 allows,
+			 * but spamware written by idiots doesn't comply */
+			for (;;) {
+				if (bp_len == 0)
+					return 0;
+				--bp_len;
+				c = *bp++;
+				if (c == '\n')
+					break;
+				if (c == '\r')
+					continue;
+				if ((c == ' ' || c == '\t' || c == ';')
+				    && !(cks->flags & DCC_CKS_MIME_QUOTED))
+					break;
+				if (c == '"') {
+					cks->flags ^= DCC_CKS_MIME_QUOTED;
+					continue;
+				}
+				bndp->bnd[cks->mhdr_pos] = c;
+				if (++cks->mhdr_pos >= DCC_CK_BND_MAX) {
+					cks->mhdr_st = CK_MHDR_ST_IDLE;
+					return 0;
+				}
+			}
+			bndp->bnd_len = cks->mhdr_pos;
+			bndp->cmp_len = 0;
+			++cks->mime_nest;
+			cks->mhdr_st = CK_MHDR_ST_IDLE;
+			break;
+		}
+	} while (bp_len != 0);
+	return 0;
+
+#undef MMATCH
+#undef MKSIP_WS
+}
+
+
+
+/* fetch bytes and convert from quoted-printable */
+u_int					/* output length */
+dcc_ck_qp_decode(DCC_GOT_CKS *cks, const char **ibufp, u_int *ibuf_lenp,
+		 char *obuf, u_int obuf_len)
+{
+#	define GC(c) do {if (!ibuf_len) return result;		\
+	--ibuf_len; (c) = *ibuf; ++ibuf;} while (0)
+	u_int ibuf_len, result;
+	const char *ibuf;
+	u_char c = 0;
+
+	if (obuf_len == 0)
+		return 0;
+	ibuf_len = *ibuf_lenp;
+	ibuf = *ibufp;
+	result = 0;
+	while (ibuf_len != 0) {
+		switch (cks->qp.state) {
+		case DCC_CK_QP_IDLE:
+			GC(c);
+			if (c != '=')
+				break;
+			cks->qp.state = DCC_CK_QP_EQ;
+			continue;
+
+		case DCC_CK_QP_EQ:
+			/* Consider first character after '=' */
+			GC(c);
+			cks->qp.x = c;
+			if (c == '\r') {
+				;
+			} else if (c == '\n') {
+				/* delete "=\n" like "=\r\n"
+				 * so that dccproc and dccm agree */
+				cks->qp.state = DCC_CK_QP_IDLE;
+				continue;
+			} else if (c >= '0' && c <= '9') {
+				cks->qp.n = c-'0';
+			} else if (c >= 'a' && c <= 'f') {
+				cks->qp.n = c-('a'-10);
+			} else if (c >= 'A' && c <= 'F') {
+				cks->qp.n = c-('A'-10);
+			} else {
+				cks->qp.state = DCC_CK_QP_FAIL1;
+				c = '=';
+				break;
+			}
+			cks->qp.state = DCC_CK_QP_1;
+			continue;
+
+		case DCC_CK_QP_1:
+			/* consider second character after '=' */
+			GC(c);
+			cks->qp.y = c;
+			if (cks->qp.x == '\r') {
+				if (c == '\n') {
+					/* delete soft line-break */
+					cks->qp.state = DCC_CK_QP_IDLE;
+					continue;
+				}
+				cks->qp.state = DCC_CK_QP_FAIL2;
+				c = '=';
+				break;
+			} else if (c >= '0' && c <= '9') {
+				c -= '0';
+			} else if (c >= 'a' && c <= 'f') {
+				c -= ('a'-10);
+			} else if (c >= 'A' && c <= 'F') {
+				c -= ('A'-10);
+			} else {
+				cks->qp.state = DCC_CK_QP_FAIL2;
+				c = '=';
+				break;
+			}
+			cks->qp.state = DCC_CK_QP_IDLE;
+			c = (cks->qp.n << 4) | c;
+			break;
+
+		case DCC_CK_QP_FAIL1:
+			/* output character after '=' of a 2-character
+			 * sequence that was not quoted-printable after all */
+			cks->qp.state = DCC_CK_QP_IDLE;
+			c = cks->qp.x;
+			break;
+
+		case DCC_CK_QP_FAIL2:
+			/* output character after '=' of a 3-character
+			 * sequence that was not quoted-printable after all */
+			cks->qp.state = DCC_CK_QP_FAIL3;
+			c = cks->qp.x;
+			break;
+
+		case DCC_CK_QP_FAIL3:
+			/* output third character of a 3-character
+			 * sequence that was not quoted-printable after all */
+			cks->qp.state = DCC_CK_QP_IDLE;
+			c = cks->qp.y;
+			break;
+		}
+
+		*obuf++ = c;
+		if (++result >= obuf_len)
+			break;
+	}
+	*ibuf_lenp = ibuf_len;
+	*ibufp = ibuf;
+	return result;
+#undef GC
+}
+
+
+
+
+#define B64B	0100			/* bad */
+#define B64EQ	0101			/* '=' */
+static u_char base64_decode[128] = {
+    B64B, B64B, B64B, B64B, B64B, B64B, B64B, B64B, /* 0x00 */
+    B64B, B64B, B64B, B64B, B64B, B64B, B64B, B64B, /* 0x08 */
+    B64B, B64B, B64B, B64B, B64B, B64B, B64B, B64B, /* 0x10 */
+    B64B, B64B, B64B, B64B, B64B, B64B, B64B, B64B, /* 0x18 */
+
+    B64B, B64B, B64B, B64B, B64B, B64B, B64B, B64B, /* 0x20   ! " # $ % & ' */
+    B64B, B64B, B64B, 62,   B64B, B64B, B64B, 63,   /* 0x28 ( ) * + , - . / */
+
+    52,	  53,	54,   55,   56,   57,   58,   59,   /* 0x30 0 1 2 3 4 5 6 7 */
+    60,   61,   B64B, B64B, B64B, B64EQ,B64B, B64B, /* 0x38 8 9 : ; < = > ? */
+
+    B64B, 0,    1,    2,    3,    4,    5,    6,    /* 0x40 @ A B C D E F G */
+    7,    8,    9,    10,   11,   12,   13,   14,   /* 0x48 H I J K L M N O */
+
+    15,   16,   17,   18,   19,   20,   21,   22,   /* 0x50 P Q R S T U V W */
+    23,   24,   25,   B64B, B64B, B64B, B64B, B64B, /* 0x58 X Y Z [ \ ] ^ _ */
+
+    B64B, 26,   27,   28,   29,   30,   31,   32,   /* 0x60 ` a b c d e f g */
+    33,   34,   35,   36,   37,   38,   39,   40,   /* 0x68 h i j k l m n o */
+
+    41,   42,   43,   44,   45,   46,   47,   48,   /* 0x70 p q r s t u v w */
+    49,   50,   51,   B64B, B64B, B64B, B64B, B64B, /* 0x78 x y z { | } ~ del */
+};
+
+u_int					/* output length */
+dcc_ck_b64_decode(DCC_GOT_CKS *cks, const char **ibufp, u_int *ibuf_lenp,
+	      char *obuf, u_int obuf_len)
+{
+	u_char c;
+	const char *ibuf;
+	u_int ibuf_len, result;
+
+	if (obuf_len < 3)
+		return 0;
+	obuf_len -= 3;
+	ibuf_len = *ibuf_lenp;
+	ibuf = *ibufp;
+	result = 0;
+	while (ibuf_len != 0) {
+		--ibuf_len;
+		c = *ibuf++;
+		c = base64_decode[c];
+		if (c == B64B)
+			continue;
+
+		if (c == B64EQ) {
+			switch (cks->b64.quantum_cnt) {
+			case 2:
+				*obuf++ = cks->b64.quantum>>4;
+				++result;
+				break;
+			case 3:
+				*obuf++ = cks->b64.quantum>>10;
+				*obuf++ = cks->b64.quantum>>2;
+				result += 2;
+				break;
+			}
+			cks->b64.quantum_cnt = 0;
+			if (result >= obuf_len)
+				break;
+		}
+
+		cks->b64.quantum = (cks->b64.quantum << 6) | c;
+		if (++cks->b64.quantum_cnt >= 4) {
+			cks->b64.quantum_cnt = 0;
+			*obuf++ = cks->b64.quantum>>16;
+			*obuf++ = cks->b64.quantum>>8;
+			*obuf++ = cks->b64.quantum;
+			result += 3;
+			if (result >= obuf_len)
+				break;
+		}
+	}
+	*ibuf_lenp = ibuf_len;
+	*ibufp = ibuf;
+	return result;
+}
+
+
+
+/* skip parts of URLs */
+int
+dcc_ck_url(DCC_URL_SKIP *url, char c, char **pbufp)
+{
+#define RET_C(s) return ((c<<DCC_CK_URL_SHIFT) | s)
+
+	/* Continue skipping a URL to its end.
+	 * Assume the end is the next blank, comma, '>', or '\n'
+	 * unless the URL is quoted.  Then continue to the quote
+	 * or until the length has become silly. */
+
+	/* convert ASCII upper to lower case */
+	if (c >= 'A' && c <= 'Z')
+		c -= 'A' - 'a';
+
+	switch (url->st) {
+	case DCC_URL_ST_IDLE:
+		if (c == 'h') {
+			/* start looking for 't' after 'h' in "http" */
+			url->flags = 0;
+			url->st = DCC_URL_ST_T1;
+		} else if (c == '=') {
+			/* look for the '=' in "href=" or "img src=" */
+			url->st = DCC_URL_ST_QUOTE;
+		}
+		RET_C(DCC_CK_URL_CHAR);
+
+	case DCC_URL_ST_QUOTE:
+		/* look for '"' or 'H' after "href=" or "img src= */
+		if (c == 'h') {
+			url->flags &= ~DCC_URL_QUOTES;
+			url->st = DCC_URL_ST_T1;
+		} else if (c == '"') {
+			url->flags |= DCC_URL_DQUOTED;
+			url->st = DCC_URL_ST_QH;
+		} else if (c == '\'') {
+			url->flags |= DCC_URL_SQUOTED;
+			url->st = DCC_URL_ST_QH;
+		} else {
+			url->st = DCC_URL_ST_IDLE;
+		}
+		RET_C(DCC_CK_URL_CHAR);
+
+	case DCC_URL_ST_QH:
+		/* seen quote; looking for start of URL */
+		if (c == 'h') {
+			url->st = DCC_URL_ST_T1;
+		} else {
+			url->st = DCC_URL_ST_IDLE;
+		}
+		RET_C(DCC_CK_URL_CHAR);
+
+	case DCC_URL_ST_T1:
+		if (c == 't')
+			url->st = DCC_URL_ST_T2;
+		else
+			url->st = DCC_URL_ST_IDLE;
+		RET_C(DCC_CK_URL_CHAR);
+
+	case DCC_URL_ST_T2:
+		if (c == 't')
+			url->st = DCC_URL_ST_P;
+		else
+			url->st = DCC_URL_ST_IDLE;
+		RET_C(DCC_CK_URL_CHAR);
+
+	case DCC_URL_ST_P:
+		if (c == 'p')
+			url->st = DCC_URL_ST_S;
+		else
+			url->st = DCC_URL_ST_IDLE;
+		RET_C(DCC_CK_URL_CHAR);
+
+	case DCC_URL_ST_S:
+		/* we are expecting the ':' or 's' after http */
+		if (c == 's')
+			url->st = DCC_URL_ST_COLON;
+		else if (c == ':')
+			url->st = DCC_URL_ST_SLASH1;
+		else
+			url->st = DCC_URL_ST_IDLE;
+		RET_C(DCC_CK_URL_CHAR);
+
+	case DCC_URL_ST_COLON:
+		/* we are expecting the ':' after http or https */
+		if (c == ':')
+			url->st = DCC_URL_ST_SLASH1;
+		else
+			url->st = DCC_URL_ST_IDLE;
+		RET_C(DCC_CK_URL_CHAR);
+
+	case DCC_URL_ST_SLASH1:
+		/* we are expecting the first '/' after http: */
+		if (c == '/')
+			url->st = DCC_URL_ST_SLASH2;
+		else
+			url->st = DCC_URL_ST_IDLE;
+		RET_C(DCC_CK_URL_CHAR);
+
+	case DCC_URL_ST_SLASH2:
+		/* we are expecting the second '/' after http:/" */
+		if (c != '/') {
+			url->st = DCC_URL_ST_IDLE;
+			RET_C(DCC_CK_URL_CHAR);
+		}
+		url->st = DCC_URL_ST_SLASH3_START;
+		RET_C(DCC_CK_URL_CK_LEN);
+
+	case DCC_URL_ST_SLASH3_START:
+		url->dot = 0;
+		url->start = *pbufp;
+		url->total = 0;
+		url->flags &= ~(DCC_URL_DEL_DOMAIN
+				| DCC_URL_PERCENT1 | DCC_URL_PERCENT2);
+		url->st = DCC_URL_ST_SLASH3;
+		/* fall into DCC_URL_ST_SLASH3 */
+	case DCC_URL_ST_SLASH3:
+		/* look for the end of the host name */
+		++url->total;
+again:
+		if (c == '.') {
+			/* keep only 1st and 2nd level domain names */
+			url->flags &= ~DCC_URL_DEL_DOMAIN;
+			if (!url->dot) {
+				/* do nothing at first '.' unless the name
+				 * was too long */
+				if (*pbufp >= url->start+DCC_URL_MAX) {
+					*pbufp = url->start;
+				} else {
+					url->dot = *pbufp;
+				}
+			} else {
+				url->flags |= DCC_URL_DEL_DOMAIN;
+			}
+			RET_C(DCC_CK_URL_DOT);
+		}
+		/* delay deleting third level domains to not be
+		 * fooled by a trailing dot */
+		if (url->flags & DCC_URL_DEL_DOMAIN) {
+			url->flags &= ~DCC_URL_DEL_DOMAIN;
+			memmove(url->start, url->dot,
+				*pbufp - url->dot);
+			*pbufp -= (url->dot - url->start);
+			url->dot = *pbufp;
+		}
+
+		if (c == '/') {
+			url->st = DCC_URL_ST_SKIP;
+			RET_C(DCC_CK_URL_HOST_END);
+		}
+		if (c == '"' && (url->flags & DCC_URL_DQUOTED)) {
+			url->st = DCC_URL_ST_IDLE;
+			RET_C(DCC_CK_URL_HOST_END);
+		}
+		if (c == '\'' && (url->flags & DCC_URL_SQUOTED)) {
+			url->st = DCC_URL_ST_IDLE;
+			RET_C(DCC_CK_URL_HOST_END);
+		}
+		if ((c == '<' || c == '>')
+		    && (url->flags & DCC_URL_QUOTES) == 0) {
+			url->st = DCC_URL_ST_IDLE;
+			RET_C(DCC_CK_URL_HOST_END);
+		}
+		if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
+			if (!(url->flags & DCC_URL_QUOTED)
+			    || url->total > DCC_URL_FAILSAFE) {
+				url->st = DCC_URL_ST_IDLE;
+				RET_C(DCC_CK_URL_HOST_END);
+			}
+			/* whitespace in a URL hostname is at best username */
+			*pbufp = url->start;
+			url->st = DCC_URL_ST_SLASH3_START;
+			RET_C(DCC_CK_URL_HOST_RESET);
+		}
+		if (c == '@') {
+			/* ignore username and password */
+			*pbufp = url->start;
+			url->st = DCC_URL_ST_SLASH3_START;
+			RET_C(DCC_CK_URL_HOST_RESET);
+		}
+
+		if (c == '%') {
+			url->flags &= ~DCC_URL_PERCENT2;
+			url->flags |= DCC_URL_PERCENT1;
+			RET_C(DCC_CK_URL_SKIP);
+		}
+		if (url->flags & DCC_URL_PERCENT1) {
+			if (c >= '0' && c <= '9') {
+				c -= '0';
+			} else if (c >= 'a' && c <= 'f') {
+				c -= 'a'-10;
+			} else {
+				*pbufp = url->start;
+				url->st = DCC_URL_ST_SLASH3_START;
+				RET_C(DCC_CK_URL_HOST_RESET);
+			}
+			if (url->flags & DCC_URL_PERCENT2) {
+				url->flags &= ~(DCC_URL_PERCENT1
+						| DCC_URL_PERCENT2);
+				c |= url->percent;
+				if (c >= 'A' && c <= 'Z')
+					c -= 'A' - 'a';
+				goto again;
+			}
+			url->percent = c << 4;
+			url->flags |= DCC_URL_PERCENT2;
+			RET_C(DCC_CK_URL_SKIP);
+		}
+
+		if (*pbufp >= url->start+DCC_URL_MAX) {
+			/* long garbage is probably a username */
+			if (url->total > DCC_URL_FAILSAFE) {
+				url->st = DCC_URL_ST_IDLE;
+				RET_C(DCC_CK_URL_CHAR);
+			}
+			RET_C(DCC_CK_URL_SKIP);
+		}
+		RET_C(DCC_CK_URL_HOST);
+
+	case DCC_URL_ST_SKIP:
+		/* skip the rest of the URL */
+		++url->total;
+		if (c == '"' || c == '\'') {
+			url->st = DCC_URL_ST_IDLE;
+			RET_C(DCC_CK_URL_SKIP);
+		}
+		if ((c == '>' || c == ' ' || c == '\t'
+		    || c == '\n' || c == '\r')
+		    && (!(url->flags & DCC_URL_QUOTES)
+			|| url->total > DCC_URL_FAILSAFE)) {
+			url->total = 0;
+			url->st = DCC_URL_ST_IDLE;
+			RET_C(DCC_CK_URL_CHAR);
+		}
+		RET_C(DCC_CK_URL_SKIP);
+	}
+	RET_C(DCC_CK_URL_CHAR);
+
+#undef RET_C
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/ckparse.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/ckparse.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,285 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * parse a named checksum
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.67 $Revision$
+ */
+
+#include "dcc_ck.h"
+#include "dcc_xhdr.h"
+#ifndef DCC_WIN32
+#include <arpa/inet.h>
+#endif
+
+#define MAX_SERVER_CIDR_BITS 16		/* fix dcc.8 if this changes */
+
+
+static int				/* -1=fatal 0=problem */
+dcc_host2ck(DCC_EMSG emsg, DCC_WF *wf,
+	    const char *ck,		/* this string of an IP address */
+	    DCC_TGTS tgts,		/* # of targets */
+	    DCC_PARSED_CK_FNC fnc,	/* do something with each checksum */
+	    DCC_PARSED_CK_CIDR_FNC cidr_fnc)
+{
+	int error;
+	DCC_FNM_LNO_BUF fnm_buf;
+	struct in6_addr addr6, mask6, *addr6p;
+	DCC_SUM sum;
+	DCC_SOCKU *sup;
+	int i, j, bits;
+
+	/* recognize xxx.xxx.xxx.xxx/y for IP address blocks
+	 * as well as simple IP addresses */
+	bits = dcc_str2cidr(emsg, &addr6, &mask6, 0, ck,
+			    wf_fnm(wf, wf->fno), wf->lno);
+	if (bits < 0)
+		return 0;
+
+	if (bits > 0) {
+		/* use client CIDR blocks if possible */
+		if (cidr_fnc)
+			return cidr_fnc(emsg, wf, bits, &addr6, &mask6, tgts);
+
+		/* This is for a server whitelist, because client whitelists
+		 * come here with non-null cidr_fnc
+		 * Allow only class-B sized blocks, because server whitelist
+		 * entries for a CIDR block consist of one checksum per IP
+		 * address in the CIDR block.  A line in a whitelist file
+		 * specifying a class-B block requires the addition of 65,536
+		 * checksums to the server database.  Instead, use whiteclnt
+		 * entries. */
+		if (128-bits > MAX_SERVER_CIDR_BITS) {
+			dcc_pemsg(EX_NOHOST, emsg,
+				  "CIDR block length in %s too large%s",
+				  ck, wf_fnm_lno(&fnm_buf, wf));
+			return 0;
+		}
+
+		for (i = 1 << (128-bits); i > 0; --i) {
+			dcc_ck_ipv6(sum, &addr6);
+			j = fnc(emsg, wf, DCC_CK_IP, sum, tgts);
+			if (j <= 0)
+				return j;
+			addr6.s6_addr32[3] = ntohl(addr6.s6_addr32[3]);
+			++addr6.s6_addr32[3];
+			addr6.s6_addr32[3] = htonl(addr6.s6_addr32[3]);
+		}
+		return 1;
+	}
+
+	/* we appear to have a host name,
+	 * which is not allowed in a per-user whitelist */
+	if (wf->wf_flags & DCC_WF_PER_USER) {
+		dcc_pemsg(EX_NOHOST, emsg,
+			  "hostname checksum illegal in per-user whitelist%s",
+			  wf_fnm_lno(&fnm_buf, wf));
+		return 0;
+	}
+
+	if (wf->wtbl)			/* need future host name resolutions */
+		wf->wtbl->hdr.flags |= DCC_WHITE_FG_HOSTNAMES;
+
+	memset(&addr6, 0, sizeof(addr6));
+	dcc_host_lock();
+	/* don't use SOCKS host name resolution because the host names that
+	 * most need whitelisting are inside the SOCKS firewall and may not
+	 * be known to outside DNS servers. */
+	if (!dcc_get_host(ck, 2, &error)) {
+		dcc_pemsg(EX_NOHOST, emsg,
+			  "hostname \"%s\": %s%s",
+			  ck, DCC_HSTRERROR(error),
+			  wf_fnm_lno(&fnm_buf, wf));
+		dcc_host_unlock();
+		return 0;
+	}
+
+	for (sup = dcc_hostaddrs; sup < dcc_hostaddrs_end; ++sup) {
+		if (sup->sa.sa_family == AF_INET6) {
+			addr6p = &sup->ipv6.sin6_addr;
+		} else {
+			dcc_ipv4toipv6(&addr6, sup->ipv4.sin_addr);
+			addr6p = &addr6;
+		}
+		if (cidr_fnc) {
+			bits = 128;
+			dcc_bits2mask(&mask6, bits);
+			j = cidr_fnc(emsg, wf, bits, addr6p, &mask6, tgts);
+		} else {
+			dcc_ck_ipv6(sum, addr6p);
+			j = fnc(emsg, wf, DCC_CK_IP, sum, tgts);
+		}
+		if (j <= 0) {
+			dcc_host_unlock();
+			return j;
+		}
+	}
+	dcc_host_unlock();
+	return 1;
+}
+
+
+
+/* generate checksum value from the name of the checksum and a string */
+int					/* 1=ok 0=problem -1=fatal */
+dcc_parse_ck(DCC_EMSG emsg,		/* failure message here */
+	     DCC_WF *wf,
+	     const char *type_nm,
+	     DCC_CK_TYPES type,
+	     const char *str,		/* ASCII string to generate checksum */
+	     DCC_TGTS tgts,		/* # of targets */
+	     DCC_PARSED_CK_FNC add_fnc,	/* do something with the checksum */
+	     DCC_PARSED_CK_CIDR_FNC cidr_fnc)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	char *phdr, c, hdr_buf[80];
+	DCC_SUM sum;
+	const char *pstr;
+
+	/* compute the checksum */
+	switch (type) {
+	case DCC_CK_IP:
+		return dcc_host2ck(emsg, wf, str, tgts, add_fnc, cidr_fnc);
+
+	case DCC_CK_ENV_FROM:
+	case DCC_CK_FROM:
+	case DCC_CK_MESSAGE_ID:
+	case DCC_CK_RECEIVED:
+	case DCC_CK_ENV_TO:
+		dcc_str2ck(sum, 0, 0, str);
+		return add_fnc(emsg, wf, type, sum, tgts);
+
+	case DCC_CK_SUB:
+		str += strspn(str, DCC_WHITESPACE);
+		pstr = str;
+		phdr = hdr_buf;
+		for (;;) {
+			c = *pstr++;
+			if (c == '\0' || c == ':'
+			    || DCC_IS_WHITE(c))
+				break;
+			c = DCC_TO_LOWER(c);
+			*phdr++ = c;
+			if (phdr >= &hdr_buf[sizeof(hdr_buf)]) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  " imposible substitute header name"
+					  " in \"%s\"%s",
+					  str, wf_fnm_lno(&fnm_buf, wf));
+				return 0;
+			}
+		}
+		pstr += strspn(pstr, DCC_WHITESPACE);
+		if (*pstr == '\0' || phdr == hdr_buf) {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  " substitute header name absent in \"%s\"%s",
+				  str, wf_fnm_lno(&fnm_buf, wf));
+			return 0;
+		}
+		dcc_str2ck(sum, hdr_buf, phdr-hdr_buf, pstr);
+		return add_fnc(emsg, wf, type, sum, tgts);
+
+	case DCC_CK_INVALID:
+	case DCC_CK_BODY:
+	case DCC_CK_FUZ1:
+	case DCC_CK_FUZ2:
+	case DCC_CK_G_MSG_R_TOTAL:
+	case DCC_CK_G_TRIPLE_R_BULK:
+	case DCC_CK_SRVR_ID:
+		break;
+	}
+
+	dcc_pemsg(EX_DATAERR, emsg, "unrecognized checksum type \"%s\"%s",
+		  type_nm, wf_fnm_lno(&fnm_buf, wf));
+	return 0;
+}
+
+
+
+/* generate checksum value from the name of the checksum and hex values */
+int					/* 1=ok 0=syntax -1=fatal */
+dcc_parse_hex_ck(DCC_EMSG emsg,		/* failure message here */
+		 DCC_WF *wf,
+		 const char *type_nm,
+		 DCC_CK_TYPES type,
+		 const char *str,	/* ASCII string to generate checksum */
+		 DCC_TGTS tgts,		/* # of targets */
+		 DCC_PARSED_CK_FNC add_fnc) /* do something with the checksum */
+{
+	union {
+	    u_int32_t n[4];
+	    DCC_SUM sum;
+	} u;
+	DCC_FNM_LNO_BUF fnm_buf;
+	int id;
+
+	if (type == DCC_CK_INVALID) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "unrecognized checksum type \"%s\"%s",
+			  type_nm, wf_fnm_lno(&fnm_buf, wf));
+		return 0;
+	}
+
+	if (4 == sscanf(str, DCC_CKSUM_HEX_PAT"\n",
+			&u.n[0], &u.n[1], &u.n[2], &u.n[3])
+	    || (type == DCC_CK_SUB
+		&& 4 == sscanf(str, "%*s "DCC_CKSUM_HEX_PAT"\n",
+			       &u.n[0], &u.n[1], &u.n[2], &u.n[3]))) {
+		/* recognize simple hex checksums */
+		u.n[0] = htonl(u.n[0]);
+		u.n[1] = htonl(u.n[1]);
+		u.n[2] = htonl(u.n[2]);
+		u.n[3] = htonl(u.n[3]);
+
+	} else if (1 == sscanf(str, DCC_XHDR_ID_SIMPLE" at %d", &id)
+		   || 1 == sscanf(str, DCC_XHDR_ID_REP_OK" at %d", &id)
+		   || 1 == sscanf(str, DCC_XHDR_ID_IGNORE" at %d", &id)
+		   || 1 == sscanf(str, DCC_XHDR_ID_ROGUE" at %d", &id)) {
+		/* parse server-ID declarations */
+		memset(&u, 0, sizeof(u));
+		u.sum[0] = DCC_CK_SRVR_ID;
+		u.sum[1] = id >> 8;
+		u.sum[2] = id;
+
+	} else {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "unrecognized checksum value \"%s\"%s",
+			  str, wf_fnm_lno(&fnm_buf, wf));
+		return 0;
+	}
+
+	/* apply the function to the checksum */
+	return add_fnc(emsg, wf, type, u.sum, tgts);
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/ckwhite.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/ckwhite.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,1608 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * check checksums in the local whitelist
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.133 $Revision$
+ */
+
+#include "dcc_ck.h"
+
+
+static const DCC_WHITE_MAGIC white_magic = WHITE_MAGIC_B_STR WHITE_MAGIC_V_STR;
+
+#define EMPTY_WHITE_SIZE    (sizeof(DCC_WHITE_TBL) - sizeof(DCC_WHITE_ENTRY))
+#define MAX_WHITE_ENTRIES   (DCC_WHITE_TBL_BINS*10)
+#define ENTRIES2SIZE0(_l)   (sizeof(DCC_WHITE_TBL)			\
+			     - sizeof(DCC_WHITE_ENTRY)			\
+			     + ((_l) * sizeof(DCC_WHITE_ENTRY)))
+#ifdef DCC_WIN32
+	/* Make the hash table files maximum size on WIN32.
+	 * You cannot change the size of a WIN32 mapping object without
+	 * getting all processes using it to release it so that it can be
+	 * recreated.  This may cause problems if the size of hash table
+	 * header changes.
+	 * Since the file does not change size, there is no need to remap it */
+#define ENTRIES2SIZE(_l)   ENTRIES2SIZE0(MAX_WHITE_ENTRIES)
+#else
+#define ENTRIES2SIZE(_l)   ENTRIES2SIZE0(_l)
+#endif
+
+
+
+void
+dcc_wf_init(DCC_WF *wf,
+	    u_int wf_flags)		/* DCC_WF_* */
+{
+	memset(wf, 0, sizeof(*wf));
+	wf->ht_fd = -1;
+	wf->wf_flags = wf_flags & ~DCC_WF_NOFILE;
+}
+
+
+
+/* this is needed only on systems without coherent mmap()/read()/write() */
+static void
+sync_white(DCC_WF *wf)
+{
+	if (wf->wtbl
+	    && 0 > MSYNC(wf->wtbl, wf->wtbl_size, MS_SYNC))
+		dcc_error_msg("msync(%s): %s", wf->ht_nm, ERROR_STR());
+}
+
+
+
+#define STILL_BROKE(wf,now) ((wf)->broken != 0				\
+			     && !DCC_IS_TIME(now, (wf)->broken,		\
+					  DCC_WHITE_BROKEN_DELAY))
+static void
+is_broken(DCC_WF *wf)
+{
+	wf->broken = time(0) + DCC_WHITE_BROKEN_DELAY;
+
+	/* This is racy, but the worst that can happen is that
+	 * another process races to set the retry timer */
+	if (!(wf->wf_flags & DCC_WF_RO) && wf->wtbl) {
+		wf->wtbl->hdr.broken = wf->broken;
+		sync_white(wf);
+	}
+}
+
+
+
+static void
+unmap_white_ht(DCC_WF *wf)
+{
+	if (!wf->wtbl)
+		return;
+
+	sync_white(wf);
+#ifdef DCC_WIN32
+	win32_unmap(&wf->ht_map, wf->wtbl, wf->ht_nm);
+#else
+	if (0 > munmap((void *)wf->wtbl, wf->wtbl_size))
+		dcc_error_msg("munmap(%s,%d): %s",
+			      wf->ht_nm, wf->wtbl_size, ERROR_STR());
+#endif
+	wf->wtbl = 0;
+}
+
+
+
+static u_char				/* 1=done, 0=failed */
+map_white_ht(DCC_EMSG emsg, DCC_WF *wf, DCC_WHITE_INX entries)
+{
+	size_t size;
+#ifndef DCC_WIN32
+	void *p;
+#endif
+
+	unmap_white_ht(wf);
+
+	if (entries > MAX_WHITE_ENTRIES) {
+		dcc_pemsg(EX_IOERR, emsg, "%s should not contain %d entries",
+			  wf->ht_nm, entries);
+		return 0;
+	}
+
+	size = ENTRIES2SIZE(entries);
+#ifdef DCC_WIN32
+	if (!wf->wtbl) {
+		wf->wtbl = win32_map(emsg, &wf->ht_map, wf->ht_nm, wf->ht_fd,
+				     size);
+		if (!wf->wtbl)
+			return 0;
+	}
+#else
+	p = mmap(0, size,
+		 (wf->wf_flags & DCC_WF_RO)
+		 ? PROT_READ : (PROT_READ|PROT_WRITE),
+		 MAP_SHARED, wf->ht_fd, 0);
+	if (p == MAP_FAILED) {
+		dcc_pemsg(EX_IOERR, emsg, "mmap(%s,%d): %s",
+			  wf->ht_nm, (int)size, ERROR_STR());
+		return 0;
+	}
+	wf->wtbl = p;
+#endif
+	wf->wtbl_size = size;
+	wf->wtbl_entries = entries;
+	wf->wtbl_flags = wf->wtbl->hdr.flags;
+
+	return 1;
+}
+
+
+
+static u_char
+close_white_ht(DCC_EMSG emsg, DCC_WF *wf)
+{
+	u_char result = 1;
+
+	if (wf->ht_fd < 0)
+		return result;
+	
+	wf->closed = 1;
+
+	unmap_white_ht(wf);
+
+#ifdef DCC_WIN32
+	/* unlock the file before closing it to keep Win95 happy */
+	if (!dcc_unlock_fd(emsg, wf->ht_fd, DCC_LOCK_ALL_FILE,
+			   "whitelist ", wf->ht_nm))
+		result = 0;
+#endif
+	if (0 > close(wf->ht_fd)) {
+		dcc_pemsg(EX_IOERR, emsg, "close(%s): %s",
+			  wf->ht_nm, ERROR_STR());
+		result = 0;
+	}
+
+	wf->ht_fd = -1;
+	memset(&wf->ht_sb, 0, sizeof(wf->ht_sb));
+#ifndef DCC_WIN32
+	wf->ht_sb.st_dev = -1;
+	wf->ht_sb.st_ino = -1;
+#endif
+	return result;
+}
+
+
+
+static int				/* 0=ok -1=failed */
+unlink_white_ht(DCC_WF *wf,
+		time_t now)		/* 0=our own new file */
+{
+	int result;
+
+	/* mark it bad if it is a brand new hash table */
+	if (!now && wf->wtbl && !(wf->wf_flags & DCC_WF_RO))
+		wf->wtbl->hdr.ascii_mtime = 0;
+
+#ifdef DCC_WIN32
+	/* racy but you cannot unlink an open file on WIN32 */
+	close_white_ht(0, wf);
+#endif
+	result = -1;
+	if (!(wf->wf_flags & DCC_WF_RO)) {
+		if (0 <= unlink(wf->ht_nm)) {
+			result = 0;
+#ifndef DCC_WIN32
+		} else if ((errno == EACCES || errno == EPERM)
+			   && dcc_get_priv_home(wf->ht_nm)) {
+			if (0 <= unlink(wf->ht_nm))
+				result = 0;
+			dcc_rel_priv();
+#endif
+		}
+		if (result < 0)
+			dcc_error_msg("unlink(%s): %s",
+				      wf->ht_nm, ERROR_STR());
+	}
+
+	/* If we failed to unlink the old hash table,
+	 * remember in core that things are broken but do not touch the file
+	 * in case it is a link to /etc/passwd or something else dangerous */
+	if (result < 0 && now)
+		wf->broken = now + DCC_WHITE_BROKEN_DELAY;
+#ifndef DCC_WIN32
+	close_white_ht(0, wf);
+#endif
+
+	return result;
+}
+
+
+
+static u_char
+new_ht_nm(DCC_EMSG emsg, DCC_WF *wf, u_char new)
+{
+	if (wf->ascii_nm_len >= ISZ(wf->ht_nm) - LITZ(DCC_WHITE_SUFFIX)) {
+		dcc_pemsg(EX_NOINPUT, emsg, "bad whitelist file name \"%s\"",
+			  wf->ascii_nm);
+		is_broken(wf);
+		return 0;
+	}
+
+	memcpy(wf->ht_nm, wf->ascii_nm, wf->ascii_nm_len);
+	strcpy(&wf->ht_nm[wf->ascii_nm_len],
+	       new ? DCC_WHITE_NEW_SUFFIX : DCC_WHITE_SUFFIX);
+	return 1;
+}
+
+
+
+u_char
+dcc_new_white_nm(DCC_EMSG emsg, DCC_WF *wf,
+		 const char *new_white_nm)
+{
+	DCC_PATH new_path;
+	const char *new;
+	u_int len;
+	int i;
+
+	if (!strcmp(new_white_nm, wf->ascii_nm))
+		return 1;
+
+	close_white_ht(0, wf);
+	dcc_wf_init(wf, wf->wf_flags);
+
+	if (!fnm2rel(new_path, new_white_nm, 0)) {
+		dcc_pemsg(EX_USAGE, emsg, "bad whitelist name \"%s\"",
+			  new_white_nm);
+		return 0;
+	}
+
+	len = strlen(new_path);
+	i = len - LITZ(DCC_WHITE_SUFFIX);
+	if (i > 0 && (!strcmp(&new_path[i], DCC_WHITE_SUFFIX)
+		      || !strcmp(&new_path[i], DCC_WHITE_NEW_SUFFIX))) {
+		len = i;
+		new_path[len] = '\0';
+	}
+
+	if (len > ISZ(wf->ht_nm) - ISZ(DCC_WHITE_SUFFIX)) {
+		dcc_pemsg(EX_USAGE, emsg, "long whitelist name \"%s\"",
+			  new_white_nm);
+		return 0;
+	}
+
+	new = path2fnm(new_path);
+	len = strlen(new);
+	memcpy(wf->ascii_nm, new, len+1);
+	wf->ascii_nm_len = len;
+	return 1;
+}
+
+
+
+/* open and shared-lock a hash table file */
+static int				/* -1=fatal 0=rebuild, 1=ok */
+open_white_ht(DCC_EMSG emsg, DCC_WF *wf)
+{
+	int size;
+	DCC_WHITE_INX entries;
+
+	close_white_ht(0, wf);
+
+	if (!new_ht_nm(emsg, wf, 0))
+		return -1;
+
+	wf->ht_fd = dcc_lock_open(emsg, wf->ht_nm,
+				  (wf->wf_flags & DCC_WF_RO)
+				  ? O_RDONLY : O_RDWR,
+				  DCC_LOCK_OPEN_SHARE,
+				  DCC_LOCK_ALL_FILE, 0);
+	if (wf->ht_fd < 0) {
+		/* if this is a `wlist` command and neither -P nor -Q
+		 * were specified, try to open the file read-only */
+		if ((wf->wf_flags & DCC_WF_WLIST)
+		    && !(wf->wf_flags & (DCC_WF_WLIST_RO | DCC_WF_WLIST_RW))) {
+			if (emsg)
+				emsg[0] = '\0';
+			wf->wf_flags |= DCC_WF_RO;
+			wf->ht_fd = dcc_lock_open(emsg, wf->ht_nm,
+						  O_RDONLY,
+						  DCC_LOCK_OPEN_SHARE,
+						  DCC_LOCK_ALL_FILE, 0);
+		}
+		if (wf->ht_fd < 0)
+			return 0;
+	}
+
+	if (fstat(wf->ht_fd, &wf->ht_sb) < 0) {
+		dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s",
+			  wf->ht_nm, ERROR_STR());
+		close_white_ht(0, wf);
+		is_broken(wf);
+		return -1;
+	}
+
+	size = wf->ht_sb.st_size - EMPTY_WHITE_SIZE;
+	if (size < 0) {
+		dcc_pemsg(EX_NOINPUT, emsg,
+			  "%s is too small to be a DCC whitelist hash table",
+			  wf->ht_nm);
+		return unlink_white_ht(wf, time(0));
+
+	} else {
+		entries = size / sizeof(DCC_WHITE_ENTRY);
+		if (!map_white_ht(emsg, wf, entries))
+			return unlink_white_ht(wf, time(0));
+	}
+
+	if (memcmp(&wf->wtbl->magic, &white_magic, sizeof(white_magic))) {
+		/* rebuild old format files */
+		if (!memcmp(&wf->wtbl->magic, WHITE_MAGIC_B_STR,
+			    LITZ(WHITE_MAGIC_B_STR))) {
+			if (dcc_clnt_debug)
+				dcc_trace_msg("%s is obsolete %s",
+					      wf->ht_nm, wf->wtbl->magic);
+		} else {
+			dcc_pemsg(EX_NOINPUT, emsg,
+				  "%s is not a DCC whitelist hash file",
+				  wf->ht_nm);
+		}
+		return unlink_white_ht(wf, time(0));
+	}
+
+	if ((size % sizeof(DCC_WHITE_ENTRY)) != 0
+	    || entries > MAX_WHITE_ENTRIES
+	    || entries < wf->wtbl->hdr.entries) {
+		dcc_pemsg(EX_NOINPUT, emsg,
+			  "invalid size of whitelist %s="OFF_DPAT,
+			  wf->ht_nm, wf->ht_sb.st_size);
+		return unlink_white_ht(wf, time(0));
+	}
+
+	if (wf->wtbl->hdr.ascii_mtime == 0) {
+		close_white_ht(0, wf);
+		return 0;		/* broken hash table */
+	}
+
+	/* we know the hash table is usable
+	 * but we might want to rebuild it */
+	wf->need_reopen = 0;
+	wf->wtbl_flags = wf->wtbl->hdr.flags;
+
+	/* wlist and dccproc work on both per-user and global whitelists,
+	 * so do not change the nature of the file if it is already known */
+	if (wf->wf_flags & DCC_WF_EITHER) {
+		if (wf->wtbl_flags & DCC_WHITE_FG_PER_USER)
+			wf->wf_flags |= DCC_WF_PER_USER;
+		else
+			wf->wf_flags &= ~DCC_WF_PER_USER;
+	}
+
+	return 1;
+}
+
+
+
+static void
+create_white_ht_sub(DCC_EMSG emsg, DCC_WF *new_wf, const DCC_WF *wf,
+		    u_char *busyp)
+{
+	/* do not use O_EXCL because we want to wait for any other
+	 * process to finish
+	 *
+	 * wait if and only if we do not have a usable file open */
+
+	new_wf->ht_fd = dcc_lock_open(emsg, new_wf->ht_nm,
+			   O_RDWR | O_CREAT,
+			   wf->ht_fd >= 0 ? DCC_LOCK_OPEN_NOWAIT : 0,
+			   DCC_LOCK_ALL_FILE, busyp);
+	if (new_wf->ht_fd < 0)
+		return;
+
+	/* a new hash table must be empty */
+	if (0 > fstat(new_wf->ht_fd, &new_wf->ht_sb)) {
+		dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s",
+			  new_wf->ht_nm, ERROR_STR());
+		close_white_ht(emsg, new_wf);
+		return;
+	}
+
+	if (new_wf->ht_sb.st_size != 0) {
+		dcc_pemsg(EX_IOERR, emsg, "%s has non-zero size %d",
+			  new_wf->ht_nm, (int)new_wf->ht_sb.st_size);
+		close_white_ht(emsg, new_wf);
+		return;
+	}
+}
+
+
+
+/* create and write-lock a new hash table file
+ *	wait for lock if we don't have existing file open */
+static int				/* 1=done, 0=file busy, -1=fatal */
+create_white_ht(DCC_EMSG emsg,
+		DCC_WF *tmp_wf,		/* build with this */
+		const DCC_WF *wf)	/* from this */
+{
+	u_char busy = 0;
+
+	tmp_wf->ascii_nm_len = wf->ascii_nm_len;
+	memcpy(tmp_wf->ascii_nm, wf->ascii_nm, wf->ascii_nm_len+1);
+	if (!new_ht_nm(emsg, tmp_wf, 1)) {
+		tmp_wf->ht_nm[0] = '\0';
+		return -1;
+	}
+
+	if (tmp_wf->wf_flags & DCC_WF_RO) {
+		dcc_pemsg(EX_IOERR, emsg,
+			  "read only access; cannot create or update %s",
+			  tmp_wf->ht_nm);
+		tmp_wf->ht_nm[0] = '\0';
+		return -1;
+	}
+
+#ifndef DCC_WIN32
+	/* We want to create a private hash table if the ASCII file
+	 * is private, but a hash table owned by the DCC user if the
+	 * ASCII file is public */
+	if (0 > access(tmp_wf->ascii_nm, R_OK | W_OK)
+	    && dcc_get_priv_home(tmp_wf->ht_nm)) {
+		/* first try to open a public hash table */
+		create_white_ht_sub(emsg, tmp_wf, wf, &busy);
+		if (tmp_wf->ht_fd < 0 && !busy) {
+			if (emsg && dcc_clnt_debug > 2)
+				dcc_error_msg("%s", emsg);
+			unlink(tmp_wf->ht_nm);
+			create_white_ht_sub(emsg, tmp_wf, wf, &busy);
+		}
+		dcc_rel_priv();
+	}
+#endif
+
+	if (tmp_wf->ht_fd < 0 && !busy) {
+		/* try to open or create a private hash table */
+		create_white_ht_sub(emsg, tmp_wf, wf, &busy);
+		if (tmp_wf->ht_fd < 0 && !busy) {
+			if (emsg && dcc_clnt_debug > 2)
+				dcc_error_msg("%s", emsg);
+			unlink(tmp_wf->ht_nm);
+			create_white_ht_sub(emsg, tmp_wf, wf, &busy);
+		}
+	}
+
+#ifndef DCC_WIN32
+	/* try one last time with privileges in case the ASCII file has
+	 * mode 666 but the directory does not */
+	if (tmp_wf->ht_fd < 0 && !busy) {
+		if (dcc_get_priv_home(tmp_wf->ht_nm)) {
+			if (emsg && dcc_clnt_debug > 2)
+				dcc_error_msg("%s", emsg);
+			unlink(tmp_wf->ht_nm);
+			create_white_ht_sub(emsg, tmp_wf, wf, &busy);
+			dcc_rel_priv();
+		}
+	}
+#endif
+	if (tmp_wf->ht_fd < 0) {
+		tmp_wf->ht_nm[0] = '\0';
+		if (busy)
+			return 0;
+		return -1;
+	}
+	return 1;
+}
+
+
+
+#define FIND_WHITE_BROKEN ((DCC_WHITE_ENTRY *)-1)
+static DCC_WHITE_ENTRY *
+find_white(DCC_EMSG emsg, DCC_WF *wf, DCC_CK_TYPES type, const DCC_SUM sum,
+	   DCC_WHITE_INX *binp)
+{
+	u_long accum;
+	DCC_WHITE_INX bin, inx;
+	DCC_WHITE_ENTRY *e;
+	int loop_cnt, i;
+
+	if (!wf->wtbl || wf->wtbl->hdr.ascii_mtime == 0)
+		return FIND_WHITE_BROKEN;
+
+	accum = type;
+	for (i = sizeof(DCC_SUM)-1; i >= 0; --i)
+		accum = (accum >> 28) + (accum << 4) + sum[i];
+	bin = accum % DIM(wf->wtbl->bins);
+	if (binp)
+		*binp = bin;
+	inx = wf->wtbl->bins[bin];
+
+	for (loop_cnt = wf->wtbl->hdr.entries+1;
+	     loop_cnt >= 0;
+	     --loop_cnt) {
+		if (!inx)
+			return 0;
+		--inx;
+		/* if necessary, expand the mapped window into the file */
+		if (inx >= wf->wtbl_entries) {
+			if (inx >= wf->wtbl->hdr.entries) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "bogus index %u in %s",
+					  inx, wf->ht_nm);
+				if (!(wf->wf_flags & DCC_WF_RO))
+					wf->wtbl->hdr.ascii_mtime = 0;
+				sync_white(wf);
+				return FIND_WHITE_BROKEN;
+			}
+			if (!map_white_ht(emsg, wf, wf->wtbl->hdr.entries))
+				return 0;
+		}
+		e = &wf->wtbl->tbl[inx];
+		if (e->type == type && !memcmp(e->sum, sum, sizeof(DCC_SUM)))
+			return e;
+		inx = e->fwd;
+	}
+
+	dcc_pemsg(EX_DATAERR, emsg, "chain length %d in %s"
+		  " starting at %d near %d for %s %s",
+		  wf->wtbl->hdr.entries+1,
+		  wf->ht_nm,
+		  (DCC_WHITE_INX)(accum % DIM(wf->wtbl->bins)), inx,
+		  dcc_type2str_err(type, 0, 0, 0),
+		  dcc_ck2str_err(type, sum, 0));
+
+	if (!(wf->wf_flags & DCC_WF_RO))
+		wf->wtbl->hdr.ascii_mtime = 0;
+	sync_white(wf);
+	return FIND_WHITE_BROKEN;
+}
+
+
+
+static int				/* 1=quit stat_white_nms() */
+stat_1white_nm(int *resultp,		/* set=1 if (supposedly) no change */
+	       DCC_WF *wf, const char *nm, time_t ascii_mtime)
+{
+	struct stat sb;
+	time_t now;
+
+	if (stat(nm, &sb) < 0) {
+		if (errno != ENOENT
+		    || !wf->wtbl
+		    || (wf->wf_flags & DCC_WF_RO)) {
+			dcc_trace_msg("stat(%s): %s", nm, ERROR_STR());
+			is_broken(wf);
+			*resultp = 0;
+			return 1;
+		}
+
+		/* only complain if an ASCII file disappears temporarily */
+		if (wf->broken == 0) {
+			dcc_trace_msg("%s disappeared: %s", nm, ERROR_STR());
+			is_broken(wf);
+			*resultp = 1;
+			return 1;
+		}
+		now = time(0);
+		if (STILL_BROKE(wf, now)) {
+			if (dcc_clnt_debug > 1)
+				dcc_trace_msg("ignoring stat(%s): %s",
+					      nm, ERROR_STR());
+			*resultp = 1;
+		} else {
+			if (dcc_clnt_debug > 1)
+				dcc_trace_msg("pay attention to stat(%s): %s",
+					      nm, ERROR_STR());
+			*resultp = 0;
+		}
+		return 1;
+	}
+
+	if (sb.st_mtime != ascii_mtime) {
+		*resultp = 0;
+		return 1;
+	}
+
+	return 0;
+}
+
+
+
+/* see if the ASCII files have changed */
+static int				/* 1=same 0=changed or sick -1=broken */
+stat_white_nms(DCC_EMSG emsg, DCC_WF *wf)
+{
+	struct stat ht_sb;
+	time_t now;
+	int i, result;
+
+	if (!wf->wtbl)
+		return -1;
+
+	now = time(0);
+	wf->next_stat_time = now + DCC_WHITE_STAT_DELAY;
+
+	/* Notice if the hash file has been unlinked */
+	if (stat(wf->ht_nm, &ht_sb) < 0) {
+		if (emsg && dcc_clnt_debug)
+			dcc_error_msg("stat(%s): %s",
+				      wf->ht_nm, ERROR_STR());
+		return -1;
+	}
+#ifdef DCC_WIN32
+	/* open files cannot be unlinked in WIN32, which lets us not
+	 * worry about whether WIN32 files have device and i-numbers */
+#else
+	if (wf->ht_sb.st_dev != ht_sb.st_dev
+	    || wf->ht_sb.st_ino != ht_sb.st_ino) {
+		if (emsg && dcc_clnt_debug > 2)
+			dcc_error_msg("%s disappeared", wf->ht_nm);
+		return -1;
+	}
+#endif /* DCC_WIN32 */
+	wf->ht_sb = ht_sb;
+
+	/* delays on re-parsing and complaining in the file override */
+	if (wf->reparse < wf->wtbl->hdr.reparse)
+		wf->reparse = wf->wtbl->hdr.reparse;
+	if (wf->broken < wf->wtbl->hdr.broken)
+		wf->broken = wf->wtbl->hdr.broken;
+
+	/* seriously broken hash tables are unusable */
+	if (wf->wtbl->hdr.ascii_mtime == 0)
+		return -1;
+
+	/* pretend things are fine if they are recently badly broken */
+	if (STILL_BROKE(wf, now))
+		return 1;
+
+	/* if the main ASCII file has disappeared,
+	 * leave the hash file open and just complain,
+	 * but only for a while */
+	result = 1;
+	if (stat_1white_nm(&result, wf, wf->ascii_nm,
+			   wf->wtbl->hdr.ascii_mtime))
+		return result;
+	/* see if any of the included ASCII files are new */
+	for (i = 0; i < DIM(wf->wtbl->hdr.white_incs); ++i) {
+		if (wf->wtbl->hdr.white_incs[i].nm[0] == '\0')
+			break;
+		/* stop at the first missing or changed included file */
+		if (stat_1white_nm(&result, wf,
+				   wf->wtbl->hdr.white_incs[i].nm,
+				   wf->wtbl->hdr.white_incs[i].mtime))
+			return result;
+	}
+
+	/* force periodic reparsing of syntax errors to nag in system log */
+	if (wf->reparse != 0
+	    && DCC_IS_TIME(now, wf->reparse, DCC_WHITE_REPARSE_DELAY))
+		return 0;
+
+	if ((wf->wtbl_flags & DCC_WHITE_FG_PER_USER)
+	    && !(wf->wf_flags & DCC_WF_PER_USER)) {
+		dcc_error_msg("%s is a per-user whitelist"
+			      " used as a global whitelist",
+			      wf->ht_nm);
+		return 0;
+	}
+	if (!(wf->wtbl_flags & DCC_WHITE_FG_PER_USER)
+	    && (wf->wf_flags & DCC_WF_PER_USER)) {
+		dcc_error_msg("%s is a global whitelist"
+			      " used as a per-user whitelist",
+			      wf->ht_nm);
+		return 0;
+	}
+
+	/* Checksums of SMTP client IP addresses are compared against the
+	 * checksums of the IP addresses of the hostnames in the ASCII file.
+	 * Occassionaly check for changes in DNS A RR's for entries in
+	 * the ASCII file, but only if there are host names or IP
+	 * addresses in the file */
+	if ((wf->wtbl->hdr.flags & DCC_WHITE_FG_HOSTNAMES)
+	    && DCC_IS_TIME(now, wf->ht_sb.st_mtime+DCC_WHITECLNT_RESOLVE,
+			   DCC_WHITECLNT_RESOLVE)) {
+		if (dcc_clnt_debug > 2)
+			dcc_trace_msg("time to rebuild %s", wf->ht_nm);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+static u_char
+write_white(DCC_EMSG emsg, DCC_WF *wf, const void *buf, int buf_len, off_t pos)
+{
+	int i;
+
+	if (wf->wtbl) {
+#ifdef DCC_WIN32
+		/* Windows disclaims coherence between ordinary writes
+		 * and memory mapped writing.  The hash tables are
+		 * fixed size on Windows because of problems with WIN32
+		 * mapping objects, so we do not need to worry about
+		 * extending the hash table file. */
+		memcpy((char *)wf->wtbl+pos, buf, buf_len);
+		return 1;
+#else
+		/* some UNIX systems have coherence trouble without msync() */
+		if (0 > MSYNC(wf->wtbl, wf->wtbl_size, MS_SYNC)) {
+			dcc_pemsg(EX_IOERR, emsg, "msync(%s): %s",
+				  wf->ht_nm, ERROR_STR());
+			return 0;
+		}
+#endif
+	}
+
+	i = lseek(wf->ht_fd, pos, SEEK_SET);
+	if (i < 0) {
+		dcc_pemsg(EX_IOERR, emsg, "lseek(%s,"OFF_DPAT"): %s",
+			  wf->ht_nm, pos, ERROR_STR());
+		return 0;
+	}
+	i = write(wf->ht_fd, buf, buf_len);
+	if (i != buf_len) {
+		if (i < 0)
+			dcc_pemsg(EX_IOERR, emsg, "write(%s,%d): %s",
+				  wf->ht_nm, buf_len, ERROR_STR());
+		else
+			dcc_pemsg(EX_IOERR, emsg, "write(%s,%d): %d",
+				  wf->ht_nm, buf_len, i);
+		return 0;
+	}
+	return 1;
+}
+
+
+
+static int				/* 1=ok,  0=bad entry, -1=fatal */
+add_white(DCC_EMSG emsg, DCC_WF *wf,
+	  DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts)
+{
+	DCC_WHITE_ENTRY *e, new;
+	DCC_WHITE_INX bin;
+	DCC_FNM_LNO_BUF fnm_buf;
+	off_t end;
+
+	/* find the hash chain for the new entry */
+	e = find_white(emsg, wf, type, sum, &bin);
+	if (e == FIND_WHITE_BROKEN)
+		return -1;
+
+	/* ignore duplicates on this, the first pass */
+	if (e)
+		return 1;
+
+	memset(&new, 0, sizeof(new));
+	new.type = type;
+	memcpy(new.sum, sum, sizeof(DCC_SUM));
+	new.tgts = tgts;
+	new.fwd = wf->wtbl->bins[bin];
+	new.lno = wf->lno;
+	new.fno = wf->fno;
+
+	/* Use a new entry at the end of the file
+	 * It will be beyond the memory mapped window into the file */
+	if (wf->wtbl->hdr.entries >= MAX_WHITE_ENTRIES) {
+		dcc_pemsg(EX_DATAERR, emsg, "more than maximum %d entries%s",
+			  wf->wtbl->hdr.entries, wf_fnm_lno(&fnm_buf, wf));
+		return 0;
+	}
+	end = ENTRIES2SIZE(wf->wtbl->hdr.entries);
+	wf->wtbl->bins[bin] = ++wf->wtbl->hdr.entries;
+	return write_white(emsg, wf, &new, sizeof(new), end) ? 1 : -1;
+}
+
+
+
+#define MAX_CIDR_BITS	7
+
+static int				/* 1=ok,  0=bad entry, -1=fatal */
+add_white_cidr(DCC_EMSG emsg, DCC_WF *wf,
+	       int bits,
+	       const struct in6_addr *addrp, const struct in6_addr *maskp,
+	       DCC_TGTS tgts)
+{
+	DCC_WHITE_CIDR_ENTRY *e;
+	struct in6_addr addr;
+	DCC_SUM sum;
+	DCC_FNM_LNO_BUF fnm_buf;
+	int result, i, j;
+
+	/* use individual entries for MAX_CIDR_BITS and smaller blocks */
+	if (128-bits <= MAX_CIDR_BITS) {
+		addr = *addrp;
+		result = 1;
+		for (i = 1 << (128-bits); i > 0; --i) {
+			dcc_ck_ipv6(sum, &addr);
+			j = add_white(emsg, wf, DCC_CK_IP, sum, tgts);
+			if (j <= 0) {
+				if (j < 0)
+					return j;
+				result = j;
+			}
+			addr.s6_addr32[3] = ntohl(addr.s6_addr32[3]);
+			++addr.s6_addr32[3];
+			addr.s6_addr32[3] = htonl(addr.s6_addr32[3]);
+		}
+		return result;
+	}
+
+	i = wf->wtbl->hdr.cidr.len;
+	for (e = wf->wtbl->hdr.cidr.e; i > 0; ++e, --i) {
+		/* ignore collisions on this, the first pass */
+		if (e->bits == bits
+		    && !memcmp(addrp, &e->addr, sizeof(*addrp)))
+			return 1;
+	}
+
+	if (wf->wtbl->hdr.cidr.len >= DIM(wf->wtbl->hdr.cidr.e)) {
+		dcc_pemsg(EX_DATAERR, emsg, "too many CIDR blocks%s",
+			  wf_fnm_lno(&fnm_buf, wf));
+		return 0;
+	}
+
+	e->bits = bits;
+	e->addr = *addrp;
+	e->mask = *maskp;
+	e->tgts = tgts;
+	e->lno = wf->lno;
+	e->fno = wf->fno;
+	++wf->wtbl->hdr.cidr.len;
+
+	return 1;
+}
+
+
+
+static void
+dup_msg(DCC_EMSG emsg, DCC_WF *wf, int e_fno, int e_lno,
+	DCC_TGTS e_tgts, DCC_TGTS tgts)
+{
+	char tgts_buf[30], e_tgts_buf[30];
+	const char *fname1, *fname2;
+
+	fname1 = wf_fnm(wf, wf->fno);
+	fname2 = wf_fnm(wf, e_fno);
+	dcc_pemsg(EX_DATAERR, emsg,
+		  "\"%s\" in line %d%s%s conflicts with \"%s\" in line"
+		  " %d of %s",
+		  dcc_tgts2str(tgts_buf, sizeof(tgts_buf), tgts, 0),
+		  wf->lno,
+		  fname1 != fname2 ? " of " : "",
+		  fname1 != fname2 ? fname1 : "",
+		  dcc_tgts2str(e_tgts_buf, sizeof(e_tgts_buf), e_tgts, 0),
+		  e_lno,
+		  fname2);
+}
+
+
+
+static DCC_TGTS
+combine_white_tgts(DCC_TGTS new, DCC_TGTS old)
+{
+	if (new < DCC_TGTS_TOO_MANY && old == DCC_TGTS_TOO_MANY)
+		return new;
+
+	if (new == DCC_TGTS_OK || old == DCC_TGTS_OK)
+		return DCC_TGTS_OK;
+	if (new == DCC_TGTS_OK2 || old == DCC_TGTS_OK2)
+		return DCC_TGTS_OK2;
+	if (new == DCC_TGTS_OK_MX || old == DCC_TGTS_OK_MX)
+		return DCC_TGTS_OK_MX;
+	if (new == DCC_TGTS_OK_MXDCC || old == DCC_TGTS_OK_MXDCC)
+		return DCC_TGTS_OK_MXDCC;
+	if (new == DCC_TGTS_TOO_MANY || old == DCC_TGTS_TOO_MANY)
+		return DCC_TGTS_TOO_MANY;
+	if (new == DCC_TGTS_SUBMIT_CLIENT || old == DCC_TGTS_SUBMIT_CLIENT)
+		return DCC_TGTS_SUBMIT_CLIENT;
+
+	return new;
+}
+
+
+
+static int			/* 1=ok,  0=bad entry, -1=fatal */
+ck_dup_white(DCC_EMSG emsg, DCC_WF *wf,
+	     DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts, u_char complain)
+{
+	DCC_WHITE_ENTRY *e;
+	DCC_FNM_LNO_BUF buf;
+	const char *from;
+
+	/* find the hash table entry */
+	e = find_white(emsg, wf, type, sum, 0);
+	if (e == FIND_WHITE_BROKEN)
+		return -1;
+	if (!e) {
+		/* We failed to find the hash table entry.  Either the
+		 * hash file is corrupt, the ASCII file is changing beneath
+		 * our feet, or a host name that failed to resolve the first
+		 * time we parsed the ASCII file is now resolving during
+		 * this second parsing. */
+		from = wf_fnm_lno(&buf, wf);
+		if (type == DCC_CK_IP) {
+			if (dcc_clnt_debug > 2)
+				dcc_trace_msg("%s entry (dis)appeared in %s%s",
+					      dcc_type2str_err(type, 0, 0, 0),
+					      wf->ht_nm,
+					      *from == '\0' ? "in ?" : from);
+			return 0;
+		}
+		dcc_pemsg(EX_DATAERR, emsg, "%s entry (dis)appeared in %s%s",
+			  dcc_type2str_err(type, 0, 0, 0),
+			  wf->ht_nm,
+			  *from == '\0' ? "in ?" : from);
+		return -1;
+	}
+
+	/* ignore perfect duplicates */
+	if (e->tgts == tgts)
+		return 1;
+
+	if (complain)
+		dup_msg(emsg, wf, e->fno, e->lno, e->tgts, tgts);
+
+	if (e->tgts != combine_white_tgts(tgts, e->tgts)) {
+		e->tgts = tgts;
+		e->lno = wf->lno;
+		e->fno = wf->fno;
+	}
+	return 0;
+}
+
+
+
+static int			/* 1=ok,  0=bad entry, -1=fatal */
+ck_dup_white_complain(DCC_EMSG emsg, DCC_WF *wf,
+	     DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts)
+{
+	return ck_dup_white(emsg, wf, type, sum, tgts, 1);
+}
+
+
+
+/* Without brute force checks that would take too long, it is impossible to
+ * check that a new CIDR block does not collide with individual entries that
+ * are already in the hash table.  Without expanding a big CIDR block into
+ * IP addresses and looking for each in the hash table, how can you check
+ * whether it covers an entry for an individual address?  One way is to
+ * parse the file twice and so check each individual IP address against
+ * the CIDR blocks. */
+static int				/* 1=ok,  0=bad entry, -1=fatal */
+ck_dup_white_cidr(DCC_EMSG emsg UATTRIB, DCC_WF *wf,
+		  int bits,
+		  const struct in6_addr *addrp, const struct in6_addr *maskp,
+		  DCC_TGTS tgts)
+{
+	DCC_WHITE_CIDR_ENTRY *e;
+	struct in6_addr addr;
+	DCC_SUM sum;
+	int result, i, j;
+
+	result = 1;
+
+	/* Check for collisions between CIDR blocks and either other
+	 * CIDR blocks or individual addresses.  Individual addresses
+	 * are sent here as /128 blocks. */
+	i = wf->wtbl->hdr.cidr.len;
+	for (e = wf->wtbl->hdr.cidr.e; i > 0; ++e, --i) {
+		if (!DCC_IN_BLOCK(e->addr, *addrp, *maskp)
+		    && !DCC_IN_BLOCK(*addrp, e->addr, e->mask))
+			continue;
+
+		/* ignore simple duplicates */
+		if (e->tgts == tgts)
+			continue;
+
+		dup_msg(emsg, wf, e->fno, e->lno, e->tgts, tgts);
+		result = 0;
+
+		/* fix direct collisions */
+		if (e->bits == bits
+		    && !memcmp(addrp, &e->addr, sizeof(*addrp))
+		    && e->tgts != combine_white_tgts(tgts, e->tgts)) {
+			e->tgts = tgts;
+			e->lno = wf->lno;
+			e->fno = wf->fno;
+		}
+	}
+
+	/* check and fix collisions among individual entries */
+	if (128-bits <= MAX_CIDR_BITS) {
+		addr = *addrp;
+		for (i = 1 << (128-bits); i > 0; --i) {
+			dcc_ck_ipv6(sum, &addr);
+			j = ck_dup_white(emsg, wf, DCC_CK_IP, sum, tgts,
+					 result > 0);
+			if (j < 0)
+				return j;
+			if (j == 0)
+				result = j;
+			addr.s6_addr32[3] = htonl(1+ntohl(addr.s6_addr32[3]));
+		}
+	}
+
+	return result;
+}
+
+
+
+/* bail out on creating a whiteclnt hash table file */
+static DCC_WHITE_RESULT
+make_white_hash_bail(DCC_WHITE_RESULT result, DCC_WF *wf, DCC_WF *tmp_wf)
+{
+	if (tmp_wf->ht_nm[0] != '\0') {
+		unlink_white_ht(tmp_wf, 0);
+		tmp_wf->ht_nm[0] = '\0';
+	}
+
+	/* we have a usable file open */
+	if (wf->wtbl)
+		return DCC_WHITE_CONTINUE;
+	return result;
+}
+
+
+
+/* (re)create the hash table file */
+static DCC_WHITE_RESULT
+make_white_hash(DCC_EMSG emsg, DCC_WF *wf, DCC_WF *tmp_wf)
+{
+	static const u_char zero = 0;
+	int ascii_fd;
+	struct stat ascii_sb;
+	DCC_PATH path;
+	DCC_WHITE_RESULT result;
+	DCC_CK_TYPES type;
+	int part_result, i;
+
+	if (dcc_clnt_debug > 2)
+		dcc_trace_msg("start parsing %s", wf->ascii_nm);
+
+	/* Do not wait to create a new, locked hash table file if we have
+	 *	a usable file.  Assume some other process is re-parsing
+	 *	the ASCII file.
+	 * If we should wait to create a new file, then we don't have a
+	 *	usable hash table and so there is no reason to unlock the
+	 *	DCC_WF structure. */
+
+#ifdef DCC_DEBUG_CLNT_LOCK
+	if (tmp_wf == &cmn_tmp_wf)
+		assert_cwf_locked();
+#endif
+
+	dcc_wf_init(tmp_wf, wf->wf_flags);
+
+	if (0 > stat(wf->ascii_nm, &ascii_sb)) {
+		result = ((errno == ENOENT || errno == ENOTDIR)
+			  ? DCC_WHITE_NOFILE
+			  : DCC_WHITE_COMPLAIN);
+
+		dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s",
+			  wf->ascii_nm, ERROR_STR());
+
+		/* Delete the hash table if the ASCII file has disappeared.
+		 * stat_white_nms() delays forcing a re-build if the ASCII
+		 * file disappears only temporarily */
+		if (wf->ht_nm[0] != '\0') {
+			close_white_ht(0, wf);
+			i = unlink(wf->ht_nm);
+			if (i < 0) {
+				if (errno != ENOENT && errno != ENOTDIR)
+					dcc_trace_msg("%s missing,:unlink(%s):"
+						      " %s",
+						      wf->ascii_nm,
+						      fnm2abs_err(path,
+							  wf->ht_nm),
+						      ERROR_STR());
+			} else if (dcc_clnt_debug > 1) {
+				dcc_trace_msg("delete %s after %s missing",
+					      fnm2abs_err(path, wf->ht_nm),
+					      wf->ascii_nm);
+			}
+		}
+		return make_white_hash_bail(result, wf, tmp_wf);
+	}
+
+	part_result = create_white_ht(emsg, tmp_wf, wf);
+	if (part_result == 0) {
+		/* The new hash table file is busy.
+		 * Do not complain if we have a usable open hash table file */
+		if (!dcc_clnt_debug && wf->wtbl)
+			return DCC_WHITE_OK;
+		/* at least ignore the need to reparse */
+		return DCC_WHITE_CONTINUE;
+	}
+	if (part_result < 0)
+		return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
+
+	/* clear the new hash table */
+	if (!write_white(emsg, tmp_wf, white_magic, sizeof(white_magic), 0)
+	    || !write_white(emsg, tmp_wf, &zero, 1, EMPTY_WHITE_SIZE-1)
+	    || !map_white_ht(emsg, tmp_wf, 0))
+		return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
+	if (tmp_wf->wf_flags & DCC_WF_PER_USER)
+		tmp_wf->wtbl->hdr.flags = DCC_WHITE_FG_PER_USER;
+	tmp_wf->wtbl_flags = tmp_wf->wtbl->hdr.flags;
+	for (type = 0; type <= DCC_CK_TYPE_LAST; ++type)
+		tmp_wf->wtbl->hdr.tholds_rej[type] = DCC_THOLD_UNSET;
+
+	ascii_fd = dcc_lock_open(emsg, tmp_wf->ascii_nm, O_RDONLY,
+				 DCC_LOCK_OPEN_NOWAIT | DCC_LOCK_OPEN_SHARE,
+				 DCC_LOCK_ALL_FILE, 0);
+	if (ascii_fd == -1)
+		return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
+
+	tmp_wf->wtbl->hdr.ascii_mtime = ascii_sb.st_mtime;
+	part_result = dcc_parse_whitefile(emsg, tmp_wf, ascii_fd,
+					  add_white, add_white_cidr);
+	if (part_result > 0) {
+		/* parse again to detect colliding definitions among
+		 * host names and CIDR blocks */
+		if (0 != lseek(ascii_fd, 0, SEEK_SET)) {
+			dcc_pemsg(EX_IOERR, emsg, "lseek(%s, 0, SEEK_SET): %s",
+				  wf->ascii_nm, ERROR_STR());
+			part_result = -1;
+		} else {
+			part_result = dcc_parse_whitefile(emsg, tmp_wf,
+							ascii_fd,
+							ck_dup_white_complain,
+							ck_dup_white_cidr);
+		}
+	}
+
+	/* if the hash table is tolerable, compute a checksum of it
+	 * to detect differing per-user hash tables */
+	if (part_result >= 0
+	    && map_white_ht(emsg, tmp_wf, tmp_wf->wtbl->hdr.entries)) {
+		MD5_CTX ctx;
+
+		MD5Init(&ctx);
+		MD5Update(&ctx, (u_char *)&tmp_wf->wtbl->hdr.cidr,
+			  sizeof(tmp_wf->wtbl->hdr.cidr));
+		i = tmp_wf->wtbl->hdr.entries;
+		while (--i >= 0) {
+			MD5Update(&ctx, &tmp_wf->wtbl->tbl[i].type,
+				  sizeof(tmp_wf->wtbl->tbl[i].type));
+			MD5Update(&ctx, tmp_wf->wtbl->tbl[i].sum,
+				  sizeof(tmp_wf->wtbl->tbl[i].sum));
+		}
+		MD5Final(tmp_wf->wtbl->hdr.ck_sum, &ctx);
+	}
+#ifdef DCC_WIN32
+	/* unlock the file before closing it to keep Win95 happy */
+	dcc_unlock_fd(0, ascii_fd, DCC_LOCK_ALL_FILE,
+		      "whitelist ", tmp_wf->ascii_nm);
+#endif
+	if (close(ascii_fd) < 0)
+		dcc_trace_msg("close(%s): %s", wf->ascii_nm, ERROR_STR());
+	if (part_result < 0)
+		return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
+	result = (part_result > 0) ? DCC_WHITE_OK : DCC_WHITE_CONTINUE;
+
+	/* ensure continued complaints about errors */
+	if (result == DCC_WHITE_CONTINUE)
+		tmp_wf->wtbl->hdr.reparse = time(0) + DCC_WHITE_REPARSE_DELAY;
+	sync_white(tmp_wf);
+
+#ifdef DCC_WIN32
+	/* WIN32 prohibits renaming open files
+	 * and there is little or no concurrency on WIN32 DCC clients
+	 * So lock the original file and copy to it. */
+	close_white_ht(0, wf);
+	wf->ht_fd = dcc_lock_open(emsg, wf->ht_nm, O_RDWR | O_CREAT,
+				  0, DCC_LOCK_ALL_FILE, 0);
+	if (wf->ht_fd < 0)
+		return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
+	if (!map_white_ht(emsg, wf, tmp_wf->wtbl_entries))
+		return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
+	memcpy(wf->wtbl, tmp_wf->wtbl, tmp_wf->wtbl_size);
+	close_white_ht(emsg, tmp_wf);
+	unlink(tmp_wf->ht_nm);
+#else
+	part_result = rename(tmp_wf->ht_nm, wf->ht_nm);
+	if (0 > part_result
+	    && dcc_get_priv_home(wf->ht_nm)) {
+		part_result = rename(tmp_wf->ht_nm, wf->ht_nm);
+		dcc_rel_priv();
+	}
+	if (0 > part_result) {
+		dcc_pemsg(EX_IOERR, emsg, "rename(%s, %s): %s",
+			  tmp_wf->ht_nm, wf->ht_nm, ERROR_STR());
+		return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
+	}
+#endif
+
+	close_white_ht(0, tmp_wf);
+	if (dcc_clnt_debug > 1)
+		dcc_trace_msg("finished parsing %s", wf->ascii_nm);
+	part_result = open_white_ht(emsg, wf);
+	if (part_result < 0)
+		result = DCC_WHITE_COMPLAIN;
+	else if (part_result == 0 && result == DCC_WHITE_OK)
+		result = DCC_WHITE_CONTINUE;
+
+	wf->next_stat_time = time(0) + DCC_WHITE_STAT_DELAY;
+
+	return result;
+}
+
+
+
+/* see that a local whitelist is ready
+ *	on failure the file is not locked
+ *	The caller must lock the DCC_WF if necessary */
+DCC_WHITE_RESULT
+dcc_rdy_white(DCC_EMSG emsg, DCC_WF *wf, DCC_WF *tmp_wf)
+{
+	time_t now;
+	int i;
+
+	if (wf->need_reopen) {
+		/* The resolver thread has change the hash table in the file
+		 * and possibly renamed it.
+		 * We need to re-open the hash table */
+		wf->broken = 0;
+		wf->reparse = 0;
+		close_white_ht(0, wf);
+	}
+
+	now = time(0);
+	if (emsg)
+		*emsg = '\0';
+
+	if (wf->ht_fd >= 0) {
+		/* The hash table is open.
+		 * If we have checked recently, assume everything is good. */
+		if (!DCC_IS_TIME(now, wf->next_stat_time, DCC_WHITE_STAT_DELAY))
+			return DCC_WHITE_OK;
+		i = stat_white_nms(emsg, wf);
+		if (i > 0)
+			return DCC_WHITE_OK;
+		if (i < 0) {
+			/* Things are broken or not open, so try to open the
+			 * hash table.
+			 * We may be racing here.  Be happy if another process
+			 * fixes the hash table while we stall trying to open
+			 * it locked. */
+			i = open_white_ht(emsg, wf);
+			if (i < 0)
+				return DCC_WHITE_COMPLAIN;
+			if (i > 0)
+				i = stat_white_nms(emsg, wf);
+		}
+
+	} else {
+		if (wf->ascii_nm[0] == '\0') {
+			dcc_pemsg(EX_NOINPUT, emsg, "no whitelist");
+			return DCC_WHITE_NOFILE;
+		}
+
+		if (STILL_BROKE(wf, now)) {
+			/* only check for a missing file occassionally */
+			if (wf->wf_flags & DCC_WF_NOFILE) {
+				dcc_pemsg(EX_NOINPUT, emsg, "%s does not exist",
+					  wf->ascii_nm);
+				return DCC_WHITE_NOFILE;
+			}
+
+			/* If things are broken, and it has not been a while,
+			 * then assume things are still broken. */
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "%s still broken", wf->ascii_nm);
+			return (dcc_clnt_debug > 2
+				? DCC_WHITE_COMPLAIN
+				: DCC_WHITE_SILENT);
+		}
+
+		i = open_white_ht(emsg, wf);
+		if (i < 0)
+			return DCC_WHITE_COMPLAIN;
+		if (i > 0)
+			i = stat_white_nms(emsg, wf);
+	}
+
+	if (i > 0)
+		return DCC_WHITE_OK;
+
+	/* Try to let the resolver thread wait for the DNS chitchat
+	 * for host names that might now be in the ASCII file.
+	 * To avoid racing with the resolver thread to delete the main
+	 * hash files, fail if there is no hash table and this is not
+	 * the resolver thread. */
+	if (i == 0
+	    && !(wf->wf_flags & DCC_WF_PER_USER)
+	    && dcc_clnt_wake_resolve()) {
+		if (wf->wtbl)
+			return DCC_WHITE_OK;
+		return (dcc_clnt_debug > 2
+			? DCC_WHITE_COMPLAIN : DCC_WHITE_SILENT);
+	}
+
+	if (STILL_BROKE(wf, now)) {
+		dcc_pemsg(EX_DATAERR, emsg, "%s still broken", wf->ascii_nm);
+		if (i == 0 && wf->wtbl)
+			return (dcc_clnt_debug > 2
+				? DCC_WHITE_CONTINUE : DCC_WHITE_SILENT);
+		return (dcc_clnt_debug > 2
+			? DCC_WHITE_COMPLAIN : DCC_WHITE_SILENT);
+	}
+
+	if (emsg && *emsg != '\0') {
+		if (i < 0) {
+			dcc_error_msg("%s", emsg);
+		} else if (dcc_clnt_debug > 2) {
+			dcc_trace_msg("%s", emsg);
+		}
+		*emsg = '\0';
+	}
+
+	switch (make_white_hash(emsg, wf, tmp_wf)) {
+	case DCC_WHITE_OK:
+		wf->wf_flags &= ~DCC_WF_NOFILE;
+		return DCC_WHITE_OK;	/* all is good */
+	case DCC_WHITE_CONTINUE:	/* minor syntax error or bad hostname */
+		wf->wf_flags &= ~DCC_WF_NOFILE;
+		return DCC_WHITE_CONTINUE;
+	case DCC_WHITE_SILENT:		/* no new message */
+		wf->wf_flags &= ~DCC_WF_NOFILE;
+		return DCC_WHITE_CONTINUE;
+	case DCC_WHITE_NOFILE:
+		wf->wf_flags |= DCC_WF_NOFILE;
+		return DCC_WHITE_NOFILE;
+	case DCC_WHITE_COMPLAIN:
+	default:
+		is_broken(wf);
+		wf->wf_flags &= ~DCC_WF_NOFILE;
+		return DCC_WHITE_COMPLAIN;
+	}
+}
+
+
+
+static u_char
+lookup_white(DCC_EMSG emsg,
+	     DCC_TGTS *tgtsp,		/* value if hit else DCC_TGTS_INVALID */
+	     DCC_WF *wf,
+	     const DCC_GOT_CKS *cks, const DCC_GOT_SUM *g)
+{
+	const DCC_WHITE_ENTRY *e;
+	const DCC_WHITE_CIDR_ENTRY *cidrp;
+	int bits;
+
+	e = find_white(emsg, wf, g->type, g->sum, 0);
+	if (e == FIND_WHITE_BROKEN) {
+		*tgtsp = DCC_TGTS_OK;
+		return 0;
+	}
+
+	if (!e) {
+		if (g->type != DCC_CK_IP) {
+			*tgtsp = DCC_TGTS_INVALID;
+			return 1;
+		}
+
+		/* if we had no hit and it is an IP address,
+		 * check the CIDR blocks */
+		bits = 0;
+		cidrp = &wf->wtbl->hdr.cidr.e[wf->wtbl->hdr.cidr.len];
+		while (cidrp != wf->wtbl->hdr.cidr.e) {
+			--cidrp;
+			/* look for the longest match */
+			if (cidrp->bits <= bits)
+				continue;
+			if (DCC_IN_BLOCK(cks->ip_addr,
+					 cidrp->addr, cidrp->mask)) {
+				*tgtsp = cidrp->tgts;
+				bits = cidrp->bits;
+			}
+		}
+		if (bits == 0)
+			*tgtsp = DCC_TGTS_INVALID;
+		return 1;
+	}
+
+	*tgtsp = e->tgts;
+	return 1;
+}
+
+
+
+/* check a local whitelist for a single checksum
+ *	on exit the file is locked except after an error */
+DCC_WHITE_RESULT
+dcc_white_sum(DCC_EMSG emsg,
+	      DCC_WF *wf,		/* in this whitelist */
+	      DCC_CK_TYPES type, const DCC_SUM sum, /* look for this checksum */
+	      DCC_TGTS *tgtsp,		/* set only if we find the checksum */
+	      DCC_WHITE_LISTING *listingp)
+{
+	DCC_WHITE_ENTRY *e;
+	DCC_WHITE_RESULT result;
+
+	result = dcc_rdy_white(emsg, wf, &cmn_tmp_wf);
+	switch (result) {
+	case DCC_WHITE_OK:
+	case DCC_WHITE_CONTINUE:
+		break;
+	case DCC_WHITE_SILENT:
+	case DCC_WHITE_NOFILE:
+	case DCC_WHITE_COMPLAIN:
+		*listingp = DCC_WHITE_RESULT_FAILURE;
+		return result;
+	}
+
+	e = find_white(emsg, wf, type, sum, 0);
+	if (e == FIND_WHITE_BROKEN) {
+		*listingp = DCC_WHITE_RESULT_FAILURE;
+		return DCC_WHITE_COMPLAIN;
+	}
+
+	if (!e) {
+		*listingp = DCC_WHITE_UNLISTED;
+	} else if (e->tgts == DCC_TGTS_OK2
+		   && type == DCC_CK_ENV_TO) {
+		/* deprecated mechanism for turn DCC checks on and off for
+		 * individual targets */
+		*tgtsp = e->tgts;
+		*listingp = DCC_WHITE_USE_DCC;
+	} else if (e->tgts == DCC_TGTS_OK) {
+		*tgtsp = e->tgts;
+		*listingp = DCC_WHITE_LISTED;
+	} else if (e->tgts == DCC_TGTS_TOO_MANY) {
+		*tgtsp = e->tgts;
+		*listingp = DCC_WHITE_BLACK;
+	} else {
+		*listingp = DCC_WHITE_UNLISTED;
+	}
+
+	return result;
+}
+
+
+
+/* see if an IP addess is that of one of our MX servers
+ *	the caller must lock cmn_wf if necessray */
+u_char					/* 0=problems */
+dcc_white_mx(DCC_EMSG emsg,
+	     DCC_TGTS *tgtsp,		/* !=0 if listed */
+	     const DCC_GOT_CKS *cks)	/* this IP address checksum */
+{
+	u_char result;
+
+	result = 1;
+	switch (dcc_rdy_white(emsg, &cmn_wf, &cmn_tmp_wf)) {
+	case DCC_WHITE_OK:
+		break;
+	case DCC_WHITE_CONTINUE:
+		result = 0;
+		break;
+	case DCC_WHITE_NOFILE:
+		*tgtsp = 0;
+		return 1;
+	case DCC_WHITE_SILENT:
+	case DCC_WHITE_COMPLAIN:
+		*tgtsp = 0;
+		return 0;
+	}
+
+	if (cks->sums[DCC_CK_IP].type != DCC_CK_IP) {
+		*tgtsp = 0;
+		return result;
+	}
+
+	if (!lookup_white(emsg, tgtsp, &cmn_wf, cks, &cks->sums[DCC_CK_IP]))
+		return 0;
+
+	return result;
+}
+
+
+
+/* See what a local whitelist file says about the checksums for a message.
+ *	The message is whitelisted if at least one checksum is in the local
+ *	whitelist or if there are two or more OK2 values.
+ *	Otherwise it is blacklisted if at least one checksum is.
+ *	The caller must lock the DCC_WF if necessary. */
+DCC_WHITE_RESULT			/* whether the lookup worked */
+dcc_white_cks(DCC_EMSG emsg, DCC_WF *wf,
+	      DCC_GOT_CKS *cks,		/* these checksums */
+	      DCC_CKS_WTGTS wtgts,	/* whitelist targets, each cks->sums */
+	      DCC_WHITE_LISTING *listingp)  /* the answer found */
+{
+	const DCC_GOT_SUM *g;
+	int inx;
+	DCC_TGTS tgts, prev_tgts;
+	DCC_WHITE_RESULT result;
+
+	result = dcc_rdy_white(emsg, wf, &cmn_tmp_wf);
+	switch (result) {
+	case DCC_WHITE_OK:
+	case DCC_WHITE_CONTINUE:
+		break;
+	case DCC_WHITE_NOFILE:
+	case DCC_WHITE_COMPLAIN:
+	case DCC_WHITE_SILENT:
+		*listingp = DCC_WHITE_RESULT_FAILURE;
+		return result;
+	}
+
+	/* look for each checksum in the hash file */
+	*listingp = DCC_WHITE_UNLISTED;
+	prev_tgts = DCC_TGTS_INVALID;
+	for (g = &cks->sums[inx = DCC_CK_TYPE_FIRST];
+	     g <= LAST(cks->sums);
+	     ++g, ++inx) {
+		/* ignore checksums we don't have */
+		if (g->type == DCC_CK_INVALID)
+			continue;
+
+		if (!lookup_white(emsg, &tgts, wf, cks, g)) {
+			*listingp = DCC_WHITE_RESULT_FAILURE;
+			return DCC_WHITE_COMPLAIN;
+		}
+		if (tgts == DCC_TGTS_INVALID) {
+			/* report any body checksums as spam for a spam trap */
+			if ((wf->wtbl_flags & DCC_WHITE_FG_TRAPS)
+			    && DCC_CK_IS_BODY(g->type))
+				tgts = DCC_TGTS_TOO_MANY;
+			else
+				continue;
+		}
+
+		if (wtgts)
+			wtgts[inx] = tgts;
+
+		if (tgts == DCC_TGTS_OK) {
+			/* found the checksum in our whitelist,
+			 * so we have the answer */
+			*listingp = DCC_WHITE_LISTED;
+
+		} else if (tgts == DCC_TGTS_OK2) {
+			if (prev_tgts == DCC_TGTS_OK2) {
+				/* two half-white checksums count the same
+				 * as a single pure white checksum
+				 * and gives the answer */
+				*listingp = DCC_WHITE_LISTED;
+				continue;
+			}
+			prev_tgts = DCC_TGTS_OK2;
+
+		} else if (tgts == DCC_TGTS_TOO_MANY) {
+			if (*listingp == DCC_WHITE_UNLISTED)
+				*listingp = DCC_WHITE_BLACK;
+		}
+	}
+
+	return result;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/clnt_init.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/clnt_init.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,250 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * connect to a DCC server for an administrative program
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.49 $Revision$
+ */
+
+#include "dcc_clnt.h"
+#include "dcc_heap_debug.h"
+
+
+static DCC_CLNT_CTXT *ctxt_free;
+static int ctxts_active;
+
+
+/* this can give the wrong answer if another thread sneaks in and
+ * switches servers */
+const char *
+dcc_srvr_nm(u_char grey)
+{
+	const DCC_SRVR_CLASS *class;
+	SRVR_INX srvr_inx;
+	const DCC_SRVR_ADDR *cur_addr;
+	NAM_INX nam_inx;
+
+	if (!dcc_clnt_info)
+		return "???";
+	class = grey ? &dcc_clnt_info->dcc : &dcc_clnt_info->grey;
+	srvr_inx = class->srvr_inx;
+	if (GOOD_SRVR(class, srvr_inx)) {
+		cur_addr = &class->addrs[srvr_inx];
+		nam_inx = cur_addr->nam_inx;
+		if (GOOD_NAM(nam_inx))
+			return class->nms[nam_inx].hostname;
+	}
+	return grey ? "greylist server" : "DCC server";
+}
+
+
+
+/* Create a DCC client context
+ *	The contexts must be locked */
+DCC_CLNT_CTXT *				/* 0=failed */
+dcc_alloc_ctxt(void)
+{
+	DCC_CLNT_CTXT *ctxt;
+
+	assert_ctxts_locked();
+
+	ctxt = ctxt_free;
+	if (ctxt) {
+		ctxt_free = ctxt->fwd;
+	} else {
+		ctxt = dcc_malloc(sizeof(*ctxt));
+	}
+	memset(ctxt, 0, sizeof(*ctxt));
+	ctxt->soc = INVALID_SOCKET;
+	++ctxts_active;
+
+	return ctxt;
+}
+
+
+
+/* the contexts must be locked */
+void
+dcc_rel_ctxt(DCC_CLNT_CTXT *ctxt)
+{
+	assert_ctxts_locked();
+
+	dcc_clnt_soc_close(ctxt);
+
+	ctxt->fwd = ctxt_free;
+	ctxt_free = ctxt;
+
+	if (--ctxts_active < 0)
+		abort();
+}
+
+
+
+/* create a temporary an anonymous map file
+ *	The contexts must be locked.
+ *	On failure, everything is undone.
+ *	On success, the file is initialized and mapped. */
+u_char					/* 0=failed; 1=mapped & locked */
+dcc_map_tmp_info(DCC_EMSG emsg,		/* cleared of stale messages */
+		 const DCC_SRVR_NM *nm,
+		 const DCC_IP *src,
+		 u_char info_flags)	/* DCC_INFO_FG_* */
+{
+	DCC_PATH tmp_info_nm;
+	int fd;
+
+	assert_ctxts_locked();
+
+	fd = dcc_mkstemp(emsg, tmp_info_nm, sizeof(tmp_info_nm), 0, 0, 0,
+			 0, "map", 1, 0);
+	if (fd < 0)
+		return 0;
+
+	if (!dcc_create_map(emsg, tmp_info_nm, &fd, nm, 1, nm, 1,
+			    src, info_flags | DCC_INFO_FG_TMP))
+		return 0;
+
+	return dcc_map_info(emsg, tmp_info_nm, fd);
+}
+
+
+
+/* start to get ready for an operation
+ *	on entry, nothing is locked,
+ *	on success, the contexts and info are locked
+ *	on failure, nothing is locked */
+DCC_CLNT_CTXT *				/* 0=failed */
+dcc_clnt_start(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt,
+	       const char *new_info_map_nm,
+	       DCC_CLNT_FGS clnt_fgs)
+{
+	if (emsg)
+		*emsg = '\0';
+	dcc_ctxts_lock();
+
+	if (!ctxt)
+		ctxt = dcc_alloc_ctxt();
+
+	if (!dcc_map_info(emsg, new_info_map_nm, -1)) {
+		dcc_rel_ctxt(ctxt);
+		dcc_ctxts_unlock();
+		return 0;
+	}
+
+	if (emsg)
+		*emsg = '\0';
+	if (!dcc_clnt_rdy(emsg, ctxt, clnt_fgs)) {
+		if (!(clnt_fgs & DCC_CLNT_FG_BAD_SRVR_OK)) {
+			dcc_unmap_close_info(0);
+			dcc_rel_ctxt(ctxt);
+			dcc_ctxts_unlock();
+			return 0;
+		}
+		if (emsg && dcc_clnt_debug)
+			dcc_trace_msg("%s", emsg);
+
+		/* lock the shared information since we are not failing */
+		if (!dcc_info_lock(emsg)) {
+			dcc_rel_ctxt(ctxt);
+			dcc_ctxts_unlock();
+			return 0;
+		}
+	}
+
+	return ctxt;
+}
+
+
+
+DCC_CLNT_CTXT *				/* 0=failed */
+dcc_clnt_start_fin(DCC_EMSG emsg,	/* cleared of stale messages */
+		   DCC_CLNT_CTXT *ctxt)
+{
+	if (!dcc_info_unlock(emsg)) {
+		dcc_unmap_close_info(0);
+		dcc_rel_ctxt(ctxt);
+		dcc_ctxts_unlock();
+		return 0;
+	}
+	dcc_ctxts_unlock();
+	return ctxt;
+}
+
+
+
+/* on success the info file is mapped, but nothing is locked
+ * on failure, nothing is mapped or locked */
+DCC_CLNT_CTXT *				/* 0=failed */
+dcc_clnt_init(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt,
+	      const char *new_info_map_nm,
+	      DCC_CLNT_FGS clnt_fgs)
+{
+	ctxt = dcc_clnt_start(emsg, ctxt, new_info_map_nm, clnt_fgs);
+	if (ctxt)
+		ctxt = dcc_clnt_start_fin(emsg, ctxt);
+	return ctxt;
+}
+
+
+
+/* start talking to a DCC server using an temporary parameter file */
+DCC_CLNT_CTXT *				/* 0=failed, 1=ready */
+dcc_tmp_clnt_init(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt,
+		  const DCC_SRVR_NM *new_srvr,
+		  const DCC_IP *src,
+		  DCC_CLNT_FGS clnt_fgs,
+		  u_char info_flags)	/* DCC_INFO_FG_* */
+{
+	if (emsg)
+		*emsg = '\0';
+
+	dcc_ctxts_lock();
+	if (!dcc_map_tmp_info(emsg, new_srvr, src, info_flags)) {
+		if (ctxt)
+			dcc_rel_ctxt(ctxt);
+		dcc_ctxts_unlock();
+		return 0;
+	}
+	dcc_ctxts_unlock();
+
+	ctxt = dcc_clnt_init(emsg, ctxt, 0, clnt_fgs | DCC_CLNT_FG_NO_FAIL);
+	if (!ctxt) {
+		dcc_ctxts_lock();
+		dcc_unmap_close_info(0);
+		dcc_ctxts_unlock();
+	}
+	return ctxt;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/clnt_send.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/clnt_send.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,3486 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * send a request from client to server
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.198 $Revision$
+ */
+
+#include "dcc_clnt.h"
+#ifdef USE_POLL
+#include <poll.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+
+DCC_CLNT_INFO *dcc_clnt_info;		/* memory mapped shared data */
+u_char dcc_all_srvrs = 0;		/* try to contact all servers */
+
+/* #define CLNT_LOSSES */
+#ifdef CLNT_LOSSES
+static u_int clnt_losses;
+#endif
+
+u_char dcc_clnt_debug;
+int dcc_debug_ttl;
+
+
+#define AGE_AVG(_v,_n,_a,_b) ((_v) = ((_v)*_a + (_n)*_b + (_a+_b)/2)/(_a+_b))
+
+/* send NOPs to measure the RTTs to servers when the current server
+ * becomes slow this often */
+#define FAST_RTT_SECS    (15*60)
+
+
+char dcc_clnt_hostname[MAXHOSTNAMELEN];	/* local hostname */
+static u_int32_t dcc_clnt_hid;		/* our DCC host-ID */
+
+
+/* Each client knows about one or more servers, lest the current server
+ * crash.  To ensure that counts of spam accumulate as quickly as possible,
+ * all of the processes on a client try to use a single server.  The
+ * closest (or fastest) server is preferred.  It is desirable for the
+ * servers to convert the hostnames of the servers to IP addresses
+ * frequently enough to track changes in address records, but not so
+ * often that a lot of time is wasted on the DNS.
+ *
+ * All of that implies that independent processes on the client need to
+ * cooperate in measuring the round trip time to the servers and maintaining
+ * their IP addresses.  On UNIX systems, this is accomplished with mmap()
+ * and a well known file.
+ *
+ * The DCC client uses 3 locks:
+ * 1. mutex to ensure that only one thread in process sends bursts of NOPs
+ *	to measure RTTs or resolves DNS names
+ * 2. mutex protecting the shared information in the map file for threads
+ *	within a process
+ * 3. fcntl() lock on the file to protect the shared information among processes
+ *
+ * To avoid ABBA deadlocks, the locks are always sought in that order.
+ * For most operaitons, only #2/#3 is needed.  Sometimes only #2.
+ *
+ * Some systems have broken fcntl() locking (e.g. NFS in Solaris).
+ * They lock the entire file. */
+
+
+/* the contexts must be locked to read or change these values */
+static int info_fd = -1;
+#ifdef DCC_WIN32
+HANDLE info_map = INVALID_HANDLE_VALUE;
+#endif
+DCC_PATH dcc_info_nm;
+
+/* info_locked is set when the file system lock on changing the mapped file is
+ *	held.  The contexts must be locked while info_locked is set, as well as
+ *	when it is checked or changed. */
+static u_char info_locked;
+
+
+
+
+/* Get the RTT used to pick servers.
+ *	It includes the local bias and the penalty for version mismatches */
+static int
+effective_rtt(const DCC_SRVR_CLASS *class, const DCC_SRVR_ADDR *ap)
+{
+	NAM_INX nam_inx;
+	int rtt;
+
+	rtt = ap->rtt;
+	if (rtt >= DCC_MAX_RTT)
+		return DCC_RTT_BAD;
+
+	nam_inx = ap->nam_inx;
+	if (!GOOD_NAM(nam_inx))
+		return DCC_RTT_BAD;
+
+	rtt += class->nms[nam_inx].rtt_adj;
+	/* penalize servers with strange versions */
+	if (ap->srvr_pkt_vers < DCC_PKT_VERSION
+#ifdef DCC_PKT_VERSION7
+	    /* protocol version 7 and 8 are the same for the free code */
+	    && ap->srvr_pkt_vers+1 != DCC_PKT_VERSION
+#endif
+	    && ap->srvr_id != DCC_ID_INVALID)
+		rtt += DCC_RTT_VERS_ADJ;
+
+	if (rtt >= DCC_RTT_BAD)
+		return DCC_RTT_BAD;
+	return rtt;
+}
+
+
+
+static inline u_char
+good_rtt(const DCC_SRVR_CLASS *class)
+{
+	int rtt;
+
+	if (!HAVE_SRVR(class))
+		return 0;
+
+	rtt = effective_rtt(class, &class->addrs[class->srvr_inx]);
+	if (rtt > class->avg_thold_rtt)
+		return 0;
+
+	return 1;
+}
+
+
+#define AP2CLASS(ap) DCC_GREY2CLASS(ap >= dcc_clnt_info->grey.addrs)
+
+
+/* compare addresses while trying to ignore IPv4 vs. IPv6 details */
+static u_char				/* 0=the addresses are equal */
+dcc_cmp_ap2su(const DCC_SRVR_ADDR *ap, const DCC_SOCKU *su)
+{
+	DCC_SRVR_ADDR ap4;
+	struct in_addr su_addr4;
+
+	if (ap->ip.port != DCC_SU_PORT(su))
+		return 1;
+
+	if (ap->ip.family == AF_INET6
+	    && dcc_ipv6toipv4(&ap4.ip.u.v4, &ap->ip.u.v6)) {
+		ap4.ip.family = AF_INET;
+		ap = &ap4;
+	}
+
+	if (su->sa.sa_family == AF_INET) {
+		return (ap->ip.family != AF_INET
+			|| ap->ip.u.v4.s_addr != su->ipv4.sin_addr.s_addr);
+	}
+
+	if (dcc_ipv6toipv4(&su_addr4, &su->ipv6.sin6_addr)) {
+		return (ap->ip.family != AF_INET
+			|| ap->ip.u.v4.s_addr != su_addr4.s_addr);
+	}
+
+	if (ap->ip.family != AF_INET6)
+		return 1;
+
+	/* both are real IPv6 and not ::1 */
+	return memcmp(&ap->ip.u.v6, &su->ipv6.sin6_addr, sizeof(ap->ip.u.v6));
+}
+
+
+
+int
+dcc_ap2str_opt(char *buf, int buf_len,
+	       const DCC_SRVR_CLASS *class, u_char inx,
+	       char port_str)		/* '\0' or '-' */
+{
+	const DCC_SRVR_ADDR *ap;
+	char *buf1;
+	int len;
+
+	ap = &class->addrs[inx];
+	dcc_ip2str(buf, buf_len, &ap->ip);
+
+	len = strlen(buf);
+	buf1 = buf+len;
+	buf_len -= len;
+	if (ap->ip.port == DCC_CLASS2PORT(class)) {
+		if (port_str) {
+			if (buf_len >= 1) {
+				*buf1 = ',';
+				if (buf_len >= 2)
+					*++buf1 = port_str;
+				*++buf1 = '\0';
+			}
+		}
+	} else if (buf_len > 0) {
+		len = snprintf(buf1, buf_len, ",%d", ntohs(ap->ip.port));
+		if (len >= buf_len)
+			len = buf_len-1;
+		buf1 += len;
+	}
+
+	return buf1-buf;
+}
+
+
+
+static const char *
+addr2str(char *buf, u_int buf_len, const DCC_SRVR_CLASS *class,
+	 int addrs_gen, const DCC_SRVR_ADDR *ap, const DCC_SOCKU *sup)
+{
+	DCC_SOCKU su;
+	char str[DCC_SU2STR_SIZE];
+	const char *host;
+
+	if (class->gen == addrs_gen) {
+		if (!sup) {
+			dcc_mk_su(&su, ap->ip.family, &ap->ip.u, ap->ip.port);
+			sup = &su;
+		}
+		dcc_su2str(str, sizeof(str), sup);
+		if (GOOD_NAM(ap->nam_inx)
+		    && (host = class->nms[ap->nam_inx].hostname,
+			!strncmp(host, str, strlen(host)))) {
+			snprintf(buf, buf_len, "%s (%s)", host, str);
+		} else {
+			snprintf(buf, buf_len, "%s", str);
+		}
+
+	} else if (sup) {
+		dcc_su2str(buf, buf_len, sup);
+
+	} else {
+		snprintf(buf, buf_len, DCC_IS_GREY_STR(class));
+	}
+	return buf;
+}
+
+
+
+/* display or log the current error message if debugging */
+static inline void
+flush_emsg(DCC_EMSG emsg, u_char debug)
+{
+	if (!emsg)
+		return;
+	if (!debug)
+		return;
+	if (*emsg != '\0') {
+		dcc_trace_msg("%s", emsg);
+		*emsg = '\0';
+	}
+}
+
+
+
+/* display or prepare a new, less interesting error message if we do not have
+ *	a buffer or if there is no old, presumably more important message. */
+static void PATTRIB(3,4)
+extra_pemsg(int ex_code, DCC_EMSG emsg, const char *msg, ...)
+{
+	va_list args;
+
+	flush_emsg(emsg, dcc_clnt_debug);
+	if (!emsg || *emsg == '\0') {
+		va_start(args, msg);
+		dcc_vpemsg(ex_code, emsg, msg, args);
+		va_end(args);
+	}
+}
+
+
+
+static void
+trace_perf(const char *msg, const DCC_SRVR_ADDR *ap)
+{
+	DCC_SRVR_CLASS *class;
+	char abuf[60];
+	char rbuf[30];
+
+	class = AP2CLASS(ap);
+	if (!GOOD_NAM(ap->nam_inx)
+	    || class->nms[ap->nam_inx].rtt_adj == 0) {
+		rbuf[0] = 0;
+	} else if (ap->srvr_pkt_vers < DCC_PKT_VERSION
+		   && ap->srvr_id != DCC_ID_INVALID) {
+		snprintf(rbuf, sizeof(rbuf), "%+d+%d",
+			 class->nms[ap->nam_inx].rtt_adj/1000,
+			 DCC_RTT_VERS_ADJ/1000);
+	} else {
+		snprintf(rbuf, sizeof(rbuf), "%+d",
+			 class->nms[ap->nam_inx].rtt_adj/1000);
+	}
+
+	if (ap->rtt == DCC_RTT_BAD) {
+		dcc_trace_msg("%s %s server %s with unknown RTT",
+			      msg, DCC_IS_GREY_STR(class),
+			      addr2str(abuf, sizeof(abuf), class,
+				       class->gen, ap, 0));
+	} else if (ap->total_xmits == 0) {
+		dcc_trace_msg("%s %s server %s with %.2f%s ms RTT,"
+			      " %d ms queue wait",
+			      msg, DCC_IS_GREY_STR(class),
+			      addr2str(abuf, sizeof(abuf), class,
+				       class->gen, ap, 0),
+			      ap->rtt/1000.0, rbuf,
+			      ap->srvr_wait/1000);
+	} else {
+		dcc_trace_msg("%s %s server %s with %.0f%%"
+			      " of %d requests answered,"
+			      " %.2f%s ms RTT, %d ms queue wait",
+			      msg, DCC_IS_GREY_STR(class),
+			      addr2str(abuf, sizeof(abuf), class,
+				       class->gen, ap, 0),
+			      (ap->total_resps*100.0)/ap->total_xmits,
+			      ap->total_xmits,
+			      ap->rtt/1000.0, rbuf,
+			      ap->srvr_wait/1000);
+	}
+}
+
+
+
+/* If the socket isn't always connected, it can receive
+ * datagrams from almost everywhere (for example, a DNS
+ * datagram could leak-in if the local port range is small
+ * and the local port has been recently doing DNS queries
+ * in its previous life).
+ *
+ * If the socket is connected, it can still receive
+ * datagrams not belonging to the connection per se. This
+ * will happen if it has been disconnected recently and there
+ * was pending data in the socket's queue.
+ *
+ * Before complaining, check that this datagram seems to be a response
+ * to something we sent */
+static void PATTRIB(5,6)
+trace_bad_packet(const DCC_XLOG *xlog, const DCC_SOCKU *su,
+		 const DCC_OP_RESP *resp, int resp_len, const char *p, ...)
+{
+	const DCC_XLOG_ENTRY *xloge;
+	va_list args;
+	char msgbuf[80];
+	char pktbuf[9*10];
+	int i, j, l;
+
+	if (!dcc_clnt_debug && xlog && su) {
+		for (xloge = xlog->base; ; ++xloge) {
+			/* forget the error message if not from a DCC server */
+			if (xloge >= xlog->next)
+				return;
+
+			/* Don't check this server entry if we haven't
+			 * transmitted anything to this host. */
+			if (xloge->op_nums.t == DCC_OP_NUMS_NULL)
+				continue;
+
+			/* is the packet from this server? */
+			if (!memcmp(su, &xloge->su, sizeof(*su)))
+				break;
+		}
+	}
+
+	va_start(args, p);
+	vsnprintf(msgbuf, sizeof(msgbuf), p, args);
+	va_end(args);
+	for (i = 0, j = 0; (i+1)*ISZ(resp->w[0]) <= resp_len; ++i) {
+		l = snprintf(&pktbuf[j], sizeof(pktbuf)-j, " %08x", resp->w[i]);
+		if (l < 9)
+			break;
+		j += l;
+	}
+	dcc_error_msg("%s;%s", msgbuf, pktbuf);
+}
+
+
+
+/* Compute the delay before the next retransmission
+ *      It always should be long enough for the DCC server to do some disk
+ *	operations even if the server and network have usually been faster. */
+static int
+retrans_time(int rtt, u_int xmit_num)
+{
+	u_int backoff;
+
+	if (rtt < DCC_MIN_RTT)
+		rtt = DCC_MIN_RTT;
+	backoff = rtt << xmit_num;	/* exponential backoff */
+	backoff += DCC_DCCD_DELAY;	/* varying server & network load */
+	if (backoff > DCC_MAX_RTT)
+		backoff = DCC_MAX_RTT;
+	return backoff;
+}
+
+
+
+static void
+get_start_time(DCC_CLNT_CTXT *ctxt)
+{
+	gettimeofday(&ctxt->start, 0);
+	ctxt->now = ctxt->start;
+	ctxt->now_us = 0;
+}
+
+
+
+static u_char				/* 1=ok, 0=time jumped */
+get_now(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt)
+{
+	gettimeofday(&ctxt->now, 0);
+	ctxt->now_us = tv_diff2us(&ctxt->now, &ctxt->start);
+	if (ctxt->now_us >= 0 && ctxt->now_us < FOREVER_US)
+		return 1;
+
+	/* ignore tiny reverse time jumps on some systems such as BSD/OS 4.1 */
+	if (ctxt->now_us < 0
+	    && ctxt->now_us > -1000) {
+		ctxt->now = ctxt->start;
+		ctxt->now_us = 0;
+		return 1;
+	}
+
+	dcc_pemsg(EX_OSERR, emsg,
+		  "clock changed an impossible %.6f seconds",
+		  (ctxt->now.tv_sec - ctxt->start.tv_sec) * 1.0
+		  + ((ctxt->now.tv_usec - ctxt->start.tv_usec)*1.0)/FOREVER_US);
+	ctxt->now = ctxt->start;
+	ctxt->now_us = 0;
+	return 0;
+}
+
+
+
+static double
+get_age(const DCC_CLNT_CTXT *ctxt)
+{
+	struct timeval now;
+
+	gettimeofday(&now, 0);
+	return tv_diff2us(&now, &ctxt->start)/(DCC_US*1.0);
+}
+
+
+
+#ifdef DCC_DEBUG_CLNT_LOCK
+void
+assert_info_locked(void)
+{
+	assert_ctxts_locked();
+	if (!info_locked)
+		dcc_logbad(EX_SOFTWARE, "don't have info locked");
+}
+
+
+
+void
+assert_info_unlocked(void)
+{
+	if (info_locked)
+		dcc_logbad(EX_SOFTWARE, "have info locked");
+}
+#endif
+
+
+/* Unlock the shared memory for other processes.
+ *      The contexts must be locked */
+u_char					/* 0=failed 1=ok */
+dcc_info_unlock(DCC_EMSG emsg)
+{
+	assert_ctxts_locked();
+#ifdef DCC_DEBUG_CLNT_LOCK
+	assert_info_locked();
+#else
+	if (!info_locked)
+		return 1;
+#endif
+
+	info_locked = 0;
+	return dcc_unlock_fd(emsg, info_fd, DCC_LOCK_ALL_FILE,
+			     "info ", dcc_info_nm);
+}
+
+
+
+/* Lock the shared memory so we can read and perhaps change it
+ *      The contexts must be locked */
+u_char					/* 0=failed, 1=ok */
+dcc_info_lock(DCC_EMSG emsg)
+{
+	assert_ctxts_locked();
+
+	if (info_locked) {
+#ifdef DCC_DEBUG_CLNT_LOCK
+		dcc_logbad(EX_SOFTWARE, "info already locked");
+#endif
+		return 1;
+	}
+
+	if (!dcc_exlock_fd(emsg, info_fd, DCC_LOCK_ALL_FILE, 60,
+			   "info ", dcc_info_nm))
+		return 0;
+
+	info_locked = 1;
+	return 1;
+}
+
+
+
+static u_char
+unmap_info(DCC_EMSG emsg)
+{
+#ifdef DCC_WIN32
+	win32_unmap(&info_map, dcc_clnt_info, dcc_info_nm);
+#else
+	if (0 > munmap((void *)dcc_clnt_info, sizeof(*dcc_clnt_info))) {
+		dcc_pemsg(EX_OSERR, emsg, "munmap(%s): %s",
+			  dcc_info_nm, ERROR_STR());
+		dcc_clnt_info = 0;
+		return 0;
+	}
+#endif
+	dcc_clnt_info = 0;
+	return 1;
+}
+
+
+
+/* Unmap and close the shared info
+ *      The contexts must be locked but not the info */
+u_char					/* 0=something wrong, 1=all over */
+dcc_unmap_close_info(DCC_EMSG emsg)	/* cleared of stale messages */
+{
+	u_char result = 1;
+
+	assert_ctxts_locked();
+	assert_info_unlocked();
+
+	if (!dcc_clnt_info)
+		return result;
+
+	if (!unmap_info(emsg))
+		result = 0;
+
+	if (0 > close(info_fd)) {
+		extra_pemsg(EX_IOERR, emsg, "close(%s): %s",
+			    dcc_info_nm, ERROR_STR());
+		result = 0;
+	}
+	info_fd = -1;
+
+	return result;
+}
+
+
+
+/* discover our host ID if we do not already know it */
+static u_char
+get_clnt_hid(DCC_EMSG emsg)
+{
+	struct timeval now;
+	int i;
+	u_char result;
+
+	if (dcc_clnt_hid != 0)
+		return 1;
+
+#ifdef HAVE_GETHOSTID
+	dcc_clnt_hid = gethostid();
+#endif
+	/* add the host name even if we have a hostid in case the hostid
+	 * is a commonl used RFC 1918 IP address */
+	if (0 > gethostname(dcc_clnt_hostname, sizeof(dcc_clnt_hostname)-1)) {
+		dcc_pemsg(EX_NOHOST, emsg, "gethostname(): %s", ERROR_STR());
+		/* do the best we can without a hostname */
+		gettimeofday(&now, 0);
+		dcc_clnt_hid = now.tv_sec + now.tv_usec;
+		result = 0;
+	} else if (dcc_clnt_hostname[0] == '\0') {
+		dcc_pemsg(EX_NOHOST, emsg, "null hostname from gethostname()");
+		/* do the best we can without a hostname */
+		gettimeofday(&now, 0);
+		dcc_clnt_hid = now.tv_sec + now.tv_usec;
+		result = 0;
+	} else {
+		for (i = 0; i < ISZ(dcc_clnt_hostname); ++i) {
+			if (!dcc_clnt_hostname[i])
+				break;
+			dcc_clnt_hid += dcc_clnt_hostname[i]*i;
+		}
+		result = 1;
+	}
+
+	/* this should almost never happen, but it could */
+	if (dcc_clnt_hid == 0)
+		dcc_clnt_hid = 1;
+	return result;
+}
+
+
+
+/* write a new DCC map file */
+u_char
+dcc_create_map(DCC_EMSG emsg,
+	       const DCC_PATH map_nm0,
+	       int *pfd,		/* leave open & unlocked FD here */
+	       const DCC_SRVR_NM *dcc_nms, int dcc_nms_len,
+	       const DCC_SRVR_NM *grey_nms, int grey_nms_len,
+	       const DCC_IP *src,
+	       u_char info_flags)	/* DCC_INFO_FG_* */
+{
+	static int op_nums_r;
+	DCC_CLNT_INFO info_clear;
+	int fd;
+	u_char created;
+	DCC_PATH map_nm;
+	int i;
+
+	if (pfd && (fd = *pfd) >= 0) {
+		created = 0;
+	} else {
+		if (!fnm2rel(map_nm, map_nm0, 0)) {
+			dcc_pemsg(EX_IOERR, emsg, "long map name \"%s\"",
+				  map_nm);
+			return 0;
+		}
+		fd = open(map_nm, O_RDWR|O_CREAT|O_EXCL, 0600);
+		if (fd < 0) {
+			dcc_pemsg(EX_IOERR, emsg, "open(%s): %s",
+				  map_nm, ERROR_STR());
+			return 0;
+		}
+		created = 1;
+	}
+
+	memset(&info_clear, 0, sizeof(info_clear));
+	strcpy(info_clear.version, DCC_MAP_INFO_VERSION);
+
+	if (dcc_nms_len != 0) {
+		if (dcc_nms_len > DCC_MAX_SRVR_NMS)
+			dcc_nms_len = DCC_MAX_SRVR_NMS;
+		memcpy(info_clear.dcc.nms, dcc_nms,
+		       sizeof(info_clear.dcc.nms[0])*dcc_nms_len);
+	}
+	info_clear.dcc.srvr_inx = NO_SRVR;
+
+	if (grey_nms_len != 0) {
+		if (grey_nms_len > DCC_MAX_SRVR_NMS)
+			grey_nms_len = DCC_MAX_SRVR_NMS;
+		memcpy(info_clear.grey.nms, grey_nms,
+		       sizeof(info_clear.grey.nms[0])*grey_nms_len);
+	}
+	info_clear.grey.srvr_inx = NO_SRVR;
+
+	if (src != 0)
+		info_clear.src = *src;
+
+	info_clear.flags = info_flags;
+	if (!get_clnt_hid(emsg)) {
+		close(fd);
+		if (pfd)
+			*pfd = -1;
+		if (created)
+			unlink(map_nm);
+		return 0;
+	}
+
+	/* ensure that we have a new report ID even if we
+	 * are repeatedly recreating a temporary map file */
+	if (dcc_clnt_info)
+		op_nums_r += dcc_clnt_info->proto_hdr.op_nums.r;
+	info_clear.proto_hdr.op_nums.r = ++op_nums_r;
+
+	i = write(fd, &info_clear, sizeof(info_clear));
+	if (i != ISZ(info_clear)) {
+		if (i < 0)
+			dcc_pemsg(EX_SOFTWARE, emsg, "write(%s): %s",
+				  map_nm, ERROR_STR());
+		else
+			dcc_pemsg(EX_IOERR, emsg,
+				  "write(%s)=%d instead of %d",
+				  map_nm, i, ISZ(info_clear));
+		close(fd);
+		if (pfd)
+			*pfd = -1;
+		if (created)
+			unlink(map_nm);
+		return 0;
+	}
+
+	if (created) {
+		if (pfd)
+			*pfd = fd;
+		else
+			close(fd);
+	}
+	return 1;
+}
+
+
+
+#ifdef DCC_MAP_INFO_VERSION_10
+/* lock and read the contents of the old info file */
+static int				/* -1=error, 0=wrong version, 1=done */
+map_convert_start(DCC_EMSG emsg,
+		  void *old_info, int old_info_size,
+		  const char *old_magic, int old_magic_size,
+		  DCC_PATH new_info_nm)
+{
+	int i;
+
+	assert_ctxts_locked();
+	assert_info_unlocked();
+
+	/* only one process or thread can fix the file so wait for
+	 * exclusive access to the old file */
+	if (!dcc_info_lock(emsg))
+		return -1;
+
+	i = read(info_fd, old_info, old_info_size);
+	if (i != old_info_size) {
+		if (i < 0) {
+			dcc_pemsg(EX_IOERR, emsg, "read(%s): %s",
+				  dcc_info_nm, ERROR_STR());
+		} else {
+			dcc_pemsg(EX_IOERR, emsg, "read(%s)=%d instead of %d",
+				  dcc_info_nm, i, old_info_size);
+		}
+		dcc_info_unlock(0);
+		return -1;
+	}
+
+	if (-1 == lseek(info_fd, SEEK_SET, 0)) {
+		dcc_pemsg(EX_IOERR, emsg, "lseek(%s): %s",
+			  dcc_info_nm, ERROR_STR());
+		dcc_info_unlock(0);
+		return -1;
+	}
+
+	if (strncmp(old_info, old_magic, old_magic_size)) {
+		if (!dcc_info_unlock(emsg))
+			return -1;
+		return 0;
+	}
+
+	if (!fnm2rel(new_info_nm, dcc_info_nm, "-new")) {
+		dcc_pemsg(EX_IOERR, emsg, "long map name \"%s\"",
+			  dcc_info_nm);
+		dcc_info_unlock(0);
+		return -1;
+	}
+	unlink(new_info_nm);
+	return 1;
+}
+
+
+
+/* the old file must be locked */
+static int				/* -1=error, 1=done */
+map_convert_fin(DCC_EMSG emsg,
+		const DCC_PATH new_info_nm,
+		const struct stat *old_sb,
+		const DCC_SRVR_NM *dcc_nms, int dcc_nms_len,
+		const DCC_SRVR_NM *grey_nms, int grey_nms_len,
+		const DCC_IP *src,
+		u_char info_flags)	/* DCC_INFO_FG_* */
+{
+	int new_fd;
+#ifdef DCC_WIN32
+	DCC_PATH old_info_nm;
+#endif
+
+	new_fd = -1;
+	if (!dcc_create_map(emsg, new_info_nm, &new_fd,
+			    dcc_nms, dcc_nms_len, grey_nms, grey_nms_len,
+			    src, info_flags)) {
+		dcc_info_unlock(0);
+		return -1;
+	}
+
+#ifdef DCC_WIN32
+	/* there are at least two races here,
+	 * but Windows does not allow renaming or unlinking (e.g. by
+	 * rename()) open files */
+	if (!fnm2rel(old_info_nm, dcc_info_nm, "-old")) {
+		dcc_pemsg(EX_IOERR, emsg, "long map name \"%s\"",
+			  dcc_info_nm);
+		return -1;
+	}
+	unlink(old_info_nm);
+
+	if (!dcc_info_unlock(emsg)) {
+		close(new_fd);
+		unlink(new_info_nm);
+		return -1;
+	}
+	if (0 > close(info_fd)) {
+		dcc_pemsg(EX_IOERR, emsg, "close(%s): %s",
+			  dcc_info_nm, ERROR_STR());
+		close(new_fd);
+		unlink(new_info_nm);
+		return -1;
+	}
+	info_fd = -1;
+
+	if (0 > rename(dcc_info_nm, old_info_nm)) {
+		dcc_pemsg(EX_IOERR, emsg, "rename(%s, %s): %s",
+			  dcc_info_nm, old_info_nm, ERROR_STR());
+		close(new_fd);
+		unlink(new_info_nm);
+		return -1;
+	}
+
+	close(new_fd);
+	if (0 > rename(new_info_nm, dcc_info_nm)) {
+		dcc_pemsg(EX_IOERR, emsg, "rename(%s, %s): %s",
+			  new_info_nm, dcc_info_nm, ERROR_STR());
+		unlink(new_info_nm);
+		return -1;
+	}
+	return 1;
+#else /* !DCC_WIN32 */
+	/* if we are running as root,
+	 * don't change the owner of the file */
+	if (getuid() == 0
+	    && 0 > fchown(new_fd, old_sb->st_uid, old_sb->st_gid)) {
+		dcc_pemsg(EX_IOERR, emsg, "chown(%s,%d,%d): %s",
+			  new_info_nm, (int)old_sb->st_uid, (int)old_sb->st_gid,
+			  ERROR_STR());
+		unlink(new_info_nm);
+		close(new_fd);
+		return -1;
+	}
+
+	if (0 > rename(new_info_nm, dcc_info_nm)) {
+		dcc_pemsg(EX_IOERR, emsg, "rename(%s, %s): %s",
+			  new_info_nm, dcc_info_nm, ERROR_STR());
+		unlink(new_info_nm);
+		close(new_fd);
+		return -1;
+	}
+
+	if (!dcc_info_unlock(emsg)) {
+		close(new_fd);
+		unlink(new_info_nm);
+		return -1;
+	}
+
+	close(new_fd);
+	return 1;
+#endif /* DCC_WIN32 */
+}
+
+
+#endif /* DCC_MAP_INFO_VERSION_10 */
+#ifdef DCC_MAP_INFO_VERSION_10
+static void
+map_convert_v10_nms(DCC_SRVR_NM new_nms[DCC_MAX_SRVR_NMS],
+		    const DCC_V10_SRVR_NM old_nms[DCC_MAX_SRVR_NMS])
+{
+	int i;
+
+	memset(new_nms, 0, sizeof(DCC_SRVR_NM)*DCC_MAX_SRVR_NMS);
+	for (i = 0; i < DCC_MAX_SRVR_NMS; ++i) {
+		new_nms[i].clnt_id = old_nms[i].clnt_id;
+		new_nms[i].port = old_nms[i].port;
+		strcpy(new_nms[i].hostname, old_nms[i].hostname);
+		memcpy(new_nms[i].passwd, old_nms[i].passwd,
+		       sizeof(new_nms[i].passwd));
+		new_nms[i].rtt_adj = old_nms[i].rtt_adj;
+	}
+}
+#endif /* DCC_MAP_INFO_VERSION_10 */
+
+
+#ifdef DCC_MAP_INFO_VERSION_5
+static int				/* -1=error, 0=wrong version, 1=done */
+map_convert_v5(DCC_EMSG emsg, const struct stat *old_sb)
+{
+	DCC_PATH new_info_nm;
+	DCC_SRVR_NM new_nms[DCC_MAX_SRVR_NMS];
+	DCC_V5_CLNT_INFO old_info;
+	int i;
+
+	if ((int)old_sb->st_size < ISZ(DCC_V5_CLNT_INFO))
+		return 0;
+
+	i = map_convert_start(emsg, &old_info, sizeof(DCC_V5_CLNT_INFO),
+			      DCC_MAP_INFO_VERSION_5, sizeof(old_info.version),
+			      new_info_nm);
+	if (i <= 0)
+		return i;
+
+	memset(&new_nms, 0, sizeof(new_nms));
+	for (i = 0; i < DIM(new_nms); ++i) {
+		new_nms[i].clnt_id = old_info.nms[i].clnt_id;
+		new_nms[i].port = old_info.nms[i].port;
+		strcpy(new_nms[i].hostname, old_info.nms[i].hostname);
+		memcpy(new_nms[i].passwd, old_info.nms[i].passwd,
+		       sizeof(new_nms[i].passwd));
+		new_nms[i].rtt_adj = old_info.nms[i].rtt_adj*10*1000;
+	}
+
+	return map_convert_fin(emsg, new_info_nm, old_sb,
+			       new_nms, DIM(new_nms), 0, 0,
+			       0, old_info.flags);
+}
+#endif /* DCC_MAP_INFO_VERSION_5 */
+
+
+
+#ifdef DCC_MAP_INFO_VERSION_6
+static int				/* -1=error, 0=wrong version, 1=done */
+map_convert_v6(DCC_EMSG emsg, const struct stat *old_sb)
+{
+	DCC_PATH new_info_nm;
+	DCC_SRVR_NM new_nms[DCC_MAX_SRVR_NMS];
+	DCC_V6_CLNT_INFO old_info;
+	int i;
+
+	if ((int)old_sb->st_size < ISZ(DCC_V6_CLNT_INFO))
+		return 0;
+
+	i = map_convert_start(emsg, &old_info, sizeof(DCC_V6_CLNT_INFO),
+			      DCC_MAP_INFO_VERSION_6, sizeof(old_info.version),
+			      new_info_nm);
+	if (i <= 0)
+		return i;
+
+	memset(&new_nms, 0, sizeof(new_nms));
+	for (i = 0; i < DIM(new_nms); ++i) {
+		new_nms[i].clnt_id = old_info.nms[i].clnt_id;
+		new_nms[i].port = old_info.nms[i].port;
+		strcpy(new_nms[i].hostname, old_info.nms[i].hostname);
+		memcpy(new_nms[i].passwd, old_info.nms[i].passwd,
+		       sizeof(new_nms[i].passwd));
+		new_nms[i].rtt_adj = old_info.nms[i].rtt_adj;
+	}
+
+	return map_convert_fin(emsg, new_info_nm, old_sb,
+			       new_nms, DIM(new_nms), 0, 0,
+			       0, old_info.flags);
+}
+#endif /* DCC_MAP_INFO_VERSION_6 */
+
+
+
+#ifdef DCC_MAP_INFO_VERSION_7
+static int				/* -1=error, 0=wrong version, 1=done */
+map_convert_v7(DCC_EMSG emsg, const struct stat *old_sb)
+{
+	DCC_PATH new_info_nm;
+	union {
+	    DCC_V7_IPV6_CLNT_INFO   ipv6;
+	    DCC_V7_NOIPV6_CLNT_INFO noipv6;
+	} old;
+	DCC_SRVR_NM new_nms[DCC_MAX_SRVR_NMS];
+	DCC_SRVR_NM grey_nms[DCC_MAX_SRVR_NMS];
+	int flags, i;
+
+	if (old_sb->st_size == sizeof(old.ipv6)) {
+		i = map_convert_start(emsg, &old.ipv6, sizeof(old.ipv6),
+				      DCC_MAP_INFO_VERSION_7,
+				      sizeof(old.ipv6.version),
+				      new_info_nm);
+		if (i <= 0)
+			return i;
+
+		map_convert_v10_nms(new_nms, old.ipv6.dcc.nms);
+		map_convert_v10_nms(grey_nms, old.ipv6.grey.nms);
+		flags = old.ipv6.flags;
+
+	} else if (old_sb->st_size == sizeof(old.noipv6)) {
+		i = map_convert_start(emsg, &old.noipv6, sizeof(old.noipv6),
+				      DCC_MAP_INFO_VERSION_7,
+				      sizeof(old.noipv6.version),
+				      new_info_nm);
+		if (i <= 0)
+			return i;
+
+		map_convert_v10_nms(new_nms, old.noipv6.dcc.nms);
+		map_convert_v10_nms(grey_nms, old.noipv6.grey.nms);
+		flags = old.noipv6.flags;
+
+	} else {
+		return 0;
+	}
+
+	return map_convert_fin(emsg, new_info_nm, old_sb,
+			       new_nms, DIM(new_nms), grey_nms, DIM(grey_nms),
+			       0, flags);
+}
+
+
+
+#endif /* DCC_MAP_INFO_VERSION_7 */
+#ifdef DCC_MAP_INFO_VERSION_8
+/* Convert an old map file.
+ *      The contexts must be locked on entry.
+ *      The old file may be locked on exit */
+static int				/* -1=error, 0=wrong version, 1=done */
+map_convert_v8(DCC_EMSG emsg, const struct stat *old_sb)
+{
+	DCC_PATH new_info_nm;
+	DCC_V8_CLNT_INFO old;
+	DCC_SRVR_NM new_nms[DCC_MAX_SRVR_NMS];
+	DCC_SRVR_NM grey_nms[DCC_MAX_SRVR_NMS];
+	int i;
+
+	if ((int)old_sb->st_size != ISZ(old))
+		return 0;
+
+	i = map_convert_start(emsg, &old, sizeof(old),
+			      DCC_MAP_INFO_VERSION_8, sizeof(old.version),
+			      new_info_nm);
+	if (i <= 0)
+		return i;
+
+	map_convert_v10_nms(new_nms, old.dcc.nms);
+	map_convert_v10_nms(grey_nms, old.grey.nms);
+
+	return map_convert_fin(emsg, new_info_nm, old_sb,
+			       new_nms, DIM(new_nms), grey_nms, DIM(grey_nms),
+			       0, old.flags);
+}
+
+
+
+#endif /* DCC_MAP_INFO_VERSION_8 */
+#ifdef DCC_MAP_INFO_VERSION_9
+/* Convert an old map file.
+ *      The contexts must be locked on entry.
+ *      The old file may be locked on exit */
+static int				/* -1=error, 0=wrong version, 1=done */
+map_convert_v9(DCC_EMSG emsg, const struct stat *old_sb)
+{
+	DCC_PATH new_info_nm;
+	DCC_V9_CLNT_INFO old;
+	DCC_SRVR_NM nms[DCC_MAX_SRVR_NMS];
+	DCC_SRVR_NM grey_nms[DCC_MAX_SRVR_NMS];
+	int i;
+
+	if ((int)old_sb->st_size != ISZ(old))
+		return 0;
+
+	i = map_convert_start(emsg, &old, sizeof(old),
+			      DCC_MAP_INFO_VERSION_9, sizeof(old.version),
+			      new_info_nm);
+	if (i <= 0)
+		return i;
+
+	map_convert_v10_nms(nms, old.dcc.nms);
+	map_convert_v10_nms(grey_nms, old.grey.nms);
+
+	return map_convert_fin(emsg, new_info_nm, old_sb,
+			       nms, DIM(nms), grey_nms, DIM(grey_nms),
+			       &old.src, old.flags);
+}
+
+
+
+#endif /* DCC_MAP_INFO_VERSION_9 */
+#ifdef DCC_MAP_INFO_VERSION_10
+/* Convert an old map file.
+ *      The contexts must be locked on entry.
+ *      The old file may be locked on exit */
+static int				/* -1=error, 0=wrong version, 1=done */
+map_convert_v10(DCC_EMSG emsg, const struct stat *old_sb)
+{
+	DCC_PATH new_info_nm;
+	DCC_V10_CLNT_INFO old;
+	DCC_SRVR_NM new_nms[DCC_MAX_SRVR_NMS];
+	DCC_SRVR_NM grey_nms[DCC_MAX_SRVR_NMS];
+	int i;
+
+	if ((int)old_sb->st_size != ISZ(old))
+		return 0;
+
+	i = map_convert_start(emsg, &old, sizeof(old),
+			      DCC_MAP_INFO_VERSION_10, sizeof(old.version),
+			      new_info_nm);
+	if (i <= 0)
+		return i;
+
+	map_convert_v10_nms(new_nms, old.dcc.nms);
+	map_convert_v10_nms(grey_nms, old.grey.nms);
+
+	return map_convert_fin(emsg, new_info_nm, old_sb,
+			       new_nms, DIM(new_nms), grey_nms, DIM(grey_nms),
+			       &old.src, old.flags);
+}
+
+
+
+#endif /* DCC_MAP_INFO_VERSION_10 */
+/* convert from a previous version
+ *	The contexts must be locked.  The old file must be open and unlocked */
+static u_char
+map_convert(DCC_EMSG emsg,
+	    const struct stat *old_sb)
+{
+	int i;
+
+	assert_ctxts_locked();
+	assert_info_unlocked();
+
+#ifdef DCC_MAP_INFO_VERSION_6
+	i = map_convert_v5(emsg, old_sb);
+	if (i < 0) {
+		dcc_unmap_close_info(0);
+		return 0;
+	}
+	/* unlock old file and open & lock new file */
+	if (i > 0)
+		return 1;
+
+	i = map_convert_v6(emsg, old_sb);
+	if (i < 0) {
+		dcc_unmap_close_info(0);
+		return 0;
+	}
+	/* unlock old file and open & lock new file */
+	if (i > 0)
+		return 1;
+#endif /* DCC_MAP_INFO_VERSION_6 */
+#ifdef DCC_MAP_INFO_VERSION_7
+	i = map_convert_v7(emsg, old_sb);
+	if (i < 0) {
+		dcc_unmap_close_info(0);
+		return 0;
+	}
+	/* unlock old file and open & lock new file */
+	if (i > 0)
+		return 1;
+#endif /* DCC_MAP_INFO_VERSION_7 */
+#ifdef DCC_MAP_INFO_VERSION_8
+	i = map_convert_v8(emsg, old_sb);
+	if (i < 0) {
+		dcc_unmap_close_info(0);
+		return 0;
+	}
+	/* unlock old file and open & lock new file */
+	if (i > 0)
+		return 1;
+#endif /* DCC_MAP_INFO_VERSION_8 */
+#ifdef DCC_MAP_INFO_VERSION_9
+	i = map_convert_v9(emsg, old_sb);
+	if (i < 0) {
+		dcc_unmap_close_info(0);
+		return 0;
+	}
+	/* unlock old file and open & lock new file */
+	if (i > 0)
+		return 1;
+#endif /* DCC_MAP_INFO_VERSION_9 */
+#ifdef DCC_MAP_INFO_VERSION_10
+	i = map_convert_v10(emsg, old_sb);
+	if (i < 0) {
+		dcc_unmap_close_info(0);
+		return 0;
+	}
+	/* unlock old file and open & lock new file */
+	if (i > 0)
+		return 1;
+#endif /* DCC_MAP_INFO_VERSION_10 */
+	dcc_pemsg(EX_DATAERR, emsg, "%s is not a DCC map file",
+		  dcc_info_nm);
+	close(info_fd);
+	info_fd = -1;
+	return 0;
+}
+
+
+
+/* Ensure the shared information is available, but do not lock it.
+ *      The contexts must be locked
+ *      SUID privileges are often released */
+u_char
+dcc_map_info(DCC_EMSG emsg,		/* cleared of stale messages */
+	     const char *new_info_nm, int new_info_fd)
+{
+	struct stat sb;
+#ifndef DCC_WIN32
+	void *p;
+#endif
+
+	for (;;) {
+		assert_ctxts_locked();
+		assert_info_unlocked();
+
+		/* work only if needed,
+		 * but always check for a version changed */
+		if (!(new_info_nm && strcmp(new_info_nm, dcc_info_nm))
+		    && new_info_fd < 0
+		    && dcc_clnt_info)
+			return 1;
+
+		if (!dcc_unmap_close_info(emsg)) {
+			if (new_info_fd >= 0)
+				close(new_info_fd);
+			return 0;
+		}
+
+		if (new_info_nm) {
+			if (!fnm2rel(dcc_info_nm, new_info_nm, 0)) {
+				dcc_pemsg(EX_IOERR, emsg, "bad map name \"%s\"",
+					  new_info_nm);
+				return 0;
+			}
+			/* don't change name if we convert the file
+			 * and so come back here */
+			new_info_nm = 0;
+		}
+		if (dcc_info_nm[0] == '\0') {
+			dcc_pemsg(EX_USAGE, emsg, "missing map file name");
+			return 0;
+		}
+
+		if (new_info_fd >= 0) {
+			info_fd = new_info_fd;
+			new_info_fd = -1;
+		} else {
+			info_fd = open(dcc_info_nm, O_RDWR, 0600);
+#ifndef DCC_WIN32
+			if (info_fd < 0
+			    && dcc_get_priv_home(dcc_info_nm)) {
+				info_fd = open(dcc_info_nm, O_RDWR, 0600);
+				dcc_rel_priv();
+			}
+#endif
+			if (info_fd < 0) {
+				dcc_pemsg(EX_NOINPUT, emsg, "open(%s): %s",
+					  dcc_info_nm, ERROR_STR());
+				return 0;
+			}
+		}
+
+		/* refuse to use the file if it is not private */
+		if (!dcc_ck_private(emsg, &sb, dcc_info_nm, info_fd)) {
+			dcc_unmap_close_info(0);
+			return 0;
+		}
+
+		if ((int)sb.st_size != ISZ(*dcc_clnt_info)) {
+			if (map_convert(emsg, &sb))
+				continue;
+			return 0;
+		}
+
+#ifdef DCC_WIN32
+		dcc_clnt_info = win32_map(emsg, &info_map, dcc_info_nm,
+					  info_fd, sizeof(*dcc_clnt_info));
+		if (!dcc_clnt_info) {
+			close(info_fd);
+			info_fd = -1;
+			return 0;
+		}
+#else
+
+		/* don't give it to children */
+		if (0 > fcntl(info_fd, F_SETFD, FD_CLOEXEC)) {
+			dcc_pemsg(EX_IOERR, emsg,
+				  "fcntl(F_SETFD FD_CLOEXEC %s): %s",
+				  dcc_info_nm, ERROR_STR());
+			close(info_fd);
+			info_fd = -1;
+			return 0;
+		}
+
+		p = mmap(0, sizeof(*dcc_clnt_info),
+			 PROT_READ|PROT_WRITE, MAP_SHARED, info_fd, 0);
+		if (p == MAP_FAILED) {
+			dcc_pemsg(EX_IOERR, emsg, "mmap(%s): %s",
+				  dcc_info_nm, ERROR_STR());
+			close(info_fd);
+			info_fd = -1;
+			return 0;
+		}
+		dcc_clnt_info = p;
+#endif /* DCC_WIN32 */
+
+		if (!strncmp(dcc_clnt_info->version, DCC_MAP_INFO_VERSION,
+			     sizeof(dcc_clnt_info->version))) {
+			/* The file is the right version.  Set our ID in case
+			 * it has been copied from another system */
+			if (!get_clnt_hid(emsg))
+				return 0;
+			dcc_clnt_info->proto_hdr.op_nums.h = dcc_clnt_hid;
+
+			return 1;
+		}
+
+		unmap_info(0);
+		if (!map_convert(emsg, &sb))
+			return 0;
+	}
+}
+
+
+
+/* SUID privileges are often released */
+u_char					/* 0=something wrong, 1=mapped */
+dcc_map_lock_info(DCC_EMSG emsg,	/* cleared of stale messages */
+		  const char *new_info_nm,
+		  int new_info_fd)
+{
+	if (!dcc_map_info(emsg, new_info_nm, new_info_fd))
+		return 0;
+
+	if (!dcc_info_lock(emsg))
+		return 0;
+
+	return 1;
+}
+
+
+
+/* All servers are broken, so make a note to not try for a while.
+ *      The contexts and the mapped information must be locked */
+static void
+fail_more(const DCC_CLNT_CTXT *ctxt, DCC_SRVR_CLASS *class)
+{
+	assert_info_locked();
+
+	/* do not inflate the delay if we are already delaying */
+	if (class->fail_exp != 0
+	    && class->fail_time >= ctxt->now.tv_sec)
+		return;
+
+	/* reset the backoff after a long quiet time */
+	if (ctxt->now.tv_sec >= (class->fail_time
+				 + (DCC_INIT_FAIL_SECS << class->fail_exp)))
+		class->fail_exp = 0;
+
+	if (++class->fail_exp > DCC_MAX_FAIL_EXP)
+		class->fail_exp = DCC_MAX_FAIL_EXP;
+	class->fail_time = (ctxt->now.tv_sec
+			    + (DCC_INIT_FAIL_SECS << class->fail_exp));
+}
+
+
+
+static u_char				/* 0=failing */
+ck_fail_time(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt, DCC_SRVR_CLASS *class)
+{
+	int dt;
+
+	assert_info_locked();
+
+	if (class->fail_exp == 0)
+		return 1;
+
+	dt = class->fail_time - ctxt->now.tv_sec;
+	if (dt > 0 && dt <= DCC_MAX_FAIL_SECS) {
+		dcc_pemsg(EX_IOERR, emsg,
+			  "continue not asking %s %d seconds after failure",
+			  DCC_IS_GREY_STR(class), dt);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+void
+dcc_force_measure_rtt(DCC_SRVR_CLASS *class)
+{
+	assert_info_locked();
+
+	class->fail_exp = 0;		/* stop giving up */
+
+	class->resolve = 0;		/* force name resolution */
+
+	class->srvr_inx = NO_SRVR;
+
+	class->measure = 0;		/* force RTT measurement */
+}
+
+
+
+/* pick the best server
+ *      The client information and the contexts must be exclusively locked.
+ *      Assume there is at least one hostname. */
+static u_char				/* 0=have none, 1=found one */
+pick_srvr(DCC_EMSG emsg, DCC_SRVR_CLASS *class)
+{
+	const DCC_SRVR_ADDR *ap, *min_ap;
+	int rtt;
+	int min_rtt;			/* smallest RTT	*/
+	int min2_rtt;			/* second smallest RTT */
+	SRVR_INX old_srvr_inx;
+
+	assert_info_locked();
+
+	if (class->num_srvrs == 0) {
+		class->srvr_inx = NO_SRVR;
+		extra_pemsg(EX_USAGE, emsg, "no valid %s server IP addresses",
+			    DCC_IS_GREY_STR(class));
+		return 0;
+	}
+
+	old_srvr_inx = class->srvr_inx;
+	min2_rtt = min_rtt = DCC_RTT_BAD;
+	min_ap = 0;
+	ap = &class->addrs[class->num_srvrs];
+	while (ap > class->addrs) {
+		--ap;
+		rtt = effective_rtt(class, ap);
+		if (rtt == DCC_RTT_BAD)
+			continue;
+
+		if (min_rtt > rtt) {
+			if (min2_rtt > min_rtt)
+				min2_rtt = min_rtt;
+			min_rtt = rtt;
+			min_ap = ap;
+		} else if (min2_rtt > rtt) {
+			min2_rtt = rtt;
+		}
+	}
+
+	/* we found a usable server */
+	if (min_ap) {
+		/* Compute the basic RTT to the server including a variance */
+		class->base_rtt = min_rtt + DCC_DCCD_DELAY;
+		if (class->base_rtt > DCC_MAX_RTT)
+			class->base_rtt = DCC_MAX_RTT;
+		/* Decide how bad the server must get before we check for
+		 *	an alternative.
+		 * If there is no good second choice, there is no point in a
+		 *	threshold for switching to it */
+		class->thold_rtt = min2_rtt + DCC_DCCD_DELAY;
+		if (class->thold_rtt >= DCC_MAX_RTT)
+			class->thold_rtt = DCC_RTT_BAD;
+
+		class->srvr_inx = (min_ap - class->addrs);
+		if (class->srvr_inx != old_srvr_inx) {
+			if (dcc_clnt_debug > 1 &&
+			    GOOD_SRVR(class, old_srvr_inx)) {
+				trace_perf("replacing",
+					   &class->addrs[old_srvr_inx]);
+				trace_perf("pick", min_ap);
+			}
+		}
+		return 1;
+	}
+
+	/* we failed to find a working server */
+	class->srvr_inx = NO_SRVR;
+
+	flush_emsg(emsg, dcc_clnt_debug);
+	if (!emsg || *emsg == '\0') {
+		char astr[(DCC_SU2STR_SIZE+1+5+1)*3];
+		const char *s, *h0;
+		int l;
+
+		l = dcc_ap2str_opt(astr, sizeof(astr),
+				   class, 0, '\0');
+		if (!strcmp(class->nms[0].hostname, astr)) {
+			h0 = "";
+			s = "";
+		} else {
+			h0 = class->nms[0].hostname;
+			s = class->nms[1].hostname[0] ? "s" : " ";
+		}
+
+		if (class->num_srvrs > 1 && l < ISZ(astr)-2) {
+			astr[l++] = ' ';
+			l += dcc_ap2str_opt(&astr[l], sizeof(astr)-l,
+					    class, 1, '\0');
+		}
+		if (class->num_srvrs > 2 && l < ISZ(astr)-2) {
+			astr[l++] = ' ';
+			dcc_ap2str_opt(&astr[l], sizeof(astr)-l,
+					    class, 1, '\0');
+		}
+		dcc_pemsg(EX_IOERR, emsg,
+			  "no working %s server%s%s%s%s%s%s%s"
+			  " at %s%s",
+			  DCC_IS_GREY_STR(class),
+			  s, h0,
+			  class->nms[1].hostname[0] ? " " : "",
+			  class->nms[1].hostname,
+			  class->nms[2].hostname[0] ? " " : "",
+			  class->nms[2].hostname,
+			  class->nms[3].hostname[0] ? " ..." : "",
+
+			  astr,
+			  class->num_srvrs > 3 ? " ..." : "");
+	}
+	return 0;
+}
+
+
+
+/* count IP addresses per host name and per second level domain name */
+typedef struct name_addrs {
+    const char *sld;			/* domain name */
+    u_char     sld_addrs;		/* # of addresses for domain name */
+    u_char     host_addrs;		/* # of addresses for a host name */
+    u_char     sld_addrs_inx;
+} NAME_ADDRS[DCC_MAX_SRVR_NMS];
+
+
+/* delete an address from a growing list of addresses */
+static void
+del_new_addr(DCC_SRVR_CLASS *class,
+	     NAME_ADDRS name_addrs,	/* addresses per server name */
+	     int tgt)			/* delete this address */
+{
+	NAM_INX nam_inx;
+	int i;
+
+	/* adjust that host's and domain's numbers of addresses and our
+	 * total number of addresses */
+	nam_inx = class->addrs[tgt].nam_inx;
+	--name_addrs[nam_inx].host_addrs;
+	--name_addrs[name_addrs[nam_inx].sld_addrs_inx].sld_addrs;
+	--class->num_srvrs;
+
+	/* slide the array of addresses to get rid of the discarded address */
+	i = class->num_srvrs - tgt;
+	if (i > 0)
+		memmove(&class->addrs[tgt], &class->addrs[tgt+1],
+			i * sizeof(class->addrs[0]));
+	memset(&class->addrs[class->num_srvrs], 0, sizeof(class->addrs[0]));
+}
+
+
+
+/* impose arbitrary local order on IP addresses */
+#define DCC_SRVRS_MOD	    16381
+
+static inline u_int
+su_srvrs_mod(const DCC_SOCKU *sup,
+	     DCC_SOCKU *sup2)
+{
+	u_int su_res;
+
+	if (dcc_ipv6sutoipv4(sup2, sup)) {
+		su_res = sup2->ipv4.sin_addr.s_addr % DCC_SRVRS_MOD;
+		su_res *= dcc_clnt_info->residue;
+		su_res %= DCC_SRVRS_MOD;
+		su_res += DCC_SRVRS_MOD;    /* distinguish IPv4 from IPv6 */
+	} else {
+		*sup2 = *sup;
+		su_res = (sup->ipv6.sin6_addr.s6_addr32[0] % DCC_SRVRS_MOD
+			  + sup->ipv6.sin6_addr.s6_addr32[1] % DCC_SRVRS_MOD
+			  + sup->ipv6.sin6_addr.s6_addr32[2] % DCC_SRVRS_MOD
+			  + sup->ipv6.sin6_addr.s6_addr32[3] % DCC_SRVRS_MOD);
+		su_res *= dcc_clnt_info->residue;
+		su_res %= DCC_SRVRS_MOD;
+	}
+	return su_res;
+}
+
+
+
+/* partially order a pair of IP addresses with a reasonably unique ordering */
+static int
+sucmp(const DCC_SOCKU *sup1, const DCC_SOCKU *sup2)
+{
+	DCC_SOCKU su1, su2;
+	u_int su1_res, su2_res;
+	int i;
+
+	su1_res = su_srvrs_mod(sup1, &su1);
+	su2_res = su_srvrs_mod(sup2, &su2);
+
+	i = (int)su1_res - (int)su2_res;
+	if (i)
+		return i;
+	return memcmp(&su1, &su2, sizeof(DCC_SOCKU));
+}
+
+
+
+/* Deal with a list of IP addresses or aliases for one DCC server hostname.
+ *	the contexts and the mmap()'ed info must be locked */
+static void
+copy_addrs(DCC_SRVR_CLASS *class,
+	   const DCC_SRVR_NM *nmp,	/* server name being resolved */
+	   const int nam_inx,
+	   NAME_ADDRS name_addrs)	/* addresses per server name */
+{
+	DCC_SRVR_ADDR *ap;
+	const DCC_SRVR_NM *nmp2;
+	DCC_SOCKU *np, su, nxt, prev;
+	u_int16_t port;
+	int i, j, k;
+
+	/* Keep as many IP addresses as we have room, but for as many
+	 * named servers as possible
+	 * Sort the addresses to keep our list stable when we re-check.
+	 * Otherwise, we would start from scratch when nothing changes
+	 * but the order of responses from a DNS server.
+	 * Sort by residue class to pick a random subset when there
+	 * are too many servers to fit in our list. */
+
+	port = nmp->port;
+
+	nxt.sa.sa_family = AF_UNSPEC;
+	for (;;) {
+		/* Pick the next address in the newly resolved list
+		 * to consider.  We want the smallest address larger
+		 * than the previous address we considered.
+		 * "Smallest" is defined using the local random ordering
+		 * of addresses. */
+		prev = nxt;
+		nxt.sa.sa_family = AF_UNSPEC;
+		for (np = dcc_hostaddrs; np < dcc_hostaddrs_end; ++np) {
+			if (np->sa.sa_family == AF_UNSPEC)
+				continue;
+			su = *np;
+			*DCC_SU_PORTP(&su) = port;
+			if ((prev.sa.sa_family == AF_UNSPEC
+			     || sucmp(&su, &prev) > 0)
+			    && (nxt.sa.sa_family == AF_UNSPEC
+				|| sucmp(&nxt, &su) > 0))
+				nxt = su;
+		}
+		/* quit if we've considered them all */
+		if (nxt.sa.sa_family == AF_UNSPEC)
+			break;
+
+		/* ignore duplicate IP addresses even for other hostnames,
+		 * unless the port numbers differ */
+		ap = &class->addrs[class->num_srvrs];
+		while (--ap >= class->addrs) {
+			if (!dcc_cmp_ap2su(ap, &nxt)) {
+				/* they are the same, so keep the one with
+				 * the non-anonymous ID
+				 * or smallest RTT adjustment */
+				nmp2 = &class->nms[ap->nam_inx];
+				i = (nmp->clnt_id == DCC_ID_ANON);
+				j = (nmp2->clnt_id == DCC_ID_ANON);
+				if (i != j) {
+					/* one is anonymous & other is not */
+					if (i)
+					    goto next_addr;
+				} else {
+					/* pick smallest RTT adjustment */
+					if (nmp->rtt_adj >= nmp2->rtt_adj)
+					    goto next_addr;
+				}
+				/* delete the previous instance */
+				del_new_addr(class, name_addrs,
+					     ap - class->addrs);
+				break;
+			}
+		}
+
+		/* If we already have as many addresses as we will use,
+		 * then pick one to discard. Discard the last address of
+		 * the host in the second level domain with the most
+		 * addresses but without eliminating all addresses for any
+		 * host name.  Look for the domain with the most IP addresses
+		 * and that has at least one host with at least two
+		 * addersses. */
+		if (class->num_srvrs == DCC_MAX_SRVR_ADDRS) {
+			int host_max, sld_max;
+			NAM_INX nam1_inx, sld1_inx, sld2_inx;
+
+			host_max = -1;
+			sld_max = -1;
+			nam1_inx = NO_NAM;
+			sld1_inx = NO_NAM;
+			for (i = 0; i <= nam_inx; i++) {
+				/* ignore hosts with only 1 IP address */
+				j = name_addrs[i].host_addrs;
+				if (j <= 1)
+					continue;
+				sld2_inx = name_addrs[i].sld_addrs_inx;
+				k = name_addrs[sld2_inx].sld_addrs;
+				if (sld_max <= k) {
+					if (sld1_inx != sld2_inx) {
+					    sld_max = k;
+					    sld1_inx = sld2_inx;
+					    host_max = j;
+					    nam1_inx = i;
+					} else if (host_max <= j) {
+					    host_max = j;
+					    nam1_inx = i;
+					}
+				}
+			}
+			/* no additional IP addresses for the target host if
+			 * it has the most IP addresses */
+			if (nam1_inx == nam_inx)
+				return;
+
+			/* find the last address of the host with the most */
+			for (i = 0, j = 0; i < class->num_srvrs; i++) {
+				if (class->addrs[i].nam_inx == nam1_inx)
+					j = i;
+			}
+			/* and delete it */
+			del_new_addr(class, name_addrs, j);
+		}
+
+		/* install the new address in the growing list */
+		ap = &class->addrs[class->num_srvrs];
+		ap->rtt = DCC_RTT_BAD;
+		if (nxt.sa.sa_family == AF_INET && DCC_INFO_IPV6())
+			dcc_ipv4sutoipv6(&nxt, &nxt);
+		else if (nxt.sa.sa_family == AF_INET6 && !DCC_INFO_IPV6())
+			dcc_ipv6sutoipv4(&nxt, &nxt);
+		dcc_su2ip(&ap->ip, &nxt);
+
+		/* If this is a previously known address,
+		 * preserve what we already knew about it
+		 * Check the address family separately because dcc_cmp_ap2su()
+		 * does not and DCC_INFO_IPV6() might have changed. */
+		for (i = 0; i < class->num_srvrs; ++i) {
+			if (class->addrs[i].ip.family == nxt.sa.sa_family
+			    && !dcc_cmp_ap2su(&class->addrs[i], &nxt)) {
+				*ap = class->addrs[i];
+				break;
+			}
+		}
+		ap->nam_inx = nam_inx;
+		++class->num_srvrs;
+
+		++name_addrs[nam_inx].host_addrs;
+		++name_addrs[name_addrs[nam_inx].sld_addrs_inx].sld_addrs;
+next_addr:;
+	}
+}
+
+
+
+/* resolve one server name into a scratch array of addresses */
+static void
+resolve_nm(DCC_EMSG emsg,
+	   DCC_SRVR_CLASS *class,
+	   int nm_inx,			/* name being resolved */
+	   NAME_ADDRS name_addrs)	/* addresses per server name */
+{
+	DCC_SRVR_NM *nmp;
+	const char *domain, *p1, *p2;
+	int error;
+	u_char result;
+	int i;
+
+	nmp = &class->nms[nm_inx];
+	nmp->defined = 0;
+	if (nmp->hostname[0] == '\0')
+		return;
+
+	if (nmp->rtt_adj > DCC_RTT_ADJ_MAX)
+		nmp->rtt_adj = DCC_RTT_ADJ_MAX;
+	else if (nmp->rtt_adj < -DCC_RTT_ADJ_MAX)
+		nmp->rtt_adj = -DCC_RTT_ADJ_MAX;
+
+	/* find the total number of addresses for this domain name */
+	domain = nmp->hostname;
+	p1 = strchr(domain, '.');
+	if (p1) {
+		for (;;) {
+			p2 = strchr(++p1, '.');
+			if (!p2)
+				break;
+			domain = p1;
+			p1 = p2;
+		}
+	}
+	name_addrs[nm_inx].sld = domain;
+	for (i = 0; i < nm_inx; ++i) {
+		if (name_addrs[i].sld != 0
+		    && !strcmp(domain, name_addrs[i].sld))
+			break;
+	}
+	name_addrs[nm_inx].sld_addrs_inx = i;
+
+	dcc_host_lock();
+	if (dcc_clnt_info->flags & DCC_INFO_FG_SOCKS)
+		result = dcc_get_host_SOCKS(nmp->hostname,
+					    DCC_INFO_IPV6() ? 2 : 0, &error);
+	else
+		result = dcc_get_host(nmp->hostname,
+				      DCC_INFO_IPV6() ? 2 : 0, &error);
+	if (!result) {
+		dcc_pemsg(EX_NOHOST, emsg, "%s: %s",
+			  nmp->hostname, DCC_HSTRERROR(error));
+		dcc_host_unlock();
+		return;
+	}
+	nmp->defined = 1;
+	copy_addrs(class, nmp, nm_inx, name_addrs);
+	dcc_host_unlock();
+}
+
+
+
+/* resolve server hostnames again
+ *      both locks must be held on entry
+ *      both will be released while working
+ *      on success, both are held
+ *      on failure only the contexts are locked
+ */
+static u_char				/* 0=no good addresses, 1=at least 1 */
+resolve_nms(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt, DCC_SRVR_CLASS *cur)
+{
+	DCC_SRVR_CLASS new;
+	int nm_inx, a_inx;
+	NAME_ADDRS name_addrs;
+	DCC_SRVR_ADDR *new_ap, *cur_ap;
+
+	assert_info_locked();
+
+	if (dcc_clnt_debug > 1)
+		dcc_trace_msg("resolve %s server host names",
+			      DCC_IS_GREY_STR(cur));
+
+	/* try not to resolve names too often
+	 * and discourage other processes and threads from resolving
+	 * or measuring RTTs until we finish */
+	cur->resolve = ctxt->now.tv_sec+DCC_MAP_RESOLVE;
+	cur->measure = ctxt->now.tv_sec+FAST_RTT_SECS;
+
+	if (cur->nms[0].hostname[0] == '\0') {
+		if (HAVE_SRVR(cur)) {
+			++cur->gen;
+			cur->avg_thold_rtt = DCC_RTT_BAD;
+			cur->srvr_inx = NO_SRVR;
+		}
+		cur->num_srvrs = 0;
+		memset(cur->addrs, 0, sizeof(cur->addrs));
+		extra_pemsg(EX_USAGE, emsg, "no %s server hostnames",
+			    DCC_IS_GREY_STR(cur));
+		dcc_info_unlock(0);
+		return 0;
+	}
+
+	new = *cur;
+	memset(new.addrs, 0, sizeof(new.addrs));
+	new.num_srvrs = 0;
+	memset(&name_addrs, 0, sizeof(name_addrs));
+
+	if (dcc_clnt_info->residue == 0) {
+		dcc_clnt_info->residue = dcc_clnt_hid % DCC_SRVRS_MOD;
+		if (dcc_clnt_info->residue == 0)
+			dcc_clnt_info->residue = 1;
+	}
+
+	/* unlock everything while we wait for DNS */
+	if (!dcc_info_unlock(emsg)) {
+		cur->resolve = 0;
+		return 0;
+	}
+	dcc_ctxts_unlock();
+	if (emsg)
+		*emsg = '\0';
+	for (nm_inx = 0; nm_inx < DIM(cur->nms); ++nm_inx)
+		resolve_nm(emsg, &new, nm_inx, name_addrs);
+	dcc_ctxts_lock();
+	if (!dcc_info_lock(emsg)) {
+		cur->resolve = 0;
+		return 0;
+	}
+
+	/* measure all RTTs at least as often as we resolve names */
+	cur->measure = 0;
+
+	/* if we fail to resolve even one server host names,
+	 * complain but try to continue using the old IP addresses */
+	a_inx = new.num_srvrs;
+	if (a_inx == 0) {
+		extra_pemsg(EX_USAGE, emsg, "no valid %s server hostnames",
+			    DCC_IS_GREY_STR(cur));
+		dcc_info_unlock(0);
+		return 0;
+	}
+
+	/* see if anything changed */
+	for (nm_inx = 0; nm_inx < DIM(cur->nms); ++nm_inx) {
+		if (cur->nms[nm_inx].defined != new.nms[nm_inx].defined)
+			break;
+	}
+	if (nm_inx >= DIM(cur->nms)
+	    && a_inx == cur->num_srvrs) {
+		/* we have the same number of old and new names and addresses,
+		 * so compare the old and new addresses */
+		new_ap = new.addrs;
+		cur_ap = cur->addrs;
+		for (;;) {
+			if (new_ap->nam_inx != cur_ap->nam_inx
+			    || memcmp(&new_ap->ip, &cur_ap->ip,
+				      sizeof(new_ap->ip))) {
+				break;
+			}
+			++new_ap;
+			++cur_ap;
+			if (!--a_inx)
+				return 1;   /* nothing changed */
+		}
+	}
+
+	/* Something changed, so we must compute RTTs */
+	++cur->gen;
+	cur->srvr_inx = NO_SRVR;
+	cur->avg_thold_rtt = -DCC_RTT_BAD;
+	memcpy(&cur->addrs, &new.addrs, sizeof(cur->addrs));
+	cur->num_srvrs = new.num_srvrs;
+	for (nm_inx = 0; nm_inx < DIM(cur->nms); ++nm_inx)
+		cur->nms[nm_inx].defined = new.nms[nm_inx].defined;
+
+	return 1;
+}
+
+
+
+void
+dcc_clnt_soc_close(DCC_CLNT_CTXT *ctxt)
+{
+	if (ctxt->soc == INVALID_SOCKET)
+		return;
+	if (SOCKET_ERROR == closesocket(ctxt->soc)
+	    && dcc_clnt_debug)
+		dcc_trace_msg("closesocket(ctxt): %s", ERROR_STR());
+	ctxt->soc = INVALID_SOCKET;
+	ctxt->conn_su.sa.sa_family = AF_UNSPEC;
+}
+
+
+
+/* disconnect (or close) and (re)open the client
+ *	The contexts and shared information must be locked on entry
+ *	and both are locked on exit */
+u_char					/* 0=failed to open the socket */
+dcc_clnt_soc_reopen(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt)
+{
+	DCC_SOCKU su;
+	DCC_SOCKLEN_T soc_len;
+	int retries;
+
+	assert_info_locked();
+
+	if (ctxt->soc != INVALID_SOCKET)
+		dcc_clnt_soc_close(ctxt);
+
+	/* try to bind to the specified local interface address
+	 * if it has changed
+	 * or if it has been some time since we last tried and failed. */
+	if (dcc_clnt_info->src.family == AF_UNSPEC) {
+		ctxt->flags &= ~DCC_CTXT_SRCBAD;
+	} else if (!(ctxt->flags & DCC_CTXT_SRCBAD)
+		   || DCC_IS_TIME(ctxt->start.tv_sec, ctxt->bind_time,
+				  DCC_CTXT_REBIND_SECS)) {
+		dcc_mk_su(&su, dcc_clnt_info->src.family,
+			  &dcc_clnt_info->src.u, 0);
+		*DCC_SU_PORTP(&su) = DCC_SU_PORT(&ctxt->bind_su);
+		retries = -1;
+		if (0 >= dcc_udp_bind(emsg, &ctxt->soc, &su, &retries)) {
+			ctxt->flags |= DCC_CTXT_SRCBAD;
+			ctxt->bind_time = (ctxt->start.tv_sec
+					   + DCC_CTXT_REBIND_SECS);
+			return 0;
+		}
+		ctxt->flags &= ~DCC_CTXT_SRCBAD;
+		ctxt->bind_time = 0;
+
+		/* we have a bound socket */
+	}
+
+	/* If we do not have a bound socket,
+	 * try to bind a new socket with IPv6 first if allowed */
+	if (ctxt->soc == INVALID_SOCKET && DCC_INFO_IPV6()) {
+		dcc_mk_su(&su, AF_INET6, 0, DCC_SU_PORT(&ctxt->bind_su));
+		retries = -1;
+		if (!dcc_udp_bind(emsg, &ctxt->soc, &su, &retries))
+			return 0;
+	}
+
+	/* if we still do not have a socket, try IPv4 */
+	if (ctxt->soc == INVALID_SOCKET) {
+		dcc_clnt_info->flags &= ~DCC_INFO_FG_IPV6;
+		dcc_mk_su(&su, AF_INET, 0, DCC_SU_PORT(&ctxt->bind_su));
+		retries = -1;
+		if (!dcc_udp_bind(emsg, &ctxt->soc, &su, &retries))
+			return 0;
+	}
+
+#if !defined(USE_POLL) && !defined(DCC_WIN32)
+	if (ctxt->soc >= FD_SETSIZE) {
+		dcc_info_unlock(0);
+		dcc_pemsg(EX_IOERR, emsg, "socket FD %d > FD_SETSIZE %d",
+			  ctxt->soc, FD_SETSIZE);
+		dcc_clnt_soc_close(ctxt);
+		return 0;
+	}
+#endif
+
+#if defined(IPPROTO_IP) && defined(IP_TTL)
+	if (dcc_debug_ttl != 0
+	    && 0 > setsockopt(ctxt->soc, IPPROTO_IP, IP_TTL,
+			      (void *)&dcc_debug_ttl, sizeof(dcc_debug_ttl))) {
+		dcc_pemsg(EX_IOERR, emsg, "setsockopt(TTL=%d):%s",
+			  dcc_debug_ttl, ERROR_STR());
+		dcc_clnt_soc_close(ctxt);
+		return 0;
+	}
+#endif
+
+	soc_len = sizeof(ctxt->bind_su);
+	if (0 > getsockname(ctxt->soc, &ctxt->bind_su.sa, &soc_len)) {
+		dcc_pemsg(EX_IOERR, emsg, "getsockname(): %s", ERROR_STR());
+		dcc_clnt_soc_close(ctxt);
+		return 0;
+	}
+	if (su.sa.sa_family == AF_INET)
+		ctxt->flags |= DCC_CTXT_USING_IPV4;
+	else
+		ctxt->flags &= ~DCC_CTXT_USING_IPV4;
+	return 1;
+}
+
+
+
+static int
+do_recv(DCC_CLNT_CTXT *ctxt, DCC_OP_RESP *resp, int resp_len, DCC_SOCKU *sup)
+{
+	DCC_SOCKLEN_T su_len;
+
+	su_len = sizeof(*sup);
+	memset(sup, 0, sizeof(*sup));
+	if (dcc_clnt_info->flags & DCC_INFO_FG_SOCKS)
+		return Rrecvfrom(ctxt->soc, WIN32_SOC_CAST resp, resp_len, 0,
+				 &sup->sa, &su_len);
+	else
+		return recvfrom(ctxt->soc, WIN32_SOC_CAST resp, resp_len, 0,
+				&sup->sa, &su_len);
+}
+
+
+
+static void
+clear_error(DCC_CLNT_CTXT *ctxt, const char *which)
+{
+	int err;
+	DCC_SOCKLEN_T errlen;
+
+	errlen = sizeof(err);
+	if (0 > getsockopt(ctxt->soc, SOL_SOCKET, SO_ERROR,
+			   WIN32_SOC_CAST &err, &errlen)) {
+		dcc_trace_msg("getsockopt(SO_ERROR): %s", ERROR_STR());
+	} else if (dcc_clnt_debug > 3 && err) {
+		dcc_trace_msg("%s SO_ERROR: %s", which, ERROR_STR1(err));
+	}
+}
+
+
+
+/* clear the socket buffer */
+static u_char
+dcc_clnt_soc_flush(DCC_CLNT_CTXT *ctxt)
+{
+	DCC_OP_RESP pkt;
+	DCC_SOCKU su;
+	char sbuf[DCC_SU2STR_SIZE];
+	char rbuf[30];
+	char ob[DCC_OPBUF];
+	int pkt_len, pkt_num;
+
+	for (pkt_num = 1; pkt_num <= 50; ++pkt_num) {
+		pkt_len = do_recv(ctxt, &pkt, sizeof(pkt), &su);
+		if (0 <= pkt_len) {
+			if (dcc_clnt_debug == 0 && pkt_num < 10)
+				continue;
+			dcc_su2str(sbuf, sizeof(sbuf), &su);
+			if (pkt_num > 1)
+				snprintf(rbuf, sizeof(rbuf), " #%d", pkt_num);
+			else
+				rbuf[0] = '\0';
+			if (pkt_len < ISZ(DCC_HDR)+ISZ(DCC_SIGNATURE)
+			    || pkt_len != ntohs(pkt.hdr.len)
+			    || pkt.hdr.pkt_vers < DCC_PKT_VERSION_MIN
+			    || pkt.hdr.pkt_vers > DCC_PKT_VERSION_MAX) {
+				trace_bad_packet(0, &su, &pkt, pkt_len,
+						 "flush%s %d stray bytes from"
+						 " %s",
+						 rbuf, pkt_len, sbuf);
+			} else {
+				dcc_trace_msg("flush%s %s from %s"
+					      " ID=%d h=%#x p=%#x r=%#x t=%#x",
+					      rbuf,
+					      dcc_hdr_op2str(ob, sizeof(ob),
+							&pkt.hdr),
+					      sbuf,
+					      ntohl(pkt.hdr.sender),
+					      pkt.hdr.op_nums.h,
+					      pkt.hdr.op_nums.p,
+					      pkt.hdr.op_nums.r,
+					      pkt.hdr.op_nums.t);
+
+			}
+			continue;
+		}
+		if (DCC_BLOCK_ERROR())
+			return 1;
+		if (UNREACHABLE_ERRORS()) {
+			if (dcc_clnt_debug > 1 || pkt_num > 10)
+				dcc_trace_msg("ignore flushed error: %s",
+					      ERROR_STR());
+			continue;
+		}
+		dcc_trace_msg("flush recvfrom(%s): %s",
+			      su.sa.sa_family
+			      ? dcc_su2str(sbuf, sizeof(sbuf), &su) : "",
+			      ERROR_STR());
+		return 0;
+	}
+
+	dcc_trace_msg("too many flushed packets or errors");
+	return 0;
+}
+
+
+
+/* connect() to the server
+ *	The contexts and shared information must be locked on entry
+ *	They are locked on exit */
+u_char
+dcc_clnt_connect(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt,
+		 const DCC_SOCKU *su)	/* 0=disconnect */
+{
+	u_char was_connected;
+
+	assert_info_locked();
+
+	/* disconnect if asked
+	 *	In theory you can use connect() with a "null address."
+	 *	In practice on some systems there is more than one or even
+	 *	no notion of an effective "null" address. */
+	if (!su) {
+		if (ctxt->conn_su.sa.sa_family == AF_UNSPEC) {
+#ifdef linux
+			/* some flavors of Linux say "Connection refused" on
+			 * sendto() on a not-connected socket when a previous
+			 * use of sendto() hit a closed port, particularly
+			 * via loopback */
+			clear_error(ctxt, "Linux dcc_clnt_connect(0)");
+#endif
+			return 1;
+		}
+		return dcc_clnt_soc_reopen(emsg, ctxt);
+	}
+
+	/* already properly connected */
+	if (!memcmp(&ctxt->conn_su, su, sizeof(ctxt->conn_su)))
+		return 1;
+
+	was_connected = (ctxt->conn_su.sa.sa_family != AF_UNSPEC);
+
+	/* At least some versions of Linux do not allow	connsecutive valid
+	 *	calls to connect().  So for Linux, always close and reopen
+	 *	the socket.
+	 * At least some versions of FreeBSD unbind a socket while
+	 *	reconnecting it.  So if the socket was bound to	local address
+	 *	or if it is time to try to bind it again, close and reopen it.
+	 */
+	if ((was_connected
+#ifndef linux
+	     && (dcc_clnt_info->src.family != AF_UNSPEC
+		 && (!(ctxt->flags & DCC_CTXT_SRCBAD)
+		     || DCC_IS_TIME(ctxt->start.tv_sec, ctxt->bind_time,
+				    DCC_CTXT_REBIND_SECS)))
+#endif /* linux */
+	     ) || !dcc_clnt_soc_flush(ctxt)) {
+		if (!dcc_clnt_soc_reopen(emsg, ctxt))
+			return 0;
+	}
+
+	if (SOCKET_ERROR == connect(ctxt->soc, &su->sa, DCC_SU_LEN(su))) {
+		char sustr[DCC_SU2STR_SIZE];
+
+		dcc_pemsg(EX_IOERR, emsg, "connect(%s): %s",
+			  dcc_su2str(sustr, sizeof(sustr), su),
+			  ERROR_STR());
+		dcc_clnt_soc_close(ctxt);
+		return 0;
+	}
+	ctxt->conn_su = *su;
+
+	/* clear ICMP Unreachable errors from previous connections */
+	if (was_connected)
+		clear_error(ctxt, "dcc_clnt_connect");
+
+	return 1;
+}
+
+
+
+/* send a single DCC message
+ * the contexts and the shared information must be locked on entry
+ * nothing is unlocked */
+static int				/* 0=failed this target, -1=all stop */
+clnt_xmit(DCC_CLNT_CTXT *ctxt,
+	  DCC_SRVR_CLASS *class, const DCC_SRVR_ADDR *ap,
+	  DCC_HDR *msg, int msg_len, u_char connect_ok)
+{
+	DCC_XLOG_ENTRY *xloge;
+#	define FSTR " from "
+	char tgt_abuf[80], src_abuf[LITZ(FSTR)+INET6_ADDRSTRLEN+1];
+	char ob[DCC_OPBUF];
+	DCC_XLOG_ENTRY *xloge1;
+	int i, result;
+
+	msg->len = htons(msg_len);
+
+	xloge = ctxt->xlog.next;
+	if (xloge > ctxt->xlog.last)
+		dcc_logbad(EX_SOFTWARE, "xloge > ctxt->xlog.last");
+	++msg->op_nums.t;
+	xloge->op_nums = msg->op_nums;
+	xloge->addr_inx = ap - class->addrs;
+	dcc_mk_su(&xloge->su, ap->ip.family, &ap->ip.u, ap->ip.port);
+	xloge->addrs_gen = class->gen;
+	xloge->sent_us = ctxt->now_us;
+	xloge->op = msg->op;
+	if (!GOOD_NAM(ap->nam_inx))
+		dcc_logbad(EX_SOFTWARE, "clnt_xmit: bad nam_inx");
+	xloge->id = class->nms[ap->nam_inx].clnt_id;
+	msg->sender = htonl(xloge->id);
+	if (xloge->id == DCC_ID_ANON) {
+		xloge->passwd[0] = '\0';
+		memset((char *)msg + (msg_len-sizeof(DCC_SIGNATURE)), 0,
+		       sizeof(DCC_SIGNATURE));
+	} else {
+		if (xloge->id == 0) {
+			if (dcc_clnt_debug)
+				dcc_logbad(EX_SOFTWARE, "zero client-ID for %s",
+					   class->nms[ap->nam_inx].hostname);
+			else
+				dcc_trace_msg("zero client-ID for %s",
+					      class->nms[ap->nam_inx].hostname);
+			class->nms[ap->nam_inx].clnt_id = DCC_ID_ANON;
+		} else if (class->nms[ap->nam_inx].passwd[0] == '\0') {
+			if (dcc_clnt_debug)
+				dcc_logbad(EX_SOFTWARE, "null password for %s",
+					   class->nms[ap->nam_inx].hostname);
+			else
+				dcc_trace_msg("null password for %s",
+					      class->nms[ap->nam_inx].hostname);
+			class->nms[ap->nam_inx].clnt_id = DCC_ID_ANON;
+		}
+		strncpy(xloge->passwd, class->nms[ap->nam_inx].passwd,
+			sizeof(xloge->passwd));
+		dcc_sign(xloge->passwd, sizeof(xloge->passwd), msg, msg_len);
+	}
+
+	/* Use connect() when possible to get ICMP Unreachable messages.
+	 * It is impossible when talking to more than one server. */
+	for (xloge1 = ctxt->xlog.base; connect_ok && xloge1 < xloge; ++xloge1) {
+		if (xloge1->op_nums.t == DCC_OP_NUMS_NULL)
+			continue;
+		if (xloge->addr_inx != xloge1->addr_inx) {
+			connect_ok = 0;
+			break;
+		}
+	}
+	if (!dcc_clnt_connect(0, ctxt, connect_ok ? &xloge->su : 0))
+		return -1;
+
+	if (ctxt->conn_su.sa.sa_family != AF_UNSPEC) {
+		if (dcc_clnt_info->flags & DCC_INFO_FG_SOCKS)
+			i = Rsend(ctxt->soc, WIN32_SOC_CAST msg, msg_len, 0);
+		else
+			i = send(ctxt->soc, WIN32_SOC_CAST msg, msg_len, 0);
+
+	} else {
+		if (dcc_clnt_info->flags & DCC_INFO_FG_SOCKS)
+			i = Rsendto(ctxt->soc, WIN32_SOC_CAST msg, msg_len, 0,
+				    &xloge->su.sa, DCC_SU_LEN(&xloge->su));
+		else
+			i = sendto(ctxt->soc, WIN32_SOC_CAST msg, msg_len, 0,
+				   &xloge->su.sa, DCC_SU_LEN(&xloge->su));
+	}
+	++ctxt->xlog.cur[ap - class->addrs].xmits;
+	if (i == msg_len) {
+		if (dcc_clnt_debug > 3)
+			dcc_trace_msg("%8.6f sent %s t=%#x to %s",
+				      get_age(ctxt),
+				      dcc_hdr_op2str(ob, sizeof(ob), msg),
+				      xloge->op_nums.t,
+				      addr2str(tgt_abuf, sizeof(tgt_abuf),
+					       class, class->gen, ap, 0));
+		++ctxt->xlog.next;
+		++ctxt->xlog.outstanding;
+		return 1;
+	}
+
+	/* stop output only for this target after ICMP Unreachable
+	 * otherwise stop all output */
+	if (i >= 0) {
+		result = -1;		/* bad length is fatal */
+	} else {
+		result = UNREACHABLE_ERRORS() ? 0 : -1;
+	}
+
+	if (result < 0 || dcc_clnt_debug) {
+		if (ctxt->bind_su.sa.sa_family == AF_UNSPEC) {
+			src_abuf[0] = '\0';
+		} else {
+			memcpy(src_abuf, FSTR, LITZ(FSTR));
+			dcc_su2str(&src_abuf[LITZ(FSTR)],
+				   sizeof(src_abuf)-LITZ(FSTR),
+				   &ctxt->bind_su);
+		}
+		if (i < 0) {
+			dcc_trace_msg("%s(%s)%s: %s",
+				      connect_ok ? "send" : "sendto",
+				      addr2str(tgt_abuf, sizeof(tgt_abuf),
+					       class,
+					       class->gen, ap, 0),
+				      src_abuf, ERROR_STR());
+		} else {
+			dcc_trace_msg("%s(%s%s)=%d instead of %d",
+				      connect_ok ? "send" : "sendto",
+				      addr2str(tgt_abuf, sizeof(tgt_abuf),
+					       class,
+					       class->gen, ap, 0),
+				      src_abuf, i, msg_len);
+		}
+	}
+	return result;
+
+#undef FSTR
+}
+
+
+
+static void
+update_rtt(DCC_CLNT_CTXT *ctxt, DCC_SRVR_CLASS *class, DCC_XLOG_ENTRY *xloge,
+	   int us)
+{
+	DCC_SRVR_ADDR *ap;
+
+	/* compute new RTT only if the map data structure is locked,
+	 * the clock did not jump,
+	 * and we're talking about the same hosts */
+	if (!info_locked
+	    || xloge->addrs_gen != class->gen)
+		return;
+
+	ap = &class->addrs[xloge->addr_inx];
+
+	if (us < 0)
+		us = 0;
+	if (us > DCC_RTT_BAD)
+		us = DCC_RTT_BAD;
+
+	if (ap->rtt == DCC_RTT_BAD) {
+		/* just set the RTT if this is a newly working server */
+		ap->rtt = us;
+		ap->total_xmits = 0;
+		ap->total_resps = 0;
+		ap->resp_mem = 0;
+		ap->rtt_updated = 0;
+
+	} else if (ctxt->now.tv_sec < ap->rtt_updated + FAST_RTT_SECS) {
+		/* adjust the RTT quickly if this is the first
+		 * measurement in a long time */
+		AGE_AVG(ap->rtt, us, 2, 1);
+		ap->rtt_updated = ctxt->now.tv_sec;
+
+	} else {
+		AGE_AVG(ap->rtt, us, 9, 1);
+		ap->rtt_updated = ctxt->now.tv_sec;
+	}
+
+	if (ap->rtt > DCC_MAX_RTT)
+		ap->rtt = DCC_MAX_RTT;
+}
+
+
+
+/* Update response rate and penalize the RTT of servers that failed to respond.
+ *	the data must be locked */
+static void
+resp_rates(DCC_CLNT_CTXT *ctxt, DCC_SRVR_CLASS *class,
+	   u_char measuring)
+{
+	DCC_SRVR_ADDR *ap;
+	DCC_XLOG_ENTRY *xloge;
+	const DCC_XLOG_ENTRY *xloge2;
+	int us, us2;
+	u_char seen;
+	int i;
+
+	for (xloge = ctxt->xlog.base; xloge < ctxt->xlog.next; ++xloge) {
+		/* ignore responses we've already handled */
+		if (xloge->op_nums.t == DCC_OP_NUMS_NULL)
+			continue;
+
+		ap = &class->addrs[xloge->addr_inx];
+
+		/* Update the RTT of this server as if we would have received
+		 * ia response if we had waited a little longer, unless we
+		 * would be assuming a faster RTT than its current average.
+		 *
+		 * Use the longest of the time spent waiting for this request
+		 * and the delays of any requests that were answered by the
+		 * server. */
+		us = ctxt->now_us - xloge->sent_us;
+		seen = 0;
+		for (xloge2=ctxt->xlog.base; xloge2<ctxt->xlog.next; ++xloge2) {
+			if (xloge2->addr_inx != xloge->addr_inx
+			    || xloge2 == xloge)
+				continue;
+			if (xloge2->op_nums.t != DCC_OP_NUMS_NULL) {
+				seen = 1;
+				continue;
+			}
+			us2 = ctxt->now_us - xloge2->sent_us;
+			if (us < us2)
+				us = us2;
+		}
+		/* update the RTT
+		 * if we waited at least as long as the current RTT
+		 * or we received at least one response */
+		if (ctxt->now_us >= ap->rtt && seen)
+			update_rtt(ctxt, class, xloge, us + DCC_DCCD_DELAY);
+
+		/* having received its answer, forget this transmission */
+		xloge->op_nums.t = DCC_OP_NUMS_NULL;
+	}
+
+	/* maintain the response rate */
+	for (i = 0, ap = class->addrs; i < DIM(ctxt->xlog.cur); ++i, ++ap) {
+		if (ap->rtt == DCC_RTT_BAD
+		    || ctxt->xlog.cur[i].xmits == 0)
+			continue;
+		if (measuring) {
+			if (ctxt->xlog.cur[i].resps != 0) {
+				++ctxt->xlog.working_addrs;
+			} else if (!(ap->resp_mem & ((1<<DCC_MAX_XMITS)-1))) {
+				/* this server is bad if there were no answers
+				 * at all for this mesurement cycle */
+				ap->rtt = DCC_RTT_BAD;
+				continue;
+			}
+		}
+		ap->total_xmits += ctxt->xlog.cur[i].xmits;
+		if (ap->total_xmits > DCC_TOTAL_XMITS_MAX)
+			ap->total_xmits = DCC_TOTAL_XMITS_MAX;
+		do {
+			ap->total_resps -= (ap->resp_mem
+					    >> (DCC_TOTAL_XMITS_MAX-1));
+			ap->resp_mem <<= 1;
+			if (ctxt->xlog.cur[i].resps != 0) {
+				ap->resp_mem |= 1;
+				++ap->total_resps;
+				--ctxt->xlog.cur[i].resps;
+			}
+		} while (--ctxt->xlog.cur[i].xmits != 0);
+	}
+}
+
+
+
+/* receive a single DCC response
+ *      The contexts must be locked.
+ *      The mapped or common info ought to be locked, but reception
+ *      works if it is not. */
+static int      /* -1=fatal error, 0=no data, 1=unreachable, 2=ok */
+clnt_recv(DCC_CLNT_CTXT *ctxt, DCC_SRVR_CLASS *class,
+	  DCC_OP_RESP *resp,		/* the response */
+	  int resp_len,
+	  const DCC_HDR *msg,		/* the original request */
+	  DCC_XLOG_ENTRY **xlogep)
+{
+	DCC_SOCKU su;
+	DCC_XLOG_ENTRY *xloge, *xloge1;
+	DCC_SRVR_ADDR *ap;
+	char str[DCC_SU2STR_SIZE+50];
+	char ob[DCC_OPBUF];
+	char ob2[DCC_OPBUF];
+	int pkt_len;
+
+	*xlogep = 0;
+	for (;;) {
+next_pkt:;
+		pkt_len = do_recv(ctxt, resp, resp_len, &su);
+		if (pkt_len < 0) {
+			/* Stop looking when there are no more packets */
+			if (DCC_BLOCK_ERROR())
+				return 0;
+
+			/* ignore ICMP Unreachables unless we have connected
+			 * to a server.
+			 * If so, forget all outstanding requests */
+			if (ctxt->conn_su.sa.sa_family != AF_UNSPEC
+			    && UNREACHABLE_ERRORS()) {
+				/* find one relevant request
+				 * and mark all of them finished */
+				for (xloge1 = ctxt->xlog.base, xloge = 0;
+				     xloge1 < ctxt->xlog.next;
+				     ++xloge1) {
+					if (xloge1->op_nums.t==DCC_OP_NUMS_NULL)
+					    continue;
+					xloge = xloge1;
+					xloge->op_nums.t = DCC_OP_NUMS_NULL;
+				}
+				if (!xloge) {
+					if (dcc_clnt_debug)
+					    dcc_trace_msg("ignore unmatched:"
+							" %s", ERROR_STR());
+					continue;
+				}
+				if (dcc_clnt_debug)
+					dcc_trace_msg("note recvfrom(%s): %s",
+						      dcc_su2str(str,
+							  sizeof(str),
+							  &ctxt->conn_su),
+						      ERROR_STR());
+				ctxt->xlog.outstanding = 0;
+				ap = &class->addrs[xloge->addr_inx];
+				ap->rtt = DCC_RTT_BAD;
+				++ctxt->xlog.cur[xloge->addr_inx].resps;
+				*xlogep = xloge;
+				return 1;
+			}
+			dcc_trace_msg( "clnt_recv recvfrom(%s): %s",
+				      su.sa.sa_family
+				      ? dcc_su2str(str, sizeof(str), &su) : "",
+				      ERROR_STR());
+			return -1;
+		}
+
+		if (pkt_len > resp_len) {
+			trace_bad_packet(&ctxt->xlog, &su, resp, pkt_len,
+					 "recv(%s)=%d>%d",
+					 dcc_su2str(str, sizeof(str), &su),
+					 pkt_len, resp_len);
+			continue;
+		}
+		if (pkt_len < ISZ(DCC_HDR)+ISZ(DCC_SIGNATURE)) {
+			trace_bad_packet(&ctxt->xlog, &su, resp, pkt_len,
+					 "recv(%s)=%d<%d",
+					 dcc_su2str(str, sizeof(str), &su),
+					 pkt_len,
+					 ISZ(DCC_HDR)+ISZ(DCC_SIGNATURE));
+			continue;
+		}
+		if (pkt_len != ntohs(resp->hdr.len)) {
+			trace_bad_packet(&ctxt->xlog, &su, resp, pkt_len,
+					 "recv(%s)=%d but hdr len=%d",
+					 dcc_su2str(str, sizeof(str), &su),
+					 pkt_len,
+					 ntohs(resp->hdr.len));
+			continue;
+		}
+
+		if (resp->hdr.pkt_vers < DCC_PKT_VERSION_MIN
+		    || resp->hdr.pkt_vers > DCC_PKT_VERSION_MAX) {
+			trace_bad_packet(&ctxt->xlog, &su, resp, pkt_len,
+					 "unrecognized version #%d from %s",
+					 resp->hdr.pkt_vers,
+					 dcc_su2str(str, sizeof(str), &su));
+			continue;
+		}
+
+		/* We cannot use the server's apparent IP address because it
+		 * might be multi-homed and respond with an address other than
+		 * the address to which we sent.  So use our records of
+		 * which OP_NUMS was sent to which server address. */
+		if (resp->hdr.op_nums.r != msg->op_nums.r
+		    || resp->hdr.op_nums.p != msg->op_nums.p
+		    || resp->hdr.op_nums.h != msg->op_nums.h) {
+			if (dcc_clnt_debug)
+				dcc_trace_msg("unmatched response from %s"
+					      " ID=%d h=%#x/%#x p=%#x/%#x"
+					      " r=%#x/%#x t=%#x",
+					      dcc_su2str(str, sizeof(str), &su),
+					      ntohl(resp->hdr.sender),
+					      resp->hdr.op_nums.h,
+					      msg->op_nums.h,
+					      resp->hdr.op_nums.p,
+					      msg->op_nums.p,
+					      resp->hdr.op_nums.r,
+					      msg->op_nums.r,
+					      resp->hdr.op_nums.t);
+			continue;
+		}
+
+		/* everything matches except perhaps the transmission # */
+		xloge = ctxt->xlog.base;
+		for (;;) {
+			if (xloge >= ctxt->xlog.next) {
+				if (dcc_clnt_debug)
+					dcc_trace_msg("stray response from %s"
+						      " ID=%d h=%#x p=%#x"
+						      " r=%#x t=%#x/%#x",
+						      dcc_su2str(str,
+							  sizeof(str), &su),
+						      ntohl(resp->hdr.sender),
+						      resp->hdr.op_nums.h,
+						      resp->hdr.op_nums.p,
+						      resp->hdr.op_nums.r,
+						      msg->op_nums.r,
+						      resp->hdr.op_nums.t);
+				goto next_pkt;
+			}
+			if (resp->hdr.op_nums.t == xloge->op_nums.t)
+				break;
+			++xloge;
+		}
+
+		ap = &class->addrs[xloge->addr_inx];
+
+#ifdef CLNT_LOSSES
+		if ((++clnt_losses % 5) == 0) {
+			dcc_trace_msg("dropped answer from %s",
+				      addr2str(str, sizeof(str), class,
+					       xloge->addrs_gen, ap, &su));
+			continue;
+		}
+#endif
+
+		if (xloge->passwd[0] != '\0'
+		    && !dcc_ck_signature(xloge->passwd, sizeof(xloge->passwd),
+					 resp, pkt_len)) {
+			dcc_error_msg("%s ID=%d rejected our password for ID %d"
+				      " and %s with %s"
+				      "    h=%#x p=%#x r=%#x t=%#x",
+				      addr2str(str, sizeof(str),
+					       class, xloge->addrs_gen,
+					       ap, &su),
+				      ntohl(resp->hdr.sender),
+				      xloge->id,
+				      dcc_hdr_op2str(ob, sizeof(ob),
+						     msg),
+				      dcc_hdr_op2str(ob2, sizeof(ob2),
+						     &resp->hdr),
+				      resp->hdr.op_nums.h,
+				      resp->hdr.op_nums.p,
+				      resp->hdr.op_nums.r,
+				      resp->hdr.op_nums.t);
+			continue;
+		}
+
+		if (dcc_clnt_debug > 3)
+			dcc_trace_msg("%8.6f received response from %s ID=%d"
+				      "    h=%#x p=%#x r=%#x t=%#x",
+				      get_age(ctxt),
+				      dcc_su2str(str, sizeof(str), &su),
+				      ntohl(resp->hdr.sender),
+				      resp->hdr.op_nums.h,
+				      resp->hdr.op_nums.p,
+				      resp->hdr.op_nums.r,
+				      resp->hdr.op_nums.t);
+
+		/* don't find the record of this transmission again */
+		xloge->op_nums.t = DCC_OP_NUMS_NULL;
+		if (ctxt->xlog.outstanding != 0)
+			--ctxt->xlog.outstanding;
+		++ctxt->xlog.cur[xloge->addr_inx].resps;
+		*xlogep = xloge;
+
+		/* Notice if multi-homing is involved
+		 * That is true if the address from which the client answered
+		 * differs from the address to which we sent */
+		if (!(ap->flags & DCC_SRVR_ADDR_MHOME)
+		    && dcc_cmp_ap2su(ap, &su)) {
+			if (dcc_clnt_debug)
+				dcc_trace_msg("%s multi-homed at %s",
+					      addr2str(str, sizeof(str),
+						       class, xloge->addrs_gen,
+						       ap, 0),
+					      dcc_su2str(str,sizeof(str), &su));
+			ap->flags |= DCC_SRVR_ADDR_MHOME;
+		}
+
+		return 2;
+	}
+}
+
+
+
+/* wait for an answer */
+int					/* -1=error, 0=timeout, 1=ready */
+dcc_select_poll(DCC_EMSG emsg,
+		SOCKET fd,
+		u_char rd,		/* 1=read 0=write */
+		int us)			/* <0=forever until signal */
+{
+#ifdef USE_POLL
+	struct pollfd fds;
+	int nfds;
+	int delay;
+
+	if (us < 0)
+		delay = -1;
+	else
+		delay = (us+999)/1000;
+
+	for (;;) {
+		fds.fd = fd;
+		/* At least some versions of Linux have POLLRDNORM etc. in
+		 * asm/poll.h, but with definitions of POLLIN, POLLPRI, etc.
+		 * that conflict with their definitions in sys/poll.h.
+		 * Perhaps it is not necessary to check for high or
+		 * low priority data, but the poll() documentation on
+		 * some systems says that asking about POLLIN does not
+		 * say anything about other data */
+#ifdef POLLRDNORM
+		if (rd)
+			fds.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;
+		else
+			fds.events = POLLOUT| POLLWRNORM | POLLWRBAND | POLLPRI;
+#else
+		if (rd)
+			fds.events = POLLIN;
+		else
+			fds.events = POLLOUT;
+#endif
+		fds.revents = 0;
+		nfds = poll(&fds, 1, delay);
+		if (nfds >= 0)
+			return nfds;
+		if (!DCC_SELECT_NERROR()) {
+			dcc_pemsg(EX_OSERR, emsg, "poll(): %s", ERROR_STR());
+			return -1;
+		}
+		if (us < 0)		/* stop forever on a signal */
+			return 0;
+	}
+#else
+	struct timeval delay, *delayp;
+	fd_set fds;
+	int nfds;
+
+	if (us < 0) {
+		delayp = 0;
+	} else {
+		us2tv(&delay, us);
+		delayp = &delay;
+	}
+
+	FD_ZERO(&fds);
+	for (;;) {
+		FD_SET(fd, &fds);
+		if (rd)
+			nfds = select(fd+1, &fds, 0, 0, delayp);
+		else
+			nfds = select(fd+1, 0, &fds, 0, delayp);
+		if (nfds >= 0)
+			return nfds;
+		if (!DCC_SELECT_NERROR()) {
+			dcc_pemsg(EX_OSERR, emsg, "select(): %s", ERROR_STR());
+			return -1;
+		}
+		if (us < 0)		/* stop forever on a signal */
+			return 0;
+	}
+#endif
+}
+
+
+
+/* Make initial estimates of the RTT to all known servers
+ *      The RTT's help the client pick a server that will respond quickly and
+ *      reliably and to know when to retransmit a request that is lost due
+ *      to network congestion or bit rot.
+ * Both locks must be held on entry.
+ * Both are released while working.
+ * Both locks are held on success.
+ * Only the contexts are locked on failure. */
+static u_char				/* 0=failed, 1=at least 1 good server */
+measure_rtt(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt,
+	    DCC_SRVR_CLASS *class,
+	    DCC_CLNT_FGS clnt_fgs)	/* DCC_CLNT_FG_* */
+{
+	DCC_SRVR_ADDR *ap;
+	DCC_NOP nop;
+	DCC_OP_RESP resp;
+	int delay_us, next_xmit;
+	int nfds, xmit_num;
+	int addrs_gen;
+	int tgt_addrs;
+	DCC_XLOG_ENTRY *xloge;
+	char ob[DCC_OPBUF], abuf[80];
+	u_char vers;
+	u_char connect_ok;
+	int tgts, i;
+
+	assert_info_locked();
+
+	/* Send NOP's to all addresses and wait for responses to
+	 * measure each server's health and RTT.
+	 * Treat all addresses as if they are of independent hosts */
+
+	if (class->nms[0].hostname[0] == '\0') {
+		class->srvr_inx = NO_SRVR;
+		dcc_pemsg(EX_NOHOST, emsg, "no %s server names",
+			  DCC_IS_GREY_STR(class));
+		dcc_info_unlock(0);
+		return 0;
+	}
+
+	memcpy(&nop.hdr, &dcc_clnt_info->proto_hdr, sizeof(nop.hdr));
+	/* servers ignore the version on NOPs except to guess the version
+	 * we will accept */
+	nop.hdr.pkt_vers = DCC_PKT_VERSION;
+	nop.hdr.op_nums.p = getpid();
+	nop.hdr.op = DCC_OP_NOP;
+	/* Do not change the transaction ID so that dbclean can kludge it.
+	 * Dccd does not care about the transaction ID on NOPs. */
+
+	if (!get_now(emsg, ctxt)) {
+		dcc_info_unlock(0);
+		return 0;
+	}
+
+	/* discourage competition from other processes and threads */
+	class->measure = ctxt->now.tv_sec+FAST_RTT_SECS;
+
+	flush_emsg(emsg, 1);
+
+	addrs_gen = class->gen;
+
+	/* stop waiting for responses when we have enough working servers */
+	tgt_addrs = class->num_srvrs;
+	if (!dcc_all_srvrs && tgt_addrs > 4)
+		tgt_addrs = 4;
+
+	memset(&ctxt->xlog, 0, sizeof(ctxt->xlog));
+	ctxt->xlog.base = ctxt->xlog.next = ctxt->xlog_entries;
+	ctxt->xlog.last = LAST(ctxt->xlog_entries);
+	delay_us = 0;
+	next_xmit = 0;
+	xmit_num = 0;
+	/* wait for the responses to the NOPs and retransmit as needed */
+	for (;;) {
+		/* wait quietly until time to retransmit */
+		if (delay_us <= 0) {
+			if (xmit_num >= DCC_MAX_XMITS)
+				break;
+			if (ctxt->xlog.working_addrs >= tgt_addrs) {
+				/* do not retransmit if we have heard from
+				 *	enough servers
+				 * quit if we have waited at least one RTT */
+				if (xmit_num > 0)
+					break;
+				delay_us = 0;
+				next_xmit = ctxt->now_us;
+
+			} else {
+				/* get delay & time of next transmission */
+				delay_us = retrans_time((clnt_fgs
+							& DCC_CLNT_FG_SLOW)
+							? DCC_MAX_RTT
+							: DCC_MIN_RTT,
+							xmit_num++);
+				next_xmit = delay_us + ctxt->now_us;
+
+				connect_ok = 1;
+				tgts = 0;
+				for (i = 0, ap = class->addrs;
+				     ap <= LAST(class->addrs);
+				     ++i, ++ap) {
+					if (ap->ip.family == 0
+					    || ctxt->xlog.cur[i].resps != 0)
+					    continue;
+					if (ap->flags & DCC_SRVR_ADDR_MHOME)
+					    connect_ok = 0;
+					++tgts;
+				}
+				/* Use a connected socket early to get
+				 *	ICMP error messages from single server.
+				 * no connection later to detect multi-homing
+				 *	that makes a server appear deaf */
+				if (tgts > 1
+				    || xmit_num > DCC_MAX_XMITS/2)
+					connect_ok = 0;
+				for (i = 0, ap = class->addrs;
+				     tgts > 0 && ap <= LAST(class->addrs);
+				     ++i, ++ap) {
+					if (ap->ip.family == 0
+					    || !GOOD_NAM(ap->nam_inx)
+					    || ctxt->xlog.cur[i].resps != 0)
+					    continue;
+					--tgts;
+					if (0 > clnt_xmit(ctxt, class, ap,
+							&nop.hdr, sizeof(nop),
+							connect_ok))
+					    break;
+				}
+			}
+
+			/* stop if nothing to wait for */
+			if (!ctxt->xlog.outstanding)
+				break;
+		}
+
+		if (!dcc_info_unlock(emsg))
+			return 0;
+		dcc_ctxts_unlock();
+		nfds = dcc_select_poll(emsg, ctxt->soc, 1, delay_us);
+		dcc_ctxts_lock();
+		if (nfds < 0)
+			return 0;
+		if (!dcc_info_lock(emsg))
+			return 0;
+
+		i = get_now(emsg, ctxt);
+		if (!i) {		/* give up if the clock jumped */
+			class->measure = 0;
+			dcc_info_unlock(0);
+			return 0;
+		}
+		if (addrs_gen != class->gen) {
+			extra_pemsg(EX_IOERR, emsg,
+				    "competition stopped RTT measurement");
+			/* if we have at least one address,
+			 * hope the other process will finish the job */
+			if (HAVE_SRVR(class)
+			    || pick_srvr(emsg, class))
+				return 1;
+
+			/* fail, but hope the other process will finish */
+			dcc_info_unlock(0);
+			return 0;
+		}
+
+		if (nfds > 0) {
+			for (;;) {
+				i = clnt_recv(ctxt, class,
+					      &resp, sizeof(resp),
+					      &nop.hdr, &xloge);
+				if (i <= 0)
+					break;
+
+				if (i == 1) /* otherwise ignore Unreachable */
+					continue;
+
+				/* record the results of a probe, and notice
+				 * if the server is the best so far */
+				ap = &class->addrs[xloge->addr_inx];
+
+				if (resp.hdr.op != DCC_OP_OK) {
+					if (dcc_clnt_debug)
+					    dcc_trace_msg("RTT NOP answered"
+							" with %s by %s",
+							dcc_hdr_op2str(ob,
+							    sizeof(ob),
+							    &resp.hdr),
+							addr2str(abuf,
+							    sizeof(abuf),
+							    class,
+							    xloge->addrs_gen,
+							    ap, 0));
+					ap->rtt = DCC_RTT_BAD;
+					continue;
+				}
+
+				vers = resp.ok.max_pkt_vers;
+				if (vers >= DCC_PKT_VERSION_MAX)
+					vers = DCC_PKT_VERSION_MAX;
+				else if (vers < DCC_PKT_VERSION_MIN)
+					vers = DCC_PKT_VERSION_MIN;
+				ap->srvr_pkt_vers = vers;
+				ap->srvr_id = ntohl(resp.hdr.sender);
+				memcpy(ap->brand, resp.ok.brand,
+				       sizeof(ap->brand));
+				ap->srvr_wait = ntohs(resp.ok.qdelay_ms)*1000;
+
+				update_rtt(ctxt, class, xloge,
+					   ctxt->now_us - xloge->sent_us
+					   + ap->srvr_wait);
+			}
+		}
+
+		if (ctxt->xlog.outstanding == 0
+		    || (ctxt->xlog.working_addrs >= tgt_addrs
+			&& xmit_num > 1))
+			next_xmit = ctxt->now_us;
+		delay_us = next_xmit - ctxt->now_us;
+	}
+	/* the contexts and the shared information are locked */
+	resp_rates(ctxt, class, 1);
+
+	if (!pick_srvr(emsg, class)) {
+		fail_more(ctxt, class);
+		dcc_info_unlock(0);
+		return 0;
+	}
+
+	/* maintain long term average that is used to switch back to
+	 * a good server that temporarily goes bad */
+	if (class->thold_rtt == DCC_RTT_BAD) {
+		/* There is no point in trying to change servers
+		 * Maybe we have only 1 */
+		class->avg_thold_rtt = DCC_RTT_BAD;
+	} else if (class->avg_thold_rtt == -DCC_RTT_BAD) {
+		/* We are being forced to consider changing servers.
+		 * The threshold for changing will be based on the RTT
+		 * for the new server */
+		class->avg_thold_rtt = class->base_rtt;
+	} else {
+		AGE_AVG(class->avg_thold_rtt, class->base_rtt, 9, 1);
+	}
+
+	class->measure = ctxt->now.tv_sec+FAST_RTT_SECS;
+
+	/* Several systems do not update the mtime of a file modified with
+	 * mmap().  Some like BSD/OS delay changing the mtime until the file
+	 * accessed with read().  Others including filesystems on some
+	 * versions of Linux apparently never change the mtime.
+	 * Do not bother temporary map files that have already been unlinked
+	 * to avoid problems on systems that do not have futimes() */
+	if (!(dcc_clnt_info->flags & DCC_INFO_FG_TMP))
+		dcc_set_mtime(emsg, dcc_info_nm, info_fd, 0);
+	flush_emsg(emsg, 1);
+
+	return 1;
+}
+
+
+
+/* Get and write-lock common info
+ *      The contexts but not the info must be locked.
+ *      The contexts remain locked on failure.  The shared information
+ *	    is locked only on success. */
+u_char					/* 0=failed 1=ok */
+dcc_clnt_rdy(DCC_EMSG emsg,		/* cleared of stale messages */
+	     DCC_CLNT_CTXT *ctxt,
+	     DCC_CLNT_FGS clnt_fgs)	/* DCC_CLNT_FG_* */
+{
+	DCC_SRVR_CLASS *class;
+	DCC_IP bind_ip, zero_ip, *src_ip;
+
+	if (!dcc_info_lock(emsg))
+		return 0;
+
+	if (!(clnt_fgs & DCC_CLNT_FG_RETRY))
+		get_start_time(ctxt);
+
+	class = DCC_GREY2CLASS(clnt_fgs & DCC_CLNT_FG_GREY);
+
+	/* just fail if things were broken and it's too soon to try again */
+	if (!(clnt_fgs & DCC_CLNT_FG_NO_FAIL)
+	    && !ck_fail_time(emsg, ctxt, class)) {
+		dcc_info_unlock(0);
+		return 0;
+	}
+
+	/* Re-open the socket if it is closed,
+	 * or we have switched between IPv4 and IPv6,
+	 * or if the local address has changed
+	 * or if the local address was broken a long time ago.
+	 * Do not compare the source port numbers, because even when there
+	 * is no explicit source address, the local port is cached in
+	 * ctxt->bind_su. */
+	dcc_su2ip(&bind_ip, &ctxt->bind_su);
+	bind_ip.port = 0;
+	if (dcc_clnt_info->src.family == AF_UNSPEC) {
+		memset(&zero_ip, 0, sizeof(zero_ip));
+		zero_ip.family = bind_ip.family;
+		src_ip = &zero_ip;
+	} else {
+		src_ip = &dcc_clnt_info->src;
+	}
+	if (ctxt->soc == INVALID_SOCKET
+	    || ((ctxt->flags & DCC_CTXT_USING_IPV4)!=0) != !DCC_INFO_IPV6()
+	    || (memcmp(src_ip, &bind_ip, sizeof(*src_ip))
+		&& (!(ctxt->flags & DCC_CTXT_SRCBAD)
+		    || DCC_IS_TIME(ctxt->start.tv_sec, ctxt->bind_time,
+				   DCC_CTXT_REBIND_SECS)))) {
+		if (!dcc_clnt_soc_reopen(emsg, ctxt)) {
+			dcc_info_unlock(0);
+			return 0;
+		}
+	}
+
+	/* Try to pick a new server if we do not have a server
+	 * or if the current server has become slow or unreliable. */
+	if (!good_rtt(class))
+		pick_srvr(emsg, class);
+
+	/* Check for new IP addresses occassionally or when we have none
+	 * If we cannot awaken a separate thread, do it ourself */
+	if ((!HAVE_SRVR(class)
+	     || DCC_IS_TIME(ctxt->now.tv_sec, class->resolve, DCC_MAP_RESOLVE))
+	    && (class->num_srvrs == 0
+		|| !dcc_clnt_wake_resolve())) {
+		if (!resolve_nms(emsg, ctxt, class))
+			return 0;
+	}
+
+	/* We might have switched to the current server when our
+	 * best server became slow.
+	 * If it has been a while, see if our best server is back
+	 * by sending NOPs to all servers. */
+	if ((class->measure == 0
+	     || (DCC_IS_TIME(ctxt->now.tv_sec, class->measure, FAST_RTT_SECS)
+		 && !good_rtt(class)))
+	    && !(clnt_fgs & DCC_CLNT_FG_NO_PICK_SRVR)) {
+		if (!measure_rtt(emsg, ctxt, class, clnt_fgs))
+			return 0;
+	}
+
+	if (!HAVE_SRVR(class) && !(clnt_fgs & DCC_CLNT_FG_BAD_SRVR_OK)) {
+		dcc_info_unlock(0);
+		return 0;
+	}
+
+	dcc_clnt_soc_flush(ctxt);
+	return 1;
+}
+
+
+
+/* send an operation to the server and get a response
+ *      The operation and response buffers must be distinct, because the
+ *	    response buffer is changed before the last use of the operation
+ *	    buffer */
+u_char					/* 0=failed 1=ok */
+dcc_clnt_op(DCC_EMSG emsg,
+	    DCC_CLNT_CTXT *ctxt,
+	    DCC_CLNT_FGS clnt_fgs,	/* DCC_CLNT_FG_* */
+	    const SRVR_INX *srvr_inxp,	/* 0 or ptr to server index */
+	    DCC_SRVR_ID *srvr_idp,	/* ID of server used */
+	    DCC_SOCKU *resp_su,		/* IP address of server used */
+	    DCC_HDR *msg, int msg_len, DCC_OPS op,
+	    DCC_OP_RESP *resp, int resp_max_len)
+{
+	DCC_SRVR_CLASS *class;
+	DCC_SRVR_ADDR *cur_addr;
+#ifdef DCC_PKT_VERSION7
+	DCC_REPORT  old_report;
+#endif
+	char addr_buf[80];
+	int addrs_gen;
+	DCC_OP_NUM op_num_r;
+	DCC_XLOG_ENTRY *xloge;
+	SRVR_INX srvr_inx;
+	int xmit_num;
+	int next_xmit, us, remaining, nfds;
+	u_char gotit;
+	int i;
+
+	if (emsg)
+		*emsg = '\0';
+	dcc_ctxts_lock();
+	if (!dcc_clnt_info
+	    && !dcc_map_info(emsg, 0, -1)) {
+		dcc_ctxts_unlock();
+		if (srvr_idp)
+			*srvr_idp = DCC_ID_INVALID;
+		return 0;
+	}
+	/* Get & lock common info.
+	 * insist on a server to talk to so that class->srvr_inx is sane */
+	if (!dcc_clnt_rdy(emsg, ctxt,
+			  clnt_fgs & ~(DCC_CLNT_FG_BAD_SRVR_OK
+				       | DCC_CLNT_FG_NO_PICK_SRVR))) {
+		dcc_ctxts_unlock();
+		if (srvr_idp)
+			*srvr_idp = DCC_ID_INVALID;
+		return 0;
+	}
+	class = DCC_GREY2CLASS(clnt_fgs & DCC_CLNT_FG_GREY);
+
+	if (resp_max_len > ISZ(*resp))
+		resp_max_len = ISZ(*resp);
+	else if (resp_max_len < ISZ(resp->hdr))
+		dcc_logbad(EX_SOFTWARE, "dcc_clnt_op(resp_max_len=%d)",
+			   resp_max_len);
+
+	/* use server that the caller wants,
+	 * if the caller specified the valid index of a server */
+	if (!srvr_inxp
+	    || !GOOD_SRVR(class, srvr_inx = *srvr_inxp))
+		srvr_inx = class->srvr_inx;
+
+	cur_addr = &class->addrs[srvr_inx];
+	if (srvr_idp)
+		*srvr_idp = cur_addr->srvr_id;
+	if (resp_su)
+		dcc_mk_su(resp_su, cur_addr->ip.family,
+			  &cur_addr->ip.u, cur_addr->ip.port);
+	addrs_gen = class->gen;
+
+	++dcc_clnt_info->proto_hdr.op_nums.r;
+	op_num_r = msg->op_nums.r;
+	memcpy(msg, &dcc_clnt_info->proto_hdr, sizeof(*msg));
+	/* old transaction ID for retransmissions */
+	if (clnt_fgs & DCC_CLNT_FG_RETRANS)
+		msg->op_nums.r = op_num_r;
+	if (cur_addr->srvr_pkt_vers > DCC_PKT_VERSION_MAX
+	    || cur_addr->srvr_pkt_vers < DCC_PKT_VERSION_MIN) {
+		dcc_pemsg(EX_DATAERR, emsg, "impossible pkt_vers %d for %s",
+			  cur_addr->srvr_pkt_vers,
+			  addr2str(addr_buf, sizeof(addr_buf), class,
+				   addrs_gen, cur_addr, 0));
+		dcc_info_unlock(0);
+		dcc_ctxts_unlock();
+		if (srvr_idp)
+			*srvr_idp = DCC_ID_INVALID;
+		return 0;
+	}
+
+#ifdef DCC_PKT_VERSION7
+	/* convert new report to old */
+	if (cur_addr->srvr_pkt_vers <= DCC_PKT_VERSION7
+	    && op == DCC_OP_REPORT) {
+		DCC_TGTS tgts;
+
+		tgts = ntohl(((DCC_REPORT *)msg)->tgts);
+#ifdef DCC_PKT_VERSION4
+		if (cur_addr->srvr_pkt_vers == DCC_PKT_VERSION4
+		    && (tgts & DCC_TGTS_SPAM)) {
+			memcpy(&old_report, msg, msg_len);
+			old_report.tgts = htonl(DCC_TGTS_TOO_MANY);
+			msg = &old_report.hdr;
+		}
+#endif
+	}
+#endif /* DCC_PKT_VERSION7 */
+
+	msg->pkt_vers = cur_addr->srvr_pkt_vers;
+	msg->op_nums.p = getpid();
+	msg->op = op;
+	gotit = 0;
+
+	/* The measured RTTs to servers helps the client pick a server
+	 * that will respond quickly and reliably and to know when to
+	 * retransmit a request that is lost due to network congestion or
+	 * bit rot.
+	 *
+	 * It is desirable for a client to concentrate its reports to
+	 * a single server.  That makes detecting spam by this and other
+	 * clients quicker.
+	 *
+	 * A client should retransmit when its initial transmission is lost
+	 * due to bit rot or congestion.  In case the loss is due to
+	 * congestion, it should retransmit only a limited number of
+	 * times and with increasing delays between retransmissions.
+	 *
+	 * It is more important that some requests from clients reach
+	 * a DCC server than others.  Most DCC checksum reports are not about
+	 * spam, and so it is best to not spend too much network bandwidth
+	 * retransmitting checksum reports or to delay the processing of the
+	 * messages. Administrative commands must be tried harder.
+	 * Therefore, let the caller of this routine decide whether to retry.
+	 * This routine merely increases the measured RTT after failures. */
+
+	memset(&ctxt->xlog, 0, sizeof(ctxt->xlog));
+	ctxt->xlog.base = ctxt->xlog.next = ctxt->xlog_entries;
+	ctxt->xlog.last = LAST(ctxt->xlog_entries);
+	xmit_num = 0;
+	next_xmit = ctxt->now_us;
+
+	/* Transmit, wait for a response, and repeat if needed.
+	 * The initial transmission is done as if it were a retransmission. */
+	for (;;) {
+		us = next_xmit - ctxt->now_us;
+		if (us <= 0) {
+			/* We have delayed long enough for each outstanding
+			 * transmission.  We are done if we have sent enough */
+			if (xmit_num >= DCC_MAX_XMITS)
+				break;
+
+			/* stop if we don't have enough time to wait */
+			us = retrans_time(cur_addr->rtt, xmit_num);
+			remaining =  DCC_MAX_DELAY - ctxt->now_us;
+			if (us > remaining) {
+				if (remaining < DCC_MIN_RTT)
+					break;
+				us = remaining;
+			}
+
+			/* wait as long as possible on the last try */
+			if (++xmit_num == DCC_MAX_XMITS
+			    && us < DCC_MAX_RTT) {
+				if (remaining > DCC_MAX_RTT)
+					us = DCC_MAX_RTT;
+				else
+					us = remaining;
+			}
+			next_xmit = us + ctxt->now_us;
+
+			/* Because of the flooding algorithm among DCC servers,
+			 * it is important that only a single server receive
+			 * reports of the checksums for a mail message.
+			 * That implies that retransmissions of reports must
+			 * go to the original server, even if some other local
+			 * client has re-resolved hostnames or switched
+			 * to a better server.
+			 * Otherwise our retransmissions to different servers
+			 * would not be recognized as retransmissions but
+			 * reports about separate copies of the mail message.
+			 * Sp we should not retransmit if the server
+			 * address table changes. */
+			if (addrs_gen != class->gen
+			    && op == DCC_OP_REPORT
+			    && !(clnt_fgs & DCC_CLNT_FG_GREY)) {
+				if (dcc_clnt_debug)
+					dcc_trace_msg("server address"
+						      " generation changed");
+				break;
+			}
+
+			if (!GOOD_NAM(cur_addr->nam_inx)) {
+				if (dcc_clnt_debug)
+					dcc_trace_msg("server deleted");
+				break;
+			}
+
+			/* use a connected socket early to get port
+			 * unreachable ICMP error messages, but do not
+			 * connect later to detect multi-homing */
+			if (0 >= clnt_xmit(ctxt, class, cur_addr, msg, msg_len,
+					   (!(cur_addr->flags
+					      & DCC_SRVR_ADDR_MHOME)
+					    && xmit_num < DCC_MAX_XMITS/2
+					    && ctxt->now_us<=DCC_MAX_DELAY/2)))
+				break;
+		}
+
+		/* release the mapped info while we wait for an answer */
+		if (!dcc_info_unlock(emsg)) {
+			dcc_ctxts_unlock();
+			if (srvr_idp)
+				*srvr_idp = DCC_ID_INVALID;
+			return 0;
+		}
+		dcc_ctxts_unlock();
+		nfds = dcc_select_poll(emsg, ctxt->soc, 1, us);
+		if (nfds < 0) {
+			/* note error, but we may already have an answer */
+			dcc_ctxts_lock();
+			class = DCC_GREY2CLASS(clnt_fgs & DCC_CLNT_FG_GREY);
+			break;
+		}
+		if (!get_now(emsg, ctxt))
+			return 0;       /* simply give up if time jumped */
+
+		/* recover the lock so that we can record the result of the
+		 * newly arrived answer in the shared and mapped file */
+		dcc_ctxts_lock();
+		class = DCC_GREY2CLASS(clnt_fgs & DCC_CLNT_FG_GREY);
+		if (!dcc_info_lock(emsg)) {
+			dcc_ctxts_unlock();
+			if (srvr_idp)
+				*srvr_idp = DCC_ID_INVALID;
+			return 0;
+		}
+
+		if (nfds > 0) {
+			u_char unreachable = 0;
+			for (;;) {
+				DCC_OP_RESP buf;
+
+				i = clnt_recv(ctxt, class, &buf,
+					      min(ISZ(buf), resp_max_len),
+					      msg, &xloge);
+				if (i <= 0)
+					break;
+				if (i == 1) {
+					/* stop delaying after the first
+					 * ICMP Unreachable message,
+					 * but collect everything that has
+					 * already arrived */
+					unreachable = 1;
+					continue;
+				}
+
+				update_rtt(ctxt, class, xloge,
+					   ctxt->now_us - xloge->sent_us
+					   + ((op != DCC_OP_REPORT
+					       && op != DCC_OP_QUERY)
+					      ? cur_addr->srvr_wait : 0));
+
+				/* save the last answer we get */
+				memcpy(resp, &buf, ntohs(buf.hdr.len));
+				gotit = 1;
+			}
+			if (i < 0 || unreachable || gotit)
+				break;
+		}
+	}
+	/* the contexts and the shared information are locked */
+
+	/* penalize server for lost packets */
+	resp_rates(ctxt, class, 0);
+
+	/* fail if the server did not answer at all */
+	if (!gotit) {
+#if 0
+		system("./abort_dccd");
+#endif
+		flush_emsg(emsg, dcc_clnt_debug);
+		dcc_pemsg(EX_TEMPFAIL, emsg, "no %s answer from %s after %d ms",
+			  DCC_IS_GREY_STR(class),
+			  addr2str(addr_buf, sizeof(addr_buf), class,
+				   addrs_gen, cur_addr, 0),
+			  ctxt->now_us/1000);
+		/* Since we got no answer at all, look for a different server.
+		 * If we can't find any server or a different server
+		 * or if we have already spent too much time,
+		 * then don't try again for a while to not delay the MTA.
+		 * If we find another server, then return the valid server-ID
+		 * of the non-responsive server to let the caller know that it
+		 * can try again immediately. */
+		if (srvr_inxp && srvr_inx == *srvr_inxp) {
+			/* but only if not using a caller specified server */
+			if (srvr_idp)
+				*srvr_idp = DCC_ID_INVALID;
+		} else if (!pick_srvr(0, class)
+			   || srvr_inx == class->srvr_inx) {
+			if (srvr_idp) {
+				if (dcc_clnt_debug)
+					dcc_trace_msg("no better alternate"
+						      " for retry");
+				*srvr_idp = DCC_ID_INVALID;
+			}
+			fail_more(ctxt, class);
+		} else if (srvr_idp
+			   && (i = retrans_time(class->addrs[class ->srvr_inx
+							].rtt, 0),
+			       ctxt->now_us + i >= DCC_MAX_DELAY)) {
+			/* discourage the caller from trying the other server
+			 * if the total delay after trying the other server
+			 * would be excessive */
+			if (dcc_clnt_debug)
+				dcc_trace_msg("alternate too slow for retry"
+					      " with rtt %d ms after %d ms",
+					      i/1000,
+					      ctxt->now_us/1000);
+			*srvr_idp = DCC_ID_INVALID;
+		}
+		dcc_info_unlock(0);
+		dcc_ctxts_unlock();
+		return 0;
+	}
+
+	/* reset failure backoff */
+	class->fail_exp = 0;
+
+	if (!dcc_info_unlock(emsg)) {
+		dcc_ctxts_unlock();
+		if (srvr_idp)
+			*srvr_idp = DCC_ID_INVALID;
+		return 0;
+	}
+	dcc_ctxts_unlock();
+
+	flush_emsg(emsg, dcc_clnt_debug);
+	return 1;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/clnt_unthreaded.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/clnt_unthreaded.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,216 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * unthreaded version of client locking
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.47 $Revision$
+ */
+
+#include "dcc_ck.h"
+
+
+/* many POSIX thread implementations have unexpected side effects on
+ * ordinary system calls, so don't use the threaded version unless
+ * necessary */
+
+u_char grey_on;
+u_char grey_query_only;
+
+DCC_WF cmn_wf, cmn_tmp_wf;
+
+
+static u_char ctxts_locked;
+
+
+void
+dcc_ctxts_lock(void)
+{
+#ifdef DCC_DEBUG_CLNT_LOCK
+	if (ctxts_locked)
+		dcc_logbad(EX_SOFTWARE, "already have ctxts lock");
+#endif
+	++ctxts_locked;
+}
+
+
+
+void
+dcc_ctxts_unlock(void)
+{
+	assert_ctxts_locked();
+	--ctxts_locked;
+}
+
+
+
+#ifdef DCC_DEBUG_CLNT_LOCK
+void
+assert_ctxts_locked(void)
+{
+	if (!ctxts_locked)
+		dcc_logbad(EX_SOFTWARE, "don't have ctxts lock");
+}
+
+
+
+void
+assert_ctxts_unlocked(void)
+{
+	if (ctxts_locked)
+		dcc_logbad(EX_SOFTWARE, "have ctxts lock");
+}
+#endif
+
+
+
+void
+dcc_syslog_lock(void)
+{
+}
+
+
+
+void
+dcc_syslog_unlock(void)
+{
+}
+
+
+
+u_char dcc_host_locked = 1;
+
+/* This function is mentioned in dccifd/dccif-test/dccif-test.c
+ *	and so cannot change lightly. */
+void
+dcc_host_lock(void)
+{
+}
+
+
+
+/* This function is mentioned in dccifd/dccif-test/dccif-test.c
+ *	and so cannot change lightly. */
+void
+dcc_host_unlock(void)
+{
+}
+
+
+
+#ifdef DCC_DEBUG_HEAP
+void
+dcc_malloc_lock(void)
+{
+}
+
+
+void
+dcc_malloc_unlock(void)
+{
+}
+#endif /* DCC_DEBUG_HEAP */
+
+
+
+#ifndef HAVE_LOCALTIME_R
+void
+dcc_localtime_lock(void)
+{
+}
+
+
+
+void
+dcc_localtime_unlock(void)
+{
+}
+#endif /* HAVE_LOCALTIME_R */
+
+
+
+void
+dcc_clnt_unthread_init(void)
+{
+#ifdef DCC_WIN32
+	win32_init();
+#endif
+}
+
+
+u_char
+dcc_clnt_wake_resolve(void)
+{
+	return 0;
+}
+
+
+
+#ifdef DCC_DEBUG_CLNT_LOCK
+void
+assert_cwf_locked(void)
+{
+}
+#endif
+
+
+
+u_char
+helper_lock_init(void)
+{
+	return 0;
+}
+
+
+
+void
+helper_lock(void)
+{
+}
+
+
+
+void
+helper_unlock(void)
+{
+}
+
+
+
+const REPLY_TPLT *
+dnsbl_parse_reply(const char *pat UATTRIB)
+{
+	return 0;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/daemon.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/daemon.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,48 @@
+/* compatibility hack for old systems lacking daemon() */
+
+#include "dcc_config.h"
+#include "dcc_paths.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+int
+dcc_daemon(int nochdir, int noclose)
+{
+	int retv;
+
+	if (!nochdir) {
+		if (chdir("/") == -1)
+			perror("chdir /");
+	}
+	retv = fork();
+	if (retv == -1)
+		return -1;		/* fork() failed */
+
+	if (retv != 0)
+		_exit(0);		/* parent of new child */
+
+	/* fork again after setsid() so that the PID is not the session
+	 * group leader so that opening a tty device won't make a
+	 * controlling terminal. */
+	setsid();
+	retv = fork();
+	if (retv == -1) {
+		perror("fork");		/* second fork() failed */
+		exit(1);		/* cannot tell caller */
+	}
+	if (retv != 0)
+		_exit(0);		/* parent of final child */
+
+	if (!noclose) {
+		close(0);
+		close(1);
+		close(2);
+		open(_PATH_DEVNULL, O_RDWR, 0666);
+		dup2(0, 1);
+		dup2(0, 2);
+	}
+	return 0;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/dccif.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/dccif.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,508 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * dccif(), a sample C interface to the DCC interface daemon, dccifd
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.31 $Revision$
+ */
+
+
+#include "dccif.h"
+#include "dcc_clnt.h"
+#include "sys/uio.h"
+#include <sys/un.h>
+#ifndef DCC_WIN32
+#include <arpa/inet.h>
+#endif
+
+
+
+static void
+dccif_closes(int *fd1, int *fd2, int *fd3)
+{
+	if (fd1 && *fd1 >= 0) {
+		close(*fd1);
+		*fd1 = -1;
+	}
+	if (fd2 && *fd2 >= 0) {
+		close(*fd2);
+		*fd2 = -1;
+	}
+	if (fd3 && *fd3 >= 0) {
+		close(*fd3);
+		*fd3 = -1;
+	}
+}
+
+
+
+static void PATTRIB(6,7)
+dccif_pemsg(int ex_code, DCC_EMSG emsg,
+	    int *fd1, int *fd2, int *fd3,
+	    const char *msg, ...)
+{
+	va_list args;
+
+	if (emsg) {
+		va_start(args, msg);
+		dcc_vpemsg(ex_code, emsg, msg, args);
+		va_end(args);
+	}
+
+	dccif_closes(fd1, fd2, fd3);
+}
+
+
+
+static u_char
+dccif_sock(DCC_EMSG emsg,
+	   const char *srvr_addr,	/* home directory, FIFO, or host,port */
+	   SOCKET *s, int* fd2, int* fd3)
+{
+	struct stat sb;
+	char host[DCC_MAXDOMAINLEN];
+	u_int16_t port;
+	DCC_SOCKU su;
+	struct sockaddr_un sunsock;
+	const char *cp;
+	int error;
+
+	/* assume srvr_addr is a hostname,port if not a file or directory */
+	if (0 > stat(srvr_addr, &sb)) {
+		cp = dcc_parse_nm_port(emsg, srvr_addr, DCC_GET_PORT_INVALID,
+				       host, sizeof(host),
+				       &port, 0, 0,
+				       0, 0);
+		if (!cp) {
+			dccif_closes(0, fd2, fd3);
+			return 0;
+		}
+		if (*cp != '\0') {
+			dccif_pemsg(EX_USAGE, emsg, 0, fd2, fd3,
+				    "invalid IP address: %s", srvr_addr);
+			return 0;
+		}
+		if (host[0] == '\0') {
+			dccif_pemsg(EX_NOHOST, emsg, 0, fd2, fd3,
+				    "missing host name in \"%s\"", srvr_addr);
+			return 0;
+		}
+		dcc_host_lock();
+		if (!dcc_get_host(host, 2, &error)) {
+			dcc_host_unlock();
+			dccif_pemsg(EX_NOHOST, emsg, 0, fd2, fd3,
+				    "%s: %s",
+				    srvr_addr, DCC_HSTRERROR(error));
+			return 0;
+		}
+		su = dcc_hostaddrs[0];
+		*DCC_SU_PORTP(&su) = port;
+		dcc_host_unlock();
+
+		*s = socket(su.sa.sa_family, SOCK_STREAM, 0);
+		if (*s < 0) {
+			dccif_pemsg(EX_IOERR, emsg, s, fd2, fd3,
+				    "socket(AF_UNIX): %s", ERROR_STR());
+			return 0;
+		}
+
+		if (0 > connect(*s, &su.sa, DCC_SU_LEN(&su))) {
+			dccif_pemsg(EX_IOERR, emsg, s, fd2, fd3,
+				    "connect(%s): %s", srvr_addr, ERROR_STR());
+			return 0;
+		}
+		return 1;
+	}
+
+	*s = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (*s < 0) {
+		dccif_pemsg(EX_IOERR, emsg, s, fd2, fd3,
+			    "socket(AF_UNIX): %s", ERROR_STR());
+		return 0;
+	}
+	memset(&sunsock, 0, sizeof(sunsock));
+	sunsock.sun_family = AF_UNIX;
+	if (S_ISDIR(sb.st_mode))
+		snprintf(sunsock.sun_path, sizeof(sunsock.sun_path),
+			 "%s/"DCC_DCCIF_UDS, srvr_addr);
+	else
+		BUFCPY(sunsock.sun_path, srvr_addr);
+#ifdef HAVE_SA_LEN
+	sunsock.sun_len = SUN_LEN(&sunsock);
+#endif
+	if (0 > connect(*s, (struct sockaddr *)&sunsock, sizeof(sunsock))) {
+		dccif_pemsg(EX_IOERR, emsg, s, fd2, fd3,
+			    "connect(%s): %s",
+			    sunsock.sun_path, ERROR_STR());
+		return 0;
+	}
+	return 1;
+}
+
+
+
+static u_char
+dccif_writev(DCC_EMSG emsg,
+	     int *out_fd, int *fd2, int *fd3,
+	     const struct iovec *iovs,
+	     int num_iovs,
+	     int wtotal)
+{
+	int i;
+
+	i = writev(*out_fd, iovs, num_iovs);
+	if (i == wtotal)
+		return 1;
+	if (i < 0)
+		dccif_pemsg(EX_IOERR, emsg, out_fd, fd2, fd3,
+			    "dccif writev(%d): %s", wtotal, ERROR_STR());
+	else
+		dccif_pemsg(EX_IOERR, emsg, out_fd, fd2, fd3,
+			    "dccif writev(%d)=%d", wtotal, i);
+	return 0;
+}
+
+
+
+static u_char
+dccif_write(DCC_EMSG emsg,
+	    int *out_fd, int *fd2, int *fd3,
+	    const void *buf, int buf_len)
+{
+	int i;
+
+	i = write(*out_fd, buf, buf_len);
+	if (i == buf_len)
+		return 1;
+	if (i < 0)
+		dccif_pemsg(EX_IOERR, emsg, out_fd, fd2, fd3,
+			    "dccif writev(%d): %s", buf_len, ERROR_STR());
+	else
+		dccif_pemsg(EX_IOERR, emsg, out_fd, fd2, fd3,
+			    "dccif writev(%d)=%d",
+			    buf_len, i);
+	return 0;
+}
+
+
+
+static int
+dccif_read(DCC_EMSG emsg,
+	   int *in_fd, int *fd2, int *fd3,
+	   char *buf, int buf_len, int min_read)
+{
+	int total, i;
+
+	total = 0;
+	for (;;) {
+		i = read(*in_fd, &buf[total], buf_len-total);
+		if (i < 0) {
+			dccif_pemsg(EX_IOERR, emsg, in_fd, fd2, fd3,
+				    "dccif read(): %s", ERROR_STR());
+			return -1;
+		}
+		total += i;
+		if (total >= min_read)
+			return total;
+		if (i == 0) {
+			dccif_pemsg(EX_IOERR, emsg, in_fd, fd2, fd3,
+				    "dccif read(): premature EOF");
+			return -1;
+		}
+	}
+}
+
+
+
+/* This function is mentioned in dccifd/dccif-test/dccif-test.c
+ *	and so cannot change lightly. */
+u_char					/* 0=failed or DCCIF_RESULT_* */
+dccif(DCC_EMSG emsg,			/* put error message here */
+      int out_body_fd,			/* -1 or write body to here */
+      char **out_body,			/* 0 or pointer for resulting body */
+      const char *opts,			/* blank separated string DCCIF_OPT_* */
+      const DCC_SOCKU *clnt_addr,	/* SMTP client IPv4 or IPv6 address */
+      const char *clnt_name,		/* optional SMTP client name */
+      const char *helo,
+      const char *env_from,		/* envelope sender */
+      DCCIF_RCPT *rcpts,		/* pruned envelope recipients */
+      int in_body_fd,			/* -1 or read body from here */
+      const char *in_body,		/* 0 or start of incoming body */
+      const char *srvr_addr)		/* home directory, FIFO, or host,port */
+{
+	static const char nl = '\n';
+	static const char cr = '\r';
+	int s;
+	char clnt_str[INET6_ADDRSTRLEN+1+1];
+	struct iovec iovs[50], *iovp;
+	int wtotal;
+#	define ADD_IOV(b,l) {int _l = (l); iovp->iov_base = (char *)(b); \
+		iovp->iov_len = _l; ++iovp; wtotal += _l;}
+	DCCIF_RCPT *rcpt;
+	char buf[4*1024];
+	char result;
+	int read_len, in_body_len, max_body_len, nxt;
+	int i, j;
+
+	if (!srvr_addr || *srvr_addr == '\0')
+		srvr_addr = DCC_HOMEDIR;
+
+	if (emsg)
+		*emsg = '\0';
+
+	if (!clnt_addr) {
+		clnt_str[0] = '\0';
+	} else {
+		dcc_su2str2(clnt_str, sizeof(clnt_str), clnt_addr);
+	}
+
+	if (!dccif_sock(emsg, srvr_addr, &s, &in_body_fd, &out_body_fd))
+		return 0;
+
+	/* first line of request */
+	iovp = iovs;
+	wtotal = 0;
+	if (opts)
+		ADD_IOV(opts, strlen(opts));
+	ADD_IOV(&nl, 1);
+
+	i = strlen(clnt_str);
+	clnt_str[i++] = '\r';
+	ADD_IOV(clnt_str, i);
+	if (i > 1 && clnt_name)
+		ADD_IOV(clnt_name, strlen(clnt_name));
+	ADD_IOV(&nl, 1);
+
+	if (helo)
+		ADD_IOV(helo, strlen(helo));
+	ADD_IOV(&nl, 1);
+
+	if (env_from)
+		ADD_IOV(env_from, strlen(env_from));
+	ADD_IOV(&nl, 1);
+
+	for (rcpt = rcpts; rcpt; rcpt = rcpt->next) {
+		ADD_IOV(rcpt->addr, strlen(rcpt->addr));
+		ADD_IOV(&cr, 1);
+		if (rcpt->user)
+			ADD_IOV(rcpt->user, strlen(rcpt->user));
+		ADD_IOV(&nl, 1);
+		if (iovp >= &iovs[DIM(iovs)-4-1]) {
+			if (!dccif_writev(emsg, &s, &in_body_fd, &out_body_fd,
+					  iovs, iovp - iovs, wtotal))
+				return 0;
+			iovp = iovs;
+			wtotal = 0;
+		}
+	}
+	ADD_IOV(&nl, 1);
+
+	/* copy (some of) the body from the buffer to the daemon */
+	if (in_body) {
+		in_body_len = strlen(in_body);
+		ADD_IOV(in_body, in_body_len);
+	} else {
+		in_body_len = 0;
+	}
+
+	if (!dccif_writev(emsg, &s, &in_body_fd, &out_body_fd,
+			  iovs, iovp - iovs, wtotal))
+		return 0;
+
+	/* copy (the rest of) the body from input file to the daemon */
+	if (in_body_fd >= 0) {
+		for (;;) {
+			i = read(in_body_fd, buf, sizeof(buf));
+			if (i <= 0) {
+				if (i == 0)
+					break;
+				dccif_pemsg(EX_IOERR, emsg,
+					    &s, &in_body_fd, &out_body_fd,
+					    "read(body): %s", ERROR_STR());
+				return 0;
+			}
+			if (!dccif_write(emsg, &s, &in_body_fd, &out_body_fd,
+					 buf, i))
+				return 0;
+			in_body_len += i;
+		}
+		if (0 > close(in_body_fd)) {
+			dccif_pemsg(EX_IOERR, emsg,
+				    &s, &in_body_fd, &out_body_fd,
+				    "close(in_body_fd): %s", ERROR_STR());
+			return 0;
+		}
+		in_body_fd = -1;
+	}
+
+	/* tell the daemon it has all of the body */
+	if (0 > shutdown(s, 1)) {
+		dccif_pemsg(EX_IOERR, emsg, &s, &in_body_fd, &out_body_fd,
+			    "shutdown(): %s", ERROR_STR());
+		return 0;
+	}
+
+	/* get the overall result */
+	read_len = dccif_read(emsg, &s, &in_body_fd, &out_body_fd,
+			      buf, sizeof(buf), 2);
+	if (read_len < 0)
+		return 0;
+	result = buf[0];
+	if (result != DCCIF_RESULT_OK
+	    && result != DCCIF_RESULT_GREY
+	    && result != DCCIF_RESULT_REJECT
+	    && result != DCCIF_RESULT_SOME
+	    && result != DCCIF_RESULT_TEMP) {
+		dccif_pemsg(EX_SOFTWARE, emsg, &s, &in_body_fd, &out_body_fd,
+			  "unrecognized dccifd result \"%.*s\"", 1, buf);
+		return 0;
+	}
+
+	/* read the vector of results from the daemon */
+	nxt = 1;			/* skip '\n' after result */
+	for (rcpt = rcpts; rcpt != 0; rcpt = rcpt->next) {
+		if (++nxt >= read_len) {
+			read_len = dccif_read(emsg,
+					      &s, &in_body_fd, &out_body_fd,
+					      buf, sizeof(buf), 2);
+			if (read_len < 0)
+				return 0;
+			nxt = 0;
+		}
+		switch (buf[nxt]) {
+		case DCCIF_RCPT_ACCEPT:
+		case DCCIF_RCPT_REJECT:
+		case DCCIF_RCPT_GREY:
+			rcpt->ok = buf[nxt];
+			break;
+		default:
+			dccif_pemsg(EX_SOFTWARE, emsg,
+				    &s, &in_body_fd, &out_body_fd,
+				    "unrecognized dccifd recipient result"
+				    " \"%c\" for %s",
+				    buf[nxt], rcpt->addr);
+			return 0;
+		}
+	}
+	if (++nxt >= read_len) {
+		read_len = dccif_read(emsg, &s, &in_body_fd, &out_body_fd,
+				      buf, 1, 1);
+		if (read_len < 0)
+			return 0;
+		nxt = 0;
+	}
+	if (buf[nxt++] != '\n') {
+		dccif_pemsg(EX_SOFTWARE, emsg, &s, &in_body_fd, &out_body_fd,
+			  "unrecognized dccifd text after results: \"%c\"",
+			  buf[nxt]);
+		return 0;
+	}
+
+	/* copy the body from the daemon to the output buffer for file */
+	if (out_body_fd >= 0) {
+		for (;;) {
+			j = read_len - nxt;
+			if (j <= 0) {
+				j = dccif_read(emsg, &s,
+					       &in_body_fd, &out_body_fd,
+					       buf, sizeof(buf), 0);
+				if (j < 0)
+					return 0;
+				if (j == 0)
+					break;
+				read_len = j;
+				nxt = 0;
+			}
+			if (!dccif_write(emsg, &out_body_fd,
+					 &s, &in_body_fd,
+					 &buf[nxt], j))
+				return 0;
+			read_len = 0;
+		}
+		if (0 > close(out_body_fd)) {
+			dccif_pemsg(EX_IOERR, emsg,
+				    &s, &in_body_fd, &out_body_fd,
+				    "close(out_body_fd): %s",
+				    ERROR_STR());
+			return 0;
+		}
+		out_body_fd = -1;
+
+	} else if (out_body) {
+		char *body;
+
+		max_body_len = in_body_len + DCC_MAX_XHDR_LEN+1;
+		body = malloc(max_body_len);
+		*out_body = body;
+		j = read_len - nxt;
+		if (j > 0) {
+			if (j > max_body_len)
+				j = max_body_len;
+			memcpy(body, &buf[nxt], j);
+			body += j;
+			max_body_len -= j;
+		}
+		for (;;) {
+			if (max_body_len <= 0) {
+				dccif_pemsg(EX_SOFTWARE, emsg,
+					    &s, &in_body_fd, &out_body_fd,
+					    "too much body from dccifd");
+				free(*out_body);
+				return 0;
+			}
+
+			j = dccif_read(emsg, &s, &in_body_fd, &out_body_fd,
+				       body, max_body_len, 0);
+			if (j < 0) {
+				free(*out_body);
+				return 0;
+			}
+			if (j == 0)
+				break;
+			body += j;
+			max_body_len -= j;
+		}
+		*body = '\0';
+	}
+
+	if (0 > close(s)) {
+		dccif_pemsg(EX_IOERR, emsg, &s, &in_body_fd, &out_body_fd,
+			    "close(socket): %s", ERROR_STR());
+		return 0;
+	}
+	return result;
+#undef ADD_IOV
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/dnsbl.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/dnsbl.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,1824 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * reject messages contain URLs that resolve to DNS blacklisted IP addresses
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.100 $Revision$
+ */
+
+#include "helper.h"
+#include "dcc_heap_debug.h"
+#ifndef DCC_WIN32
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+
+/* can check MX and NS addresses only with a standard resolver library */
+#define MXNS_DNSBL
+#if !defined(HAVE__RES) || !defined(HAVE_RES_INIT)
+#undef HAVE__RES
+#undef HAVE_RES_INIT
+#undef MXNS_DNSBL
+#endif
+#if !defined (HAVE_RES_QUERY) || !defined(HAVE_DN_EXPAND)
+#undef MXNS_DNSBL
+#endif
+#if !defined(HAVE_HSTRERROR)
+#undef MXNS_DNSBL			/* MX lookups need raw hsterror() */
+#endif
+#if !defined(T_MX) || !defined(T_A) || !defined(T_AAAA) || !defined(T_NS)
+#undef MXNS_DNSBL
+#endif
+#if !defined(C_IN) || !defined(C_IN) || !defined(PACKETSZ)
+#undef MXNS_DNSBL
+#endif
+#if !defined(RES_DEFNAMES) || !defined(RES_DNSRCH) || !defined(RES_NOALIASES)
+#undef MXNS_DNSBL
+#endif
+
+
+DNSBL *dnsbls;
+static DNSBL_UNHIT dnsbl_groups;
+u_char have_dnsbl_groups;
+
+HELPER helper;
+u_char have_helpers;
+DCC_PATH dnsbl_progpath;
+
+static u_char is_helper;
+static const char *helper_str = "";
+
+static u_char have_ipv4_dnsbl;
+static u_char have_ipv6_dnsbl;
+static u_char have_name_dnsbl;
+
+#define MAX_MSG_SECS 1000
+#ifndef RES_TIMEOUT
+#define RES_TIMEOUT 3
+#endif
+static int msg_secs = 25;		/* total seconds/mail message */
+static time_t msg_us;
+static int url_secs = 11;		/* total seconds/host name */
+static time_t url_us;
+
+
+/* Parse a string of the form:
+ *	    "domain[,[IPaddr][,name|ipv4|ipv6]]
+ *	or of one of the forms:
+ *	    set:progpath	path of helper program
+ *	    set:debug=X		more logging
+ *	    set:msg-secs=S	total seconds checking blacklists/message
+ *	    set:url-secs=S	total seconds per host name
+ *	    set:[no-]client	client IP address checks
+ *	    set:[no-]mail_host	envelope mail_from checks
+ *	    set:[no-]URL	body URL checks
+ *	    set:[no-]MX		MX checks
+ *	    set:[no-]NS		NS checks
+ *	    set:defaults	restore defaults
+ *	    set:group=X		start group of DNSBLs
+ *	    set:[no-]temp-fail	timeout temporarily fails SMTP transaction
+ *	    set:max_helpers=X	override dccm or dccifd max_work
+ *
+ *	    set:[no-]envelope	 obsolete mail host & client
+ *
+ *	    set:helper=soc,fd,X	start DNS resolver process
+ */
+u_char					/* 0=bad */
+dcc_parse_dnsbl(DCC_EMSG emsg, const char *entry, const char *progpath,
+		u_char tfail)
+{
+	static DNSBL_FGS cur_fgs = DNSBL_FG_DEFAULT;
+	static const REPLY_TPLT *cur_reply = 0;
+	static int bl_num = 0;
+	static int cur_group = 0;
+	DNSBL_GBITS gbit;
+	DNSBL *dp, *dp1, **dpp;
+	const char *tgt_ip;		/* "hit" IP address of this blacklist */
+	DNSBL_DOM tgt_ip_buf;
+	DNSBL_TYPE bl_type;
+	struct in6_addr tgt, tgt_mask;
+	int tgt_bits;
+	u_char tgt_use_ipv6;
+	DCC_EMSG addr_emsg;
+	int error, bl_dom_len;
+	int val;
+	char *p;
+#ifdef HAVE_HELPERS
+	int soc;
+	int fd;
+	int total_helpers;
+#	define SAVE_ARG(arg) helper_save_arg("-B", arg)
+#else
+#	define SAVE_ARG(arg)
+#endif
+
+	if (progpath && dnsbl_progpath[0] == '\0') {
+		const char *slash = strrchr(progpath, '/');
+		if (slash)
+			++slash;
+		else
+			slash = progpath;
+		snprintf(dnsbl_progpath, sizeof(dnsbl_progpath),
+			 "%.*sdns-helper", (int)(slash-progpath), progpath);
+	}
+
+	/* pretend to turn on temp-fail for dccproc to get
+	 * DNSBLx(timeout) messages */
+	if (tfail)
+		cur_fgs |= DNSBL_FG_TFAIL;
+
+	/* handle parameter settings */
+	if (!CLITCMP(entry, "set:")) {
+		const char *parm = entry+LITZ("set:");
+#ifdef HAVE_HELPERS
+		/* start running a helper process on the last, magic -B */
+		if (3 == sscanf(parm, HELPER_PAT, &soc, &fd, &total_helpers)) {
+			helper_child(soc, fd, total_helpers);
+		}
+#endif
+		if (!CLITCMP(parm, "progpath=")) {
+			BUFCPY(dnsbl_progpath, parm+LITZ("progpath="));
+			return 1;
+		}
+		if (!strcasecmp(parm, "debug")) {
+			++helper.debug;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (1 == sscanf(parm, "debug=%d", &val)) {
+			helper.debug = val;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "envelope")) {	/* obsolete */
+			cur_fgs |= (DNSBL_FG_CLIENT | DNSBL_FG_MAIL_HOST);
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "no-envelope")) {	/* obsolete */
+			cur_fgs &= ~(DNSBL_FG_CLIENT | DNSBL_FG_MAIL_HOST);
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "client")) {
+			/* obsolete */
+			cur_fgs |= DNSBL_FG_CLIENT;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "no-client")) {
+			/* obsolete */
+			cur_fgs &= ~DNSBL_FG_CLIENT;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "mail_host")) {
+			/* obsolete */
+			cur_fgs |= DNSBL_FG_MAIL_HOST;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "no-mail_host")) {
+			/* obsolete */
+			cur_fgs &= ~DNSBL_FG_MAIL_HOST;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "body")) {    /* obsolete */
+			cur_fgs |= DNSBL_FG_URL;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "url")) {
+			cur_fgs |= DNSBL_FG_URL;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "no-body")) { /* obsolete */
+			cur_fgs &= ~DNSBL_FG_URL;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "no-URL")) {
+			cur_fgs &= ~DNSBL_FG_URL;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "mx")) {
+#ifdef MXNS_DNSBL
+			cur_fgs |= DNSBL_FG_MX;
+			SAVE_ARG(entry);
+			return 1;
+#else
+			dcc_pemsg(EX_USAGE, emsg,
+				  "MX DNS blacklist checks not supported");
+			return 0;
+#endif
+		}
+		if (!strcasecmp(parm, "no-mx")) {
+			cur_fgs &= ~DNSBL_FG_MX;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "ns")) {
+#ifdef MXNS_DNSBL
+			cur_fgs |= DNSBL_FG_MX;
+			SAVE_ARG(entry);
+			return 1;
+#else
+			dcc_pemsg(EX_USAGE, emsg,
+				  "NS DNS blacklist checks not supported");
+			return 0;
+#endif
+		}
+		if (!strcasecmp(parm, "no-ns")) {
+			cur_fgs &= ~DNSBL_FG_NS;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "defaults")) {
+			cur_fgs = DNSBL_FG_DEFAULT;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!CLITCMP(parm, "rej-msg=")) {
+			parm += LITZ("rej-msg=");
+			if (*parm == '\0')
+				cur_reply = 0;
+			else
+				cur_reply = dnsbl_parse_reply(parm);
+			/* do not save for helpers */
+			return 1;
+		}
+		if (!CLITCMP(parm, "msg-secs=")) {
+			parm += LITZ("msg-secs=");
+			val = strtoul(parm, &p, 10);
+			if (*p != '\0' || val < 1 || val > MAX_MSG_SECS) {
+				dcc_pemsg(EX_USAGE, emsg,
+					  "bad number of seconds in \"-B %s\"",
+					  entry);
+				return 0;
+			}
+			if (msg_secs != val) {
+				msg_secs = val;
+				SAVE_ARG(entry);
+			}
+			return 1;
+		}
+		if (!CLITCMP(parm, "url-secs=")) {
+			parm += LITZ("url-secs=");
+			val = strtoul(parm, &p, 10);
+			if (*p != '\0' || val < 1 || val > MAX_MSG_SECS) {
+				dcc_pemsg(EX_USAGE, emsg,
+					  "bad number of seconds in \"-B %s\"",
+					  entry);
+				return 0;
+			}
+			if (url_secs != val) {
+				url_secs = val;
+				SAVE_ARG(entry);
+			}
+			return 1;
+		}
+		if (1 == sscanf(parm, "group=%d", &val)
+		    && val >= 1 && val <= MAX_DNSBL_GROUPS) {
+			cur_group = val-1;
+			have_dnsbl_groups = 1;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "temp-fail")) {
+			cur_fgs |= DNSBL_FG_TFAIL;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (!strcasecmp(parm, "no-temp-fail")) {
+			cur_fgs &= ~DNSBL_FG_TFAIL;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		if (1 == sscanf(parm, "max_helpers=%d", &val)
+		    && val >= 1 && val < 1000) {
+			helper.max_helpers = val;
+			SAVE_ARG(entry);
+			return 1;
+		}
+		dcc_pemsg(EX_USAGE, emsg, "unrecognized  \"-B %s\"",
+			  entry);
+		return 0;
+	}
+
+	/* we must have a DNSBL specification */
+	bl_type = DNSBL_TYPE_IPV4;	/* assume it is a simple IPv4 DNSBL */
+	tgt_ip = strchr(entry, ',');
+	if (!tgt_ip) {
+		bl_dom_len = strlen(entry);
+		have_ipv4_dnsbl = 1;
+	} else {
+		bl_dom_len = tgt_ip - entry;
+		++tgt_ip;
+
+		/* notice trailing ",name" or ",IPv4" */
+		p = strchr(tgt_ip, ',');
+		if (!p) {
+			have_ipv4_dnsbl = 1;
+		} else {
+			++p;
+			if (!strcasecmp(p, "name")) {
+				bl_type = DNSBL_TYPE_NAME;
+				have_name_dnsbl = 1;
+			} else if (!strcasecmp(p, "IPV4")) {
+				bl_type = DNSBL_TYPE_IPV4;
+				have_ipv4_dnsbl = 1;
+			} else if (!strcasecmp(p, "IPV6")) {
+				bl_type = DNSBL_TYPE_IPV6;
+				have_ipv6_dnsbl = 1;
+			} else {
+				dcc_pemsg(EX_NOHOST, emsg,
+					  "unknown DNSBL type in \"%s\"",
+					  entry);
+				return 0;
+			}
+			STRLCPY(tgt_ip_buf.c, tgt_ip,
+				min(ISZ(tgt_ip_buf), p-tgt_ip));
+			tgt_ip = tgt_ip_buf.c;
+		}
+	}
+
+	if (entry[0] == '.') {
+		++entry;
+		--bl_dom_len;
+	}
+	if (bl_dom_len < 1) {
+		dcc_pemsg(EX_NOHOST, emsg,
+			  "invalid DNS blacklist \"%s\"", entry);
+		return 0;
+	}
+
+	/* assume 127.0.0.2 if the target address is missing */
+	if (!tgt_ip || *tgt_ip == '\0')
+		tgt_ip = "127.0.0.2";
+
+	if (!strcasecmp(tgt_ip, "any")) {
+		/* That we get a result when we lookup up an address in the
+		 * in the DNSBL is all that matters in this case.
+		 * Whether it is IPv6 or IPv4 does not */
+		tgt_use_ipv6 = 2;
+		memset(&tgt, 0, sizeof(tgt));
+		memset(&tgt_mask, 0, sizeof(tgt_mask));
+
+	} else if (0 != (tgt_bits = dcc_str2cidr(emsg, &tgt, &tgt_mask,
+						 &tgt_use_ipv6, tgt_ip,
+						 0, 0))) {
+		if (tgt_bits < 0) {
+			dcc_pemsg(EX_NOHOST, addr_emsg,
+				  "invalid DNS blacklist %s \"%s\"",
+				  addr_emsg, entry);
+			return 0;
+		}
+
+	} else {
+		dcc_host_lock();
+		if (dcc_get_host(tgt_ip, 3, &error)) {
+			/* prefer an IPv4 target address */
+			if (dcc_hostaddrs[0].sa.sa_family == AF_INET) {
+				tgt_use_ipv6 = 0;
+				dcc_ipv4toipv6(&tgt,
+					       dcc_hostaddrs[0].ipv4.sin_addr);
+			} else {
+				tgt_use_ipv6 = 1;
+				tgt = dcc_hostaddrs[0].ipv6.sin6_addr;
+			}
+			dcc_host_unlock();
+			dcc_bits2mask(&tgt_mask, 128);
+
+		} else {
+			dcc_host_unlock();
+			dcc_pemsg(EX_NOHOST, emsg,
+				  "invalid DNSBL target IP address \"%s\": %s",
+				  tgt_ip, DCC_HSTRERROR(error));
+			return 0;
+		}
+	}
+
+	if (bl_dom_len >= ISZ(DNSBL_DOM) - INET6_ADDRSTRLEN*2) {
+		dcc_host_unlock();
+		/* we cannot fit the DNSBL base and the target name or address
+		 * into blw->probe.  We need to do DNS lookups of names
+		 * like 33.22.11.10.dnsbl.example.com or
+		 * domain.dom.bl.example.com */
+		dcc_pemsg(EX_NOHOST, emsg, "DNSBL name \"%s\" too long", entry);
+		return 0;
+	}
+
+	dp = dcc_malloc(sizeof(*dp));
+	memset(dp, 0, sizeof(*dp));
+
+	dp->tgt_use_ipv6 = tgt_use_ipv6;
+	dp->tgt = tgt;
+	dp->tgt_mask = tgt_mask;
+
+	dp->bl_type = bl_type;
+	dp->fgs = cur_fgs;
+	dp->reply = cur_reply;
+	memcpy(&dp->bl_dom.c, entry, bl_dom_len);
+	dp->bl_dom_len = bl_dom_len+1;	/* count trailing '\0' */
+	dp->bl_num = ++bl_num;
+	dp->group = cur_group;
+	gbit = DNSBL_G2B(dp->group);
+	dnsbl_groups.all |= gbit;
+	if (cur_fgs & DNSBL_FG_CLIENT)
+		dnsbl_groups.client |= gbit;
+	if (cur_fgs & DNSBL_FG_MAIL_HOST) {
+		dnsbl_groups.mail_host |= gbit;
+		if (cur_fgs & DNSBL_FG_MX)
+			dnsbl_groups.mail_host_mx |= gbit;
+		if (cur_fgs & DNSBL_FG_NS)
+			dnsbl_groups.mail_host_ns |= gbit;
+	}
+	if (cur_fgs & DNSBL_FG_URL) {
+		dnsbl_groups.url |= gbit;
+		if (cur_fgs & DNSBL_FG_MX)
+			dnsbl_groups.url_mx |= gbit;
+		if (cur_fgs & DNSBL_FG_NS)
+			dnsbl_groups.url_ns |= gbit;
+	}
+
+	/* link the DNSBLs in the order they are declared so that they
+	 * can be prioritized */
+	dpp = &dnsbls;
+	for (;;) {
+		dp1 = *dpp;
+		if (!dp1) {
+			*dpp = dp;
+			break;
+		}
+		/* notice sufficiently duplicate DNSBLS */
+		if (!(dp->fgs & DNSBL_FG_DUP) && !dp1->dup
+		    && dp->tgt_use_ipv6 == dp1->tgt_use_ipv6
+		    && dp->bl_type == dp1->bl_type
+		    && dp->bl_dom_len == dp1->bl_dom_len
+		    && !memcmp(dp->bl_dom.c, dp1->bl_dom.c, dp->bl_dom_len)) {
+			dp->fgs |= DNSBL_FG_DUP;
+			dp1->dup = dp;
+		}
+		dpp = &dp1->fwd;
+	}
+
+	SAVE_ARG(entry);
+	return 1;
+#undef SAVE_ARG
+}
+
+
+
+/* resolve inconsistencies among the -B parameters */
+static inline void
+fix_url_secs(void)
+{
+	if (url_secs > msg_secs)
+		url_secs =  msg_secs;
+	msg_us = msg_secs * DCC_US;
+	url_us = url_secs * DCC_US;
+}
+
+
+
+static char *
+type_str(char *buf, int buf_len, const DNSBL_WORK *blw, DNSBL_FGS fgs)
+{
+	const char *type;
+	char sustr[DCC_SU2STR_SIZE];
+	int i;
+
+	switch (fgs & DNSBL_FG_TYPES) {
+	case DNSBL_FG_CLIENT:
+		type = "SMTP client";
+		if (blw->tgt.dom.c[0] == '\0') {
+			snprintf(buf, buf_len, "%s %s",
+				 type, dcc_ipv6tostr2(sustr, sizeof(sustr),
+						      &blw->tgt.addr));
+			return buf;
+		}
+		break;
+	case DNSBL_FG_MAIL_HOST:
+		type = "mail_host";
+		break;
+	case DNSBL_FG_URL:
+		type = "URL";
+		break;
+	case DNSBL_FG_MX | DNSBL_FG_MAIL_HOST :
+		type = "mail_host MX";
+		break;
+	case DNSBL_FG_MX | DNSBL_FG_URL:
+		type = "URL MX";
+		break;
+	case DNSBL_FG_NS | DNSBL_FG_MAIL_HOST:
+		type = "mail_host NS";
+		break;
+	case DNSBL_FG_NS | DNSBL_FG_URL:
+		type = "URL NS";
+		break;
+
+	default:
+		dcc_logbad(EX_SOFTWARE, "impossible DNSBL hit type %#x", fgs);
+		break;
+	}
+
+	i = snprintf(buf, buf_len, "%s %s", type, blw->tgt.dom.c);
+	if (i >= buf_len && buf_len > 4)
+		strcpy(&buf[buf_len-4], "...");
+	return buf;
+}
+
+
+
+static void PATTRIB(4,5)
+dnsbl_log(const DNSBL_WORK *blw, const DNSBL_GROUP *blg, DNSBL_FGS fgs,
+	  const char *pat, ...)
+{
+	char type_buf[sizeof(blg->btype_buf)];
+	const char *type0, *type1;
+	char gbuf[8];
+	const char *gnum;
+	char msg[256];
+	va_list args;
+
+	va_start(args, pat);
+	vsnprintf(msg, sizeof(msg), pat, args);
+	va_end(args);
+
+	if (fgs) {
+		type0 = " ";
+		type1 = type_str(type_buf, sizeof(type_buf), blw, fgs);
+	} else {
+		type0 = "";
+		type1 = "";
+	}
+	if (!have_dnsbl_groups) {
+		gnum = "";
+	} else if (!blg) {
+		gnum = "*";
+	} else {
+		snprintf(gbuf, sizeof(gbuf), "%d",
+			 (int)(blg - blw->groups)+1);
+		gnum = gbuf;
+	}
+
+	if (helper.debug) {
+		if (dcc_no_syslog)
+			thr_trace_msg(blw->log_ctxt, "DNSBL%s%s%s%s %s",
+				      gnum, helper_str, type0, type1, msg);
+		else
+			thr_trace_msg(blw->log_ctxt, "%s DNSBL%s%s%s%s %s",
+				      blw->id,
+				      gnum, helper_str, type0, type1, msg);
+	} else {
+		thr_log_print(blw->log_ctxt, 1, "DNSBL%s%s%s%s %s\n",
+			      helper_str, gnum, type0, type1, msg);
+	}
+}
+
+
+
+void
+dcc_dnsbl_result(ASK_ST *ask_stp, DNSBL_WORK *blw)
+{
+	DNSBL *dp;
+	DNSBL_GROUP *blg;
+	int gnum;
+
+	if (!blw)
+		return;
+
+	for (gnum = 0, blg = blw->groups;
+	     gnum < MAX_DNSBL_GROUPS;
+	     ++gnum, ++blg) {
+		if (blg->fgs & DNSBL_FG_HITS) {
+			*ask_stp |= (ASK_ST_DNSBL_HIT(gnum) | ASK_ST_LOGIT);
+			dnsbl_log(blw, blg, 0, "%s %s=%s",
+				  blg->btype, blg->probe.c, blg->result);
+		} else if (blg->fgs & DNSBL_FG_TIMEO) {
+			*ask_stp |= ASK_ST_DNSBL_TIMEO(gnum);
+			for (dp = dnsbls; dp; dp = dp->fwd) {
+				if (dp->group != gnum)
+					continue;
+				if (dp->fgs & DNSBL_FG_TFAIL) {
+					*ask_stp |= ASK_ST_DNSBL_TFAIL(gnum);
+					break;
+				}
+			}
+		}
+	}
+}
+
+
+
+/* There are several timing requirements:
+ *	- Do not spend too much time on any single URL or envelope value.
+ *	- At helper.debug >=1, log the first DNSBL check in a group
+ *	    unfinished for lack of time.  Also log checks for single URLs that
+ *	    are partially unfinished.
+ *	- At helper.debug >=2, log things that take a long time
+ *	- Minimize log messages, because the noise can be deafening.
+ *	- Mark unfinished groups */
+
+
+/* A check has been abandoned or timed out. */
+static void
+set_timeo(DNSBL_WORK *blw, DNSBL_FGS fgs)
+{
+	DNSBL *dp;
+	DNSBL_GROUP *blg;
+	int gnum;
+
+	/* groups that are marked as having suffered timeouts can be
+	 * hit by later URLs */
+	if (fgs == 0) {
+		/* mark all groups if we don't know the type of request */
+		for (gnum = 0, blg = blw->groups;
+		     gnum < MAX_DNSBL_GROUPS;
+		     ++gnum, ++blg) {
+			/* ignore groups that have hit or timed out */
+			if (!(blw->unhit.all & DNSBL_G2B(gnum))
+			    || (blg->fgs & DNSBL_FG_TIMEO))
+				continue;
+			blg->fgs |= DNSBL_FG_TIMEO;
+		}
+
+	} else {
+		/* mark only groups that still care about this type */
+		for (dp = dnsbls; dp; dp = dp->fwd) {
+			/* this DNSBL does not care about this type
+			 * or the group to which it belongs has been hit */
+			if (!(dp->fgs & fgs & DNSBL_FG_HITS)
+			    || !(blw->unhit.all & DNSBL_G2B(dp->group)))
+				continue;
+			blw->groups[dp->group].fgs |= DNSBL_FG_TIMEO;
+		}
+	}
+
+	/* no complaint if time remains */
+	if (blw->url_us >= blw->url_us_used)
+		return;
+
+	/* no more log messages if everything has been complained about */
+	if (blw->url_us < 0 || blw->msg_us < 0)
+		return;
+
+	/* only one final message/URL */
+	blw->url_us = -1;
+	if (helper.debug < 2)
+		return;
+
+	if (is_helper) {
+		/* Messages from the helper process go to the system
+		 * log but not the per-message log file.
+		 * The helper works on a single URL or envelope value
+		 * and so does not know about the time limit for the
+		 * entire message. */
+		dnsbl_log(blw, 0, fgs, "failed after %.1f url_secs used",
+			  blw->url_us_used / (DCC_US*1.0));
+
+	} else if (blw->msg_us > blw->url_us_used) {
+		/* time remains for later URLs */
+		dnsbl_log(blw, 0, fgs,
+			  "failed after using %.1f url_secs;"
+			  " %.1f msg-secs remain",
+			  blw->url_us_used / (DCC_US*1.0),
+			  (blw->msg_us - blw->url_us_used) / (DCC_US*1.0));
+
+	} else {
+		dnsbl_log(blw, 0, fgs, "failed after using %.1f sec",
+			  blw->url_us_used / (DCC_US*1.0));
+	}
+}
+
+
+
+/* see if we are out of time before doing something */
+static inline u_char			/* 0=too late */
+time_ck_pre(DNSBL_WORK *blw, DNSBL_FGS fgs)
+{
+	/* don't worry if there is plenty of time */
+	if (blw->url_us >= blw->url_us_used)
+		return 1;
+
+	/* There is no more time. Ether previous operations succeeded slowly
+	 * or failed and were logged.
+	 * In the first case, log this operation and mark the groups. */
+	set_timeo(blw, fgs);
+	return 0;
+}
+
+
+
+/* see if we ran out of time after doing something */
+static u_char				/* 0=out of time */
+time_ck_post(DNSBL_WORK *blw, DNSBL_FGS fgs,
+	     u_char timedout)		/* 1=operation timed out */
+{
+	struct timeval now;
+	time_t used_us;
+
+	if (blw->url_us <= 0)
+		return 0;			/* previously out of time */
+
+	gettimeofday(&now, 0);
+	used_us = tv_diff2us(&now, &blw->url_start);
+
+	if (blw->url_us >= used_us && !timedout) {
+		if (helper.debug > 1
+		    && (used_us-blw->url_us_used) > url_us/4)
+			dnsbl_log(blw, 0, fgs, "%s after using %.1f url_secs",
+				  timedout ? "failed" : "succeeded",
+				  (used_us - blw->url_us_used) / (DCC_US*1.0));
+		blw->url_us_used = used_us;
+		return 1;
+	}
+
+	blw->url_us_used = used_us;
+	set_timeo(blw, fgs);
+	return 0;
+}
+
+
+
+/* start timer before we start to check something in the DNS blacklists
+ *	give up if we are already out of time */
+static u_char				/* 0=already too much time spent */
+msg_secs_start(DNSBL_WORK *blw, DNSBL_FGS fgs)
+{
+	blw->url_us = url_us;
+	blw->url_us_used = 0;
+
+	if (blw->msg_us <= 0) {
+		if (blw->msg_us == 0) {
+			/* out of time for next URL before we start it */
+			if (helper.debug)
+				dnsbl_log(blw, 0, fgs,
+					  "%d msg-secs already exhausted",
+					  msg_secs);
+			blw->msg_us = -1;   /* only one log message */
+			blw->url_us = -1;
+		}
+		/* mark the groups but do not log anything */
+		set_timeo(blw, fgs);
+		return 0;
+	}
+
+	gettimeofday(&blw->url_start, 0);
+	return 1;
+}
+
+
+
+/* account for time used */
+static void
+msg_secs_fin(DNSBL_WORK *blw)
+{
+	if (blw->msg_us < 0)
+		return;			/* prevously out of time */
+
+	blw->msg_us -= blw->url_us_used;
+	if (blw->msg_us > 0)
+		return;
+
+	if (blw->url_us >= blw->url_us_used) {
+		/* The clock had no time for more DNS work for this
+		 * name or address, but we finished and so it might
+		 * not matter.
+		 * Ensure a log message on the next check, if any */
+		blw->msg_us = 0;
+	}
+}
+
+
+
+#ifndef HAVE__RES
+static void
+dnsbl_res_delays(const DNSBL_WORK *blw UATTRIB)
+{
+	return;
+}
+#else
+/* Limit resolver delays to as much as we are willing to wait
+ *	We should be talking to a local caching resolver.  If it does not answer
+ *	immediately, it is unlikely to later.  If it does eventually get an
+ *	answer, the answer will probably ready the next time we ask.
+ *  dcc_host_lock() must be held. */
+static void
+dnsbl_res_delays(const DNSBL_WORK *blw)
+{
+	static int init_res_retrans;
+	static int init_res_retry;
+	int budget;			/* seconds we can afford */
+	int res_retrans;		/* retransmition delay */
+	int res_retry;			/* # of retransmissions */
+	int total;			/* retrans*retry = worst case delay */
+
+	/* get the current value */
+	if (!_res.options & RES_INIT) {
+		res_init();
+		init_res_retry = _res.retry;
+		if (!init_res_retry)
+			init_res_retry = 4;
+		init_res_retrans = _res.retrans;
+		if (!init_res_retrans)
+			init_res_retrans = RES_TIMEOUT;
+	}
+	res_retry = init_res_retry;
+	res_retrans = init_res_retrans;
+
+	/* assume binary exponential backoffs as in the BIND resolver */
+	total = ((1<<res_retry) -1) * res_retrans;
+
+	/* If the default values could take too long, then try 2 seconds.
+	 * If that is still too long, go to 1 retransmission and an initial
+	 * retransmission delay of 1/3 of the total allowed delay.
+	 * 1/3 from one exponential backoff for a total of 2**2-1=3 times
+	 * the initial delay.
+	 * We should be using a local caching DNS server and so should not
+	 * see many lost packets */
+	budget = (blw->url_us - blw->url_us_used + DCC_US/2) / DCC_US;
+	if (budget < 1)
+		budget = 1;
+
+	if (total >= budget) {
+		res_retry = 2;		/* 2 retries are often few enough */
+		total = ((1<<res_retry) -1) * res_retrans;
+
+		/* if that is not few enough,
+		 * then reduce the retransmission delay to fit */
+		if (total >= budget) {
+			res_retrans = budget/3;
+			if (res_retrans == 0)
+				res_retrans = 1;
+		}
+	}
+
+	if (_res.retry != res_retry
+	    || _res.retrans != res_retrans) {
+		_res.retry = res_retry;
+		_res.retrans = res_retrans;
+		if (helper.debug > 4)
+			dnsbl_log(blw, 0, 0,
+				  "budget=%d  _res.retry=%d"
+				  " _res.retrans=%d seconds",
+				  budget, res_retry, res_retrans);
+	}
+}
+#endif /* !HAVE__RES */
+
+
+
+static inline void
+blw_clear(DNSBL_WORK *blw)
+{
+	DNSBL_GROUP *blg;
+
+	blw->tgt.dom.c[0] = '\0';
+	blw->tgt_dom_len = 0;
+	for (blg = blw->groups; blg <= LAST(blw->groups); ++blg) {
+		blg->fgs = 0;
+		blg->btype = 0;
+		blg->result[0] = '\0';
+		blg->tgt.c[0] = '\0';
+		blg->probe.c[0] = '\0';
+	}
+}
+
+
+
+/* get ready to handle a mail message */
+void
+dcc_dnsbl_init(DCC_GOT_CKS *cks,
+	       DCC_CLNT_CTXT *dcc_ctxt, void *log_ctxt, const char *id)
+{
+	DNSBL_WORK *blw;
+	int i;
+
+	if (!dnsbls)
+		return;
+
+	blw = cks->dnsbl;
+	if (!blw) {
+		blw = dcc_malloc(sizeof(*blw));
+		memset(blw, 0, sizeof(*blw));
+		cks->dnsbl = blw;
+
+		/* general initializations on the first use of DNS blacklists */
+		fix_url_secs();
+	}
+
+	blw_clear(blw);
+	blw->msg_us = msg_us;
+	blw->id = id;
+	blw->dcc_ctxt = dcc_ctxt;
+	blw->log_ctxt = log_ctxt;
+	blw->unhit = dnsbl_groups;
+	for (i = 0; i < DIM(blw->tgt_cache); ++i)
+		blw->tgt_cache[i].dom.c[0] = '\0';
+	blw->tgt_cache_pos = 0;
+}
+
+
+
+/* look for a host name or IP address in a DNS blacklist and its duplicates
+ *	and any hit groups of DNSBLs.
+ *	These DNS operations should be done with local default values for
+ *	RES_DEFNAMES, RES_DNSRCH, and RES_NOALIASES because the blacklist
+ *	might be something local and strange. */
+static u_char				/* 0=no more time or blacklists */
+lookup(DNSBL_WORK *blw,
+       const DNSBL_DOM *probe,		/* look for this */
+       const DNSBL *dp,			/* check this blacklist & its dups */
+       DNSBL_FGS fgs,			/* type of lookup */
+       const void *tgt)			/* IP address or whatever for tracing */
+{
+#	define NUM_SUSTRS 3
+	char sustrs[DCC_SU2STR_SIZE*NUM_SUSTRS+1+sizeof("...")];
+	const DCC_SOCKU *hap;
+	struct in6_addr addr6;
+	const struct in6_addr *addr6p;
+	int error;
+	DNSBL_GROUP *blg;
+	DNSBL_GBITS gbit;
+	u_char hit;
+	char *p;
+	int i;
+
+	if (!time_ck_pre(blw, fgs))
+		return 0;
+
+	/* resolve a list of IP addresses for the probe in the DNSBL */
+	dcc_host_lock();
+	dnsbl_res_delays(blw);
+	if (!dcc_get_host(probe->c, dp->tgt_use_ipv6, &error)) {
+		dcc_host_unlock();
+		if (helper.debug > 1)
+			dnsbl_log(blw, 0, fgs, "gethostbyname(%s): %s",
+				  probe->c, DCC_HSTRERROR(error));
+		return time_ck_post(blw, fgs, error == TRY_AGAIN);
+	}
+
+	/* check each address obtained for the probe in the DNSBL
+	 * for a hit in the list of duplicate references to this DNSBL */
+	hit = 0;
+	do {
+		/* skip this reference if it is for the wrong type of DNSBL */
+		if ((dp->fgs & fgs) != fgs)
+			continue;
+		/* skip this reference if the containing group has a hit */
+		blg = &blw->groups[dp->group];
+		if (blg->fgs & DNSBL_FG_HITS)
+			continue;
+		/* check all of the addresses for a hit with this reference */
+		for (hap = dcc_hostaddrs; hap < dcc_hostaddrs_end; ++hap) {
+			/* finished if any answer is good enough
+			 * or if we get a match */
+			if (hap->sa.sa_family == AF_INET6) {
+				addr6p = &hap->ipv6.sin6_addr;
+			} else {
+				addr6p = &addr6;
+				dcc_ipv4toipv6(&addr6, hap->ipv4.sin_addr);
+			}
+			if (!DCC_IN_BLOCK(*addr6p, dp->tgt, dp->tgt_mask))
+				continue;
+
+			/* got a hit */
+			blg->dnsbl = dp;
+			blg->fgs = fgs;
+			if (dp->bl_type == DNSBL_TYPE_IPV4)
+				dcc_ipv4tostr(blg->tgt.c, sizeof(blg->tgt),
+					      tgt);
+			else if (dp->bl_type == DNSBL_TYPE_IPV6)
+				dcc_ipv6tostr(blg->tgt.c, sizeof(blg->tgt),
+					      tgt);
+			else if (dp->bl_type == DNSBL_TYPE_NAME)
+				BUFCPY(blg->tgt.c, tgt);
+			blg->btype = type_str(blg->btype_buf,
+					      sizeof(blg->btype_buf),
+					      blw, blg->fgs);
+			BUFCPY(blg->probe.c, probe->c);
+			dcc_su2str2(blg->result, sizeof(blg->result), hap);
+			if (helper.debug > 1 && (is_helper || !have_helpers))
+				dnsbl_log(blw, blg, fgs, "hit %s=%s",
+					  probe->c,
+					  dcc_su2str2(sustrs, sizeof(sustrs),
+						      hap));
+			gbit = DNSBL_G2B(dp->group);
+			if (fgs & DNSBL_FG_CLIENT)
+				blw->unhit.client &= ~gbit;
+			if (fgs & DNSBL_FG_MAIL_HOST) {
+				blw->unhit.mail_host &= ~gbit;
+				if (fgs & DNSBL_FG_MX)
+					blw->unhit.mail_host_mx &= ~gbit;
+				if (fgs & DNSBL_FG_NS)
+					blw->unhit.mail_host_ns &= ~gbit;
+			}
+			if (fgs & DNSBL_FG_URL) {
+				blw->unhit.url &= ~gbit;
+				if (fgs & DNSBL_FG_MX)
+					blw->unhit.url_mx &= ~gbit;
+				if (fgs & DNSBL_FG_NS)
+					blw->unhit.url_ns &= ~gbit;
+			}
+			blw->unhit.all &= ~gbit;
+			if (!blw->unhit.all) {
+				dcc_host_unlock();
+				time_ck_post(blw, fgs, 0);
+				return 0;
+			}
+			hit = 1;
+			break;
+		}
+
+		/* check the results in duplicate references to the DNSBL */
+	} while ((dp = dp->dup) != 0);
+
+	if (hit || helper.debug < 2) {
+		dcc_host_unlock();
+		return time_ck_post(blw, fgs, 0);
+	}
+
+	p = sustrs;
+	i = 0;
+	do {
+		dcc_su2str2(p, DCC_SU2STR_SIZE, &dcc_hostaddrs[i]);
+		p += strlen(p);
+		*p++ = ' ';
+		*p = '\0';
+		if (p >= &sustrs[DCC_SU2STR_SIZE*NUM_SUSTRS]) {
+			strcpy(p, "...");
+			break;
+		}
+	} while (dcc_hostaddrs_end > &dcc_hostaddrs[++i]);
+	dcc_host_unlock();
+	dnsbl_log(blw, 0, fgs, "miss; gethostbyname(%s)=%s", probe->c, sustrs);
+	return time_ck_post(blw, fgs, 0);
+
+#undef NUM_SUSTRS
+}
+
+
+
+/* check one IP address against the DNS blacklists */
+static u_char				/* 0=no more time or blacklists */
+ip_lookup(DNSBL_WORK *blw,
+	  DNSBL_TYPE tgt_type,
+	  const void *tgt,		/* in_addr* or in6_addr*, not aligned */
+	  DNSBL_FGS fgs)		/* type of lookup */
+{
+	const DNSBL *dp;
+	const u_char *bp;
+	DNSBL_DOM probe;
+
+	/* check all DNSBLs for a group without a hit */
+	for (dp = dnsbls; dp; dp = dp->fwd) {
+		if (dp->bl_type != tgt_type)
+			continue;
+		if ((dp->fgs & fgs) != fgs)
+			continue;
+		if (dp->fgs & DNSBL_FG_DUP)
+			continue;
+
+		if (!(blw->unhit.all & DNSBL_G2B(dp->group)))
+			continue;
+
+		bp = (u_char *)tgt;
+		if (tgt_type == DNSBL_TYPE_IPV4)
+			snprintf(probe.c, sizeof(probe.c),
+				 "%d.%d.%d.%d.%s",
+				 bp[3], bp[2], bp[1], bp[0],
+				 dp->bl_dom.c);
+		else
+			snprintf(probe.c, sizeof(probe.c),
+				 "%d.%d.%d.%d.%d.%d.%d.%d"
+				 ".%d.%d.%d.%d.%d.%d.%d.%d.%s",
+				 bp[15], bp[14], bp[13], bp[12],
+				 bp[11], bp[10], bp[9], bp[8],
+				 bp[7], bp[6], bp[5], bp[4],
+				 bp[3], bp[2], bp[1], bp[0],
+				 dp->bl_dom.c);
+
+		if (!lookup(blw, &probe, dp, fgs, tgt))
+			return 0;
+	}
+	return 1;
+}
+
+
+
+/* convert a name to one or more IP addresses to be checked in a DNS blacklist.
+ *  dcc_host_lock() must be held.
+ *	These DNS operations need RES_DEFNAMES and RES_DNSRCH off and
+ *	RES_NOALIASES on when the name is an MX or NS server name.
+ */
+static u_char				/* 0=failed */
+dnsbl_get_host(DNSBL_WORK *blw,
+	       const DNSBL_DOM *dom,
+	       u_char use_ipv6,
+	       int *errorp,		/* put error number here */
+	       DNSBL_FGS fgs		/* 0, DNSBL_FG_MX, or DNSBL_FG_NS */
+#ifndef MXNS_DNSBL
+	       UATTRIB
+#endif
+	       )
+{
+#ifdef MXNS_DNSBL
+	u_long save_options;
+#endif
+	u_char result;
+
+#ifdef MXNS_DNSBL
+	save_options = _res.options;
+	if (fgs & (DNSBL_FG_MX | DNSBL_FG_NS)) {
+		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);
+		_res.options |= RES_NOALIASES;
+	}
+#endif
+	dnsbl_res_delays(blw);
+	result = dcc_get_host(dom->c, use_ipv6, errorp);
+#ifdef MXNS_DNSBL
+	if (fgs & (DNSBL_FG_MX | DNSBL_FG_NS))
+		_res.options = save_options;
+#endif
+	return result;
+}
+
+
+
+/* look for a domain name in the DNS blacklists */
+static u_char				/* 0=no more time or blacklists */
+name_lookup(DNSBL_WORK *blw,
+	    const DNSBL_DOM *tgt,
+	    DNSBL_FGS fgs)		/* type of lookup */
+{
+	const DNSBL *dp;
+	DNSBL_DOM probe;
+	const char *p;
+	int tgt_len, i;
+
+	if (!have_name_dnsbl)
+		return 1;
+
+	for (dp = dnsbls; dp; dp = dp->fwd) {
+		if (dp->bl_type != DNSBL_TYPE_NAME)
+			continue;
+		if ((dp->fgs & fgs) != fgs)
+			continue;
+		if (dp->fgs & DNSBL_FG_DUP)
+			continue;
+
+		if (!(blw->unhit.all & DNSBL_G2B(dp->group)))
+			continue;
+
+		/* trim trailing '.' from names */
+		p = tgt->c;
+		tgt_len = strlen(p);
+		if (tgt_len > 0 && p[tgt_len-1] == '.')
+			--tgt_len;
+
+		if (tgt_len != 0) {	/* handle empty name */
+			/* truncate long names on the left and complain */
+			i = (tgt_len + dp->bl_dom_len) - (sizeof(probe.c) - 1);
+			if (i > 0) {
+				if (helper.debug)
+					dnsbl_log(blw, 0, 0,
+						  "target \"%s\""
+						  " is %d bytes too long",
+						  tgt->c, i);
+				p += i;
+				tgt_len -= i;
+			}
+			memcpy(&probe.c[0], p, tgt_len);
+			probe.c[tgt_len++] = '.';
+		}
+		memcpy(&probe.c[tgt_len], dp->bl_dom.c, dp->bl_dom_len);
+
+		if (!lookup(blw, &probe, dp, fgs, tgt))
+			return 0;
+	}
+
+	return 1;
+}
+
+
+
+/* look for a domain name and its IP addresses in the DNS blacklists */
+static u_char				/* 0=no more time or blacklists */
+name_ip_lookup(DNSBL_WORK *blw,
+	       const DNSBL_DOM *tgt,
+	       DNSBL_FGS fgs)		/* type of lookup */
+{
+	const DCC_SOCKU *sup;
+	struct in_addr ipv4[8];
+	struct in6_addr ipv6[4];
+	int i, error;
+
+	/* check the name */
+	if (!name_lookup(blw, tgt, fgs))
+		return 0;
+
+	/* cannot resolve a null name into an address to try in the DNSBLs */
+	if (tgt->c[0] == '\0')
+		return 1;
+
+	if (!time_ck_pre(blw, fgs))
+		return 0;
+
+	/* check IPv4 addresses for the name in IPv4 DNSBLs */
+	if (have_ipv4_dnsbl) {
+		dcc_host_lock();
+		/* first resolve IPv4 addresses for the URL or client name */
+		if (!dnsbl_get_host(blw, tgt, 0, &error, fgs)) {
+			dcc_host_unlock();
+			if (helper.debug > 1)
+				dnsbl_log(blw, 0, fgs, "gethostbyname(%s): %s",
+					  tgt->c, DCC_HSTRERROR(error));
+			if (!time_ck_post(blw, fgs, error == TRY_AGAIN))
+				return 0;
+		} else {
+			/* Try several of the IP addresses for the domain.
+			 * Save any IP addresses we want to check before we
+			 * check them, because checking changes the array
+			 * of addresses. */
+			for (sup = dcc_hostaddrs, i = 0;
+			     sup < dcc_hostaddrs_end && i < DIM(ipv4);
+			     ++sup, ++i) {
+				ipv4[i] = sup->ipv4.sin_addr;
+			}
+			dcc_host_unlock();
+			if (!time_ck_post(blw, fgs, 0))
+				return 0;
+			/* check the addresses in all of the DNS blacklists */
+			do {
+				if (!ip_lookup(blw, DNSBL_TYPE_IPV4,
+					      &ipv4[--i], fgs))
+					return 0;
+			} while (i > 0);
+		}
+	}
+
+	/* finally try IPv6 addresses for the name */
+	if (have_ipv6_dnsbl) {
+		dcc_host_lock();
+		if (!dnsbl_get_host(blw, tgt, 1, &error, fgs)) {
+			dcc_host_unlock();
+			if (helper.debug > 1)
+				dnsbl_log(blw, 0, fgs, "gethostbyname(%s): %s",
+					  tgt->c, DCC_HSTRERROR(error));
+			if (!time_ck_post(blw, fgs, error == TRY_AGAIN))
+				return 0;
+		} else {
+			for (sup = dcc_hostaddrs, i = 0;
+			     sup < dcc_hostaddrs_end && i < DIM(ipv6);
+			     ++sup, ++i) {
+				ipv6[i] = sup->ipv6.sin6_addr;
+			}
+			dcc_host_unlock();
+			if (!time_ck_post(blw, fgs, 0))
+				return 0;
+			do {
+				if (!ip_lookup(blw, DNSBL_TYPE_IPV6,
+					       &ipv4[--i], fgs))
+					return 0;
+			} while (i > 0);
+		}
+	}
+
+	return 1;
+}
+
+
+
+/* check an SMTP client */
+static void
+dnsbl_client(DNSBL_WORK *blw)
+{
+	struct in_addr ipv4;
+
+	if (blw->tgt.dom.c[0] != '\0'
+	    && blw->tgt.dom.c[0] != '[')
+		name_lookup(blw, &blw->tgt.dom, DNSBL_FG_CLIENT);
+
+	if (have_ipv4_dnsbl
+	    && dcc_ipv6toipv4(&ipv4, &blw->tgt.addr)) {
+		if (!ip_lookup(blw, DNSBL_TYPE_IPV4,
+			       &ipv4, DNSBL_FG_CLIENT))
+			return;
+	}
+
+	if (have_ipv6_dnsbl
+	    && !ip_lookup(blw, DNSBL_TYPE_IPV6,
+			  &blw->tgt.addr, DNSBL_FG_CLIENT))
+		return;
+}
+
+
+
+
+#ifdef MXNS_DNSBL
+/* look at DNS resource records */
+static u_char				/* 1=continue 0=stop looking */
+rr_lookup(DNSBL_WORK *blw,
+	  const char *name,		/* see if this domain is blacklisted */
+	  DNSBL_FGS fgs,
+	  u_short req_type)		/* T_A, T_AAAA, or T_MX */
+{
+	union {
+	    u_char  buf[PACKETSZ+20];
+	    HEADER  hdr;
+	} answer;
+	DNSBL_DOM dom;
+	const u_char *ap, *ap1, *eom;
+	int cnt, skip;
+	u_short resp_type;
+	u_short resp_class, rdlength;
+	int i;
+
+	if (!time_ck_pre(blw, fgs))
+		return 0;
+
+	/* resolve a set of RRs of the desired type for a name */
+	dnsbl_res_delays(blw);
+	dcc_host_lock();
+	i = res_query(name, C_IN, req_type, answer.buf, sizeof(answer.buf));
+	dcc_host_unlock();
+	if (i < 0) {
+		/* use raw hstrerror() here because we are using the
+		 * raw resolver */
+		if (helper.debug > 1)
+			dnsbl_log(blw, 0, fgs, "res_query(%s): %s",
+				  name, hstrerror(h_errno));
+		/* stop looking after too much time or NXDOMAIN */
+		return (time_ck_post(blw, fgs, h_errno == TRY_AGAIN)
+			&& h_errno != HOST_NOT_FOUND);
+	}
+	if (!time_ck_post(blw, fgs, 0))
+		return 0;
+
+	ap = &answer.buf[HFIXEDSZ];
+	if (i > ISZ(answer.buf))
+		i = ISZ(answer.buf);
+	eom = &answer.buf[i];
+
+	/* skip the question */
+	cnt = ntohs(answer.hdr.qdcount);
+	while (--cnt >= 0) {
+		skip = dn_skipname(ap, eom);
+		if (skip < 0) {
+			if (helper.debug > 1)
+				dnsbl_log(blw, 0, fgs, "dn_skipname(%s)=%d",
+					  name, skip);
+			/* look for other RRs */
+			return 1;
+		}
+		ap += skip+QFIXEDSZ;
+	}
+
+
+	/* check each name or address RR in the answer section */
+	for (cnt = ntohs(answer.hdr.ancount);
+	     --cnt >= 0 && ap < eom;
+	     ap += rdlength) {
+		/* get the name */
+		skip = dn_expand(answer.buf, eom, ap, dom.c, sizeof(dom.c));
+		if (skip < 0) {
+			if (helper.debug > 1)
+				dnsbl_log(blw, 0, fgs,
+					  "answer dn_expand(%s)=%d",
+					  name, skip);
+			return 1;
+		}
+		ap += skip;
+
+		/* get RR type and class and skip strange RRs */
+		GETSHORT(resp_type, ap);
+		GETSHORT(resp_class, ap);
+		ap += 4;		/* skip TTL */
+		GETSHORT(rdlength, ap);	/* get rdlength */
+
+		/* we care only about relevant answers */
+		if (resp_type != req_type
+		    || resp_class != C_IN)
+			continue;
+
+		if (req_type == T_MX) {
+			/* check MX name */
+			ap1 = ap + 2;	/* skip MX preference */
+			skip = dn_expand(answer.buf, eom, ap1,
+					 dom.c, sizeof(dom.c));
+			if (skip < 0) {
+				if (helper.debug > 1)
+					dnsbl_log(blw, 0, fgs,
+						  "MX dn_expand(%s)=%d",
+						  name, skip);
+				return 1;
+			}
+
+			if (dom.c[0] == '\0'
+			    && helper.debug > 1)
+				dnsbl_log(blw, 0, fgs, "null MX name");
+
+			if (!name_ip_lookup(blw, &dom, fgs))
+				return 0;
+
+		} else if (req_type == T_A) {
+			if (!ip_lookup(blw, DNSBL_TYPE_IPV4, ap, fgs))
+				return 0;
+#ifndef NO_IPV6
+		} else if (req_type == T_AAAA) {
+			if (!ip_lookup(blw, DNSBL_TYPE_IPV6, ap, fgs))
+				return 0;
+#endif
+		}
+	}
+
+	/* check the authority section only if we care about name servers
+	 * and we are not looking for MX servers */
+	if ((fgs & DNSBL_FG_MX)
+	    || 0 == ((fgs & DNSBL_FG_URL) ? blw->unhit.url_ns
+		     : blw->unhit.mail_host_ns))
+		return 1;
+
+	/* we could look at the additional section, but it can be incomplete */
+	fgs |= DNSBL_FG_NS;
+	for (cnt = ntohs(answer.hdr.nscount);
+	     --cnt >= 0 && ap < eom;
+	     ap += rdlength) {
+		/* get the name */
+		skip = dn_expand(answer.buf, eom, ap, dom.c, sizeof(dom.c));
+		if (skip < 0) {
+			if (helper.debug > 1)
+				dnsbl_log(blw, 0, fgs,
+					  "ns dn_expand(%s)=%d",
+					  name, skip);
+			return 1;
+		}
+		ap += skip;
+
+		/* get RR type and class */
+		GETSHORT(resp_type, ap);
+		GETSHORT(resp_class, ap);
+		ap += 4;		/* skip TTL */
+		GETSHORT(rdlength, ap);	/* get rdlength */
+
+		/* we care only about NS RRs */
+		if (resp_type != T_NS
+		    || resp_class != C_IN)
+			continue;
+
+		skip = dn_expand(answer.buf, eom, ap, dom.c, sizeof(dom.c));
+		if (skip < 0) {
+			if (helper.debug > 1)
+				dnsbl_log(blw, 0, fgs,
+					  "ns answer dn_expand(%s)=%d",
+					  name, skip);
+			return 1;
+		}
+
+		if (dom.c[0] == '\0'
+		    && helper.debug > 1)
+			dnsbl_log(blw, 0, fgs, "null NS name");
+
+		if (!name_ip_lookup(blw, &dom, fgs))
+			return 0;
+	}
+
+	return 1;
+}
+#endif /* MXNS_DNSBL */
+
+
+
+/* look for a domain in the DNS blacklists, including its MX & NS servers */
+static void
+dnsbl_name_host_url(DNSBL_WORK *blw,
+		    DNSBL_FGS fg)
+{
+	struct in_addr ipv4;
+#ifndef NO_IPV6
+	struct dcc_in6_addr ipv6;
+#endif
+
+	/* recognize an ASCII IP address */
+	ipv4.s_addr = inet_addr(blw->tgt.dom.c);
+	if (INADDR_NONE != ipv4.s_addr) {
+		ip_lookup(blw, DNSBL_TYPE_IPV4, &ipv4, fg);
+		return;
+	}
+#ifndef NO_IPV6
+	if (0 < inet_pton(AF_INET6, blw->tgt.dom.c, &ipv6)) {
+		ip_lookup(blw, DNSBL_TYPE_IPV6, &ipv6, fg);
+		return;
+	}
+#endif
+
+#ifndef MXNS_DNSBL
+	/* check simple name and its addresses if we do not have a
+	 * resolver library */
+	name_ip_lookup(blw, &blw->tgt.dom, fg);
+
+#else /* MXNS_DNSBL */
+	/* If we have a resolver library, check first for the name itself */
+	if (!name_lookup(blw, &blw->tgt.dom, fg))
+		return;
+
+	/* Look for name servers in the authority section after asking for
+	 * A RRs.  You cannot rely on the bad guys' DNS servers to answer an
+	 * ANY request */
+	if (have_ipv4_dnsbl && !rr_lookup(blw, blw->tgt.dom.c, fg, T_A))
+		return;
+#ifndef NO_IPV6
+	if (have_ipv6_dnsbl && !rr_lookup(blw, blw->tgt.dom.c, fg, T_AAAA))
+		return;
+#endif
+
+	/* Check MX servers if allowed by at least one DNS blacklist.
+	 * To ape the fall back to A RRs when MX RRs are missing, we need to
+	 * check A RR for evil.  However, we've already done that */
+	if (0 != ((fg & DNSBL_FG_URL) ? blw->unhit.url_mx
+		  : blw->unhit.mail_host_mx))
+		rr_lookup(blw, blw->tgt.dom.c, fg | DNSBL_FG_MX, T_MX);
+#endif /* MXNS_DNSBL */
+}
+
+
+
+#ifdef HAVE_HELPERS
+/* do some DNSBL work in a helper process */
+u_char					/* 1=try to send the response */
+dnsbl_work(const DNSBL_REQ *req, DNSBL_RESP *resp)
+{
+	DNSBL_WORK blw;
+	DNSBL_RESP_GROUP *rst;
+	DNSBL_GROUP *blg;
+	int gnum, i;
+
+	blw_clear(&blw);
+	blw.url_start = req->hdr.start;
+	blw.msg_us = MAX_MSG_SECS*DCC_US*2;
+	blw.url_us = req->hdr.avail_us;
+	blw.url_us_used = 0;
+	blw.id = req->hdr.id;
+	blw.dcc_ctxt = 0;
+	blw.log_ctxt = 0;
+
+	if (!is_helper) {
+		/* this must be the first job for this helper process */
+		is_helper = 1;
+		helper_str = " helper";
+		fix_url_secs();
+	}
+
+	blw.unhit = req->unhit;
+	for (i = 0, blg = blw.groups; i < MAX_DNSBL_GROUPS; ++i, ++blg)
+		blg->fgs = req->fgs[i];
+	switch (req->fg) {
+	case DNSBL_FG_CLIENT:		/* SMTP client IP address */
+		blw.tgt.addr = req->tgt.addr;
+		BUFCPY(blw.tgt.dom.c, req->tgt.dom.c);
+		dnsbl_client(&blw);
+		break;
+	case DNSBL_FG_MAIL_HOST:	/* envelope mail_from */
+	case DNSBL_FG_URL:		/* URL in body */
+		BUFCPY(blw.tgt.dom.c, req->tgt.dom.c);
+		dnsbl_name_host_url(&blw, req->fg);
+		break;
+	default:
+		dcc_logbad(EX_SOFTWARE, "%s DNSBL%s unknown type %d",
+			   req->hdr.id, helper_str, req->fg);
+	}
+
+	resp->unhit = blw.unhit;
+	for (gnum = 0, blg = blw.groups, rst = resp->groups;
+	     gnum < MAX_DNSBL_GROUPS;
+	     ++gnum, ++rst, ++blg) {
+		rst->fgs = blg->fgs;	/* copy hit & timeout flags */
+		if (0 == ((resp->unhit.all ^ req->unhit.all)
+			  & DNSBL_G2B(gnum))) {
+			/* no new hit here in the helper
+			 * or had a hit in the parent */
+			rst->bl_num = -1;
+			rst->probe.c[0] = '\0';
+			rst->result[0] = '\0';
+		} else {
+			rst->bl_num = blg->dnsbl->bl_num;
+			BUFCPY(rst->tgt.c, blg->tgt.c);
+			BUFCPY(rst->probe.c, blg->probe.c);
+			BUFCPY(rst->result, blg->result);
+		}
+	}
+
+	return 1;
+}
+
+
+
+/* ask a helper process to check for something in the DNS blacklists */
+static void
+use_helper(DNSBL_WORK *blw, DNSBL_FGS fg)
+{
+	DNSBL_REQ req;
+	DNSBL_RESP resp;
+	DNSBL_RESP_GROUP *rst;
+	DNSBL_GROUP *blg;
+	const DNSBL *dp;
+	int gnum, i;
+
+	memset(&req, 0, sizeof(req));
+
+	BUFCPY(req.hdr.id, blw->id);
+	req.unhit = blw->unhit;
+	for (gnum = 0, blg = blw->groups;
+	     gnum < MAX_DNSBL_GROUPS;
+	     ++gnum, ++blg)
+		req.fgs[gnum] = blg->fgs;
+	req.fg = fg;
+	switch (fg) {
+	case DNSBL_FG_CLIENT:
+		BUFCPY(req.tgt.dom.c, blw->tgt.dom.c);
+		req.tgt.addr = blw->tgt.addr;
+		break;
+	case DNSBL_FG_MAIL_HOST:
+	case DNSBL_FG_URL:
+		BUFCPY(req.tgt.dom.c, blw->tgt.dom.c);
+		break;
+	default:
+		dcc_logbad(EX_SOFTWARE, "unknown DNSBL type %d", fg);
+	}
+
+	if (!ask_helper(blw->dcc_ctxt, blw->log_ctxt, url_us,
+			&req.hdr, sizeof(req), &resp.hdr, sizeof(resp))) {
+		time_ck_post(blw, fg, 1);
+		msg_secs_fin(blw);
+		return;
+	}
+
+	blw->unhit = resp.unhit;
+	/* record hits and timeouts seen by the helper */
+	for (gnum = 0, blg = blw->groups, rst = resp.groups;
+	     gnum < MAX_DNSBL_GROUPS;
+	     ++gnum, ++rst, ++blg) {
+		blg->fgs = rst->fgs;
+		if (0 == ((resp.unhit.all ^ req.unhit.all) & DNSBL_G2B(gnum)))
+			continue;
+
+		/* This group has a new hit.
+		 * Discover the right DNSBL so we can use the right
+		 * template for the SMTP status message */
+		for (i = rst->bl_num, dp = dnsbls; i > 1; --i, dp = dp->fwd) {
+			if (!dp)
+				dcc_logbad(EX_SOFTWARE,
+					   "bad helper DNSBL #%d",
+					   rst->bl_num);
+		}
+		blg->dnsbl = dp;
+		BUFCPY(blg->tgt.c, rst->tgt.c);
+		BUFCPY(blg->probe.c, rst->probe.c);
+		BUFCPY(blg->result, rst->result);
+		blg->btype = type_str(blg->btype_buf, sizeof(blg->btype_buf),
+				      blw, blg->fgs);
+	}
+
+	msg_secs_fin(blw);
+}
+#endif /* HAVE_HELPERS */
+
+
+void
+dcc_client_dnsbl(DNSBL_WORK *blw, const struct in6_addr *addr, const char *name)
+{
+	/* nothing to do if no client DNS blacklists have been configured */
+	if (!blw || !blw->unhit.client)
+		return;
+
+	/* or if we have already spent too much time checking blacklists */
+	if (!msg_secs_start(blw, DNSBL_FG_CLIENT))
+		return;
+
+	BUFCPY(blw->tgt.dom.c, name);
+	blw->tgt.addr = *addr;
+
+#ifdef HAVE_HELPERS
+	if (have_helpers) {
+		use_helper(blw, DNSBL_FG_CLIENT);
+		return;
+	}
+#endif
+
+	dnsbl_client(blw);
+	msg_secs_fin(blw);
+}
+
+
+
+void
+dcc_mail_host_dnsbl(DNSBL_WORK *blw, const char *host)
+{
+	/* nothing to do if no DNS blacklists have been configured
+	 * or we do not have an envelope Mail_From host name
+	 * or no recipients care about mail_host hits */
+	if (!blw || !blw->unhit.mail_host
+	    || !host || *host == '\0')
+		return;
+
+	/* or no still active DNSBLs care about the Mail_From host name */
+
+	/* or if we have already spent too much time checking blacklists */
+	if (!msg_secs_start(blw, DNSBL_FG_MAIL_HOST))
+		return;
+
+	BUFCPY(blw->tgt.dom.c, host);
+
+#ifdef HAVE_HELPERS
+	if (have_helpers) {
+		use_helper(blw, DNSBL_FG_MAIL_HOST);
+		return;
+	}
+#endif
+
+	dnsbl_name_host_url(blw, DNSBL_FG_MAIL_HOST);
+	msg_secs_fin(blw);
+}
+
+
+
+void
+url_dnsbl(DNSBL_WORK *blw)
+{
+	int i;
+
+	/* nothing to do if no body URL DNSBLs have been configured
+	 * or if we already have hits for all DNSBLs */
+	if (!blw || !blw->unhit.url)
+		return;
+
+	/* or if we have already spent too much time checking blacklists */
+	if (!msg_secs_start(blw, DNSBL_FG_URL))
+		return;
+
+	/* avoid checking the same names */
+	for (i = 0; i < DIM(blw->tgt_cache); ++i) {
+		if (!strcmp(blw->tgt.dom.c, blw->tgt_cache[i].dom.c)) {
+			if (helper.debug > 1)
+				dnsbl_log(blw, 0, DNSBL_FG_URL,
+					  "tgt_cache hit");
+			blw->tgt_cache_pos = i;
+			return;
+		}
+	}
+	BUFCPY(blw->tgt_cache[blw->tgt_cache_pos].dom.c, blw->tgt.dom.c);
+	blw->tgt_cache_pos = (blw->tgt_cache_pos + 1) % DIM(blw->tgt_cache);
+
+#ifdef HAVE_HELPERS
+	if (have_helpers) {
+		use_helper(blw, DNSBL_FG_URL);
+		return;
+	}
+#endif
+
+	dnsbl_name_host_url(blw, DNSBL_FG_URL);
+	msg_secs_fin(blw);
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/error_msg.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/error_msg.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,361 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.53 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include "dcc_paths.h"
+#ifndef DCC_WIN32
+#include <syslog.h>
+#endif
+
+extern void dcc_syslog_lock(void);
+extern void dcc_syslog_unlock(void);
+
+u_char trace_quiet;
+
+u_char dcc_no_syslog;
+
+int dcc_error_priority = LOG_ERR | LOG_MAIL;
+int dcc_trace_priority = LOG_NOTICE | LOG_MAIL;
+
+
+/* commonly used, but not thread safe */
+int dcc_ex_code = EX_UNAVAILABLE;
+
+
+DCC_PATH dcc_progname;
+int dcc_progname_len;
+
+#ifdef HAVE___PROGNAME
+extern const char *__progname;
+#endif
+
+
+static void
+clean_stdfd(int stdfd)
+{
+	struct stat sb;
+	int fd;
+
+	if (0 > fstat(stdfd, &sb) && errno == EBADF) {
+		fd = open(_PATH_DEVNULL, 0, O_RDWR);
+		if (fd < 0)		/* ignore errors we can't help */
+			return;
+		if (fd != stdfd) {
+			dup2(fd, stdfd);
+			close(fd);
+		}
+	}
+}
+
+
+
+/* prevent surprises from uses of stdio FDs by ensuring that the FDs are open */
+void
+clean_stdio(void)
+{
+	clean_stdfd(STDIN_FILENO);
+	clean_stdfd(STDOUT_FILENO);
+	clean_stdfd(STDERR_FILENO);
+}
+
+
+
+void
+dcc_syslog_init(u_char use_syslog,
+		const char *argv0 UATTRIB, const char *suffix)
+{
+	const char *p;
+
+	/* Solaris defaults to "syslog" with a null identification string,
+	 * but does not seem to have __progname set by crt0. */
+#undef GOT_PROGNAME
+#ifdef HAVE_GETPROGNAME
+	p = getprogname();
+#	define GOT_PROGNAME
+#endif
+#if defined(HAVE___PROGNAME) && !defined(GOT_PROGNAME)
+	p = __progname;
+#	define GOT_PROGNAME
+#endif
+#ifndef GOT_PROGNAME
+	p = strrchr(argv0, '/');
+#ifdef DCC_WIN32
+	if (!p)
+		p = strrchr(argv0, '\\');
+#endif
+	if (!p)
+		p = argv0;
+	else
+		++p;
+#ifdef DCC_WIN32
+	/* strip ".exe" from Windows progam name */
+	dcc_progname_len = strlen(p);
+	if (dcc_progname_len > LITZ(".exe")
+	    && !CLITCMP(&p[dcc_progname_len-LITZ(".exe")], ".exe")) {
+		char *p1 = strdup(p);
+		p1[dcc_progname_len-LITZ(".exe")] = '\0';
+		p = p1;
+	}
+#endif /* DCC_WIN32 */
+#endif /* !GOT_PROGNAME */
+	snprintf(dcc_progname, sizeof(dcc_progname), "%s%s",
+		 p, suffix ? suffix : "");
+	dcc_progname_len = strlen(dcc_progname);
+
+	/* ensure that stdout and stderr exist so that when we open
+	 * database or other files, we don't get file descriptor 1 or 2
+	 * and then later write error messages to them. */
+	clean_stdio();
+
+#ifdef DCC_WIN32
+	dcc_no_syslog = 1;
+#else
+	/* Don't wait for the console if somehow we must use it,
+	 * because that messes up dccm. */
+#ifndef LOG_NOWAIT
+#define LOG_NOWAIT 0
+#endif
+	openlog(dcc_progname, LOG_PID | LOG_NOWAIT, LOG_MAIL);
+	if (!use_syslog)
+		dcc_no_syslog = 1;
+#endif /* DCC_WIN32 */
+}
+
+
+
+void
+dcc_vfatal_msg(const char *p, va_list args)
+{
+	char logbuf[LOGBUF_SIZE];
+	int i;
+
+	/* write the message with the "fatal error" addition as
+	 * a single message to syslog */
+	i = vsnprintf(logbuf, sizeof(logbuf), p, args);
+	if (i >= ISZ(logbuf))
+		strcpy(&logbuf[ISZ(logbuf)-sizeof("...")], "...");
+
+	fflush(stdout);			/* keep stderr and stdout straight */
+	fprintf(stderr, "%s; fatal error\n", logbuf);
+	fflush(stderr);
+
+	if (dcc_no_syslog)
+		return;
+
+	dcc_syslog_lock();
+	syslog(dcc_error_priority, "%s; fatal error", logbuf);
+	closelog();
+	dcc_syslog_unlock();
+}
+
+
+
+int
+dcc_verror_msg(const char *p, va_list args)
+{
+	char logbuf[LOGBUF_SIZE];
+	int i;
+
+	i = vsnprintf(logbuf, sizeof(logbuf), p, args);
+	if (i >= ISZ(logbuf)) {
+		strcpy(&logbuf[ISZ(logbuf)-sizeof("...")], "...");
+		i = ISZ(logbuf)-1;
+	} else if (i == 0) {
+		i = snprintf(logbuf, sizeof(logbuf), "(empty error message)");
+	}
+
+	fflush(stdout);			/* keep stderr and stdout straight */
+	fwrite(logbuf, i, 1, stderr);
+	if (logbuf[i-1] != '\n') {
+		fwrite("\n", 1, 1, stderr);
+		++i;
+	}
+
+	if (!dcc_no_syslog) {
+		dcc_syslog_lock();
+		syslog(dcc_error_priority, "%s", logbuf);
+		dcc_syslog_unlock();
+	}
+
+	return i;
+}
+
+
+
+void PATTRIB(1,2)
+dcc_error_msg(const char *p, ...)
+{
+	va_list args;
+
+	va_start(args, p);
+	dcc_verror_msg(p, args);
+	va_end(args);
+}
+
+
+
+void
+dcc_vtrace_msg(const char *p, va_list args)
+{
+	char logbuf[LOGBUF_SIZE];
+	int i;
+
+	/* Some systems including Linux with gcc 3.4.2 on AMD 64 processors
+	 * do not allow two uses of a va_list but requires va_copy()
+	 * Other systems do not have any notion of va_copy(). */
+	i = vsnprintf(logbuf, sizeof(logbuf), p, args);
+	if (i >= ISZ(logbuf))
+		strcpy(&logbuf[ISZ(logbuf)-sizeof("...")], "...");
+
+	fflush(stdout);			/* keep stderr and stdout straight */
+	fprintf(stderr, "%s\n", logbuf);
+
+	if (!dcc_no_syslog) {
+		dcc_syslog_lock();
+		syslog(dcc_trace_priority, "%s", logbuf);
+		dcc_syslog_unlock();
+	}
+}
+
+
+
+void PATTRIB(1,2)
+dcc_trace_msg(const char *p, ...)
+{
+	va_list args;
+
+	va_start(args, p);
+	dcc_vtrace_msg(p, args);
+	va_end(args);
+}
+
+
+
+/* send only to system log if being quiet */
+void PATTRIB(1,2)
+quiet_trace_msg(const char *p, ...)
+{
+	va_list args;
+
+	va_start(args, p);
+	if (trace_quiet) {
+		vsyslog(dcc_trace_priority, p, args);
+	} else {
+		dcc_vtrace_msg(p, args);
+	}
+	va_end(args);
+}
+
+
+
+void
+dcc_vpemsg(int ex_code, DCC_EMSG emsg, const char *msg, va_list args)
+{
+	if (!emsg) {
+		dcc_verror_msg(msg, args);
+	} else {
+		dcc_ex_code = ex_code;
+		vsnprintf(emsg, sizeof(DCC_EMSG), msg, args);
+	}
+}
+
+
+
+void PATTRIB(3,4)
+dcc_pemsg(int ex_code, DCC_EMSG emsg, const char *msg, ...)
+{
+	va_list args;
+
+	va_start(args, msg);
+	dcc_vpemsg(ex_code, emsg, msg, args);
+	va_end(args);
+}
+
+
+
+const char *
+fnm_lno(DCC_FNM_LNO_BUF *buf, const char *fnm, int lno)
+{
+	DCC_PATH tmp;
+
+	if (!fnm || *fnm == '\0') {
+		buf->b[0] = '\0';
+	} else {
+		fnm2abs(tmp, fnm, "");
+		snprintf(buf->b, sizeof(buf->b), DCC_FNM_LNO_PAT, lno, tmp);
+	}
+	return buf->b;
+}
+
+
+
+int
+dcc_vearly_log(EARLY_LOG *el, const char *p, va_list args)
+{
+#	define ELIPS_STR "...\n"
+	int max_len, len;
+
+	max_len = sizeof(el->buf) - el->len;
+	if (max_len <= 0)
+		return 0;
+
+	len = vsnprintf(&el->buf[el->len], max_len, p, args);
+	if (len < max_len) {
+		el->len += len;
+		return len;
+	} else {
+		memcpy(&el->buf[sizeof(el->buf)-LITZ(ELIPS_STR)],
+		       ELIPS_STR, LITZ(ELIPS_STR));
+		el->len = sizeof(el->buf);
+		return max_len;
+	}
+
+#undef ELIPS_STR
+}
+
+
+
+const char *
+optopt2str(int i)
+{
+	static char b[] = "-x";
+
+	b[1] = i;
+	return b;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/fnm2path.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/fnm2path.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,331 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.26 $Revision$
+ */
+
+#include "dcc_defs.h"
+#ifdef DCC_WIN32
+#include <direct.h>
+#endif
+
+DCC_PATH dcc_homedir;
+int dcc_homedir_len = 2;		/* (ensure comparisons fail) */
+
+
+#ifndef DCC_WIN32
+static u_char
+trimpath(DCC_PATH path, const DCC_PATH tmp)
+{
+	const char *s;
+	char c, *t, *p;
+
+
+	/* trim "//", "/./", "../", "/../", or trailing "/" */
+	s = tmp;
+	if (s[0] == '.' && s[1] == '/')
+		s += 2;
+	t = path;
+	for (;;) {
+		c = *s++;
+		if (c != '/') {
+			*t = c;
+			if (c == '\0')
+				break;
+			++t;
+			continue;
+		}
+
+		/* check character after '/' */
+		c = *s;
+		if (c == '/')		/* discard first '/' in "//" */
+			continue;
+		if (c == '\0'		/* discard trailing '/' */
+		    && t != path)
+			continue;
+
+		if (c != '.') {
+			*t++ = '/';
+			continue;
+		}
+
+		/* we have seen "/." */
+		c = *++s;
+		if (c == '/')		/* discard "/." in "/./" */
+			continue;
+
+		/* trim trailing "/." */
+		if (c == '\0')
+			continue;
+
+		if (c != '.'
+		    || (*(s+1) != '/' && *(s+1) != '\0')) {
+			*t++ = '/';
+			*t++ = '.';
+			continue;
+		}
+
+		/* we have "/../" or "/..\0", so remove last name in target */
+		*t = '\0';
+		p = strrchr(path, '/');
+		if (p) {
+			t = p;
+			if (t == path)	/* convert "/.." to "/" */
+				++t;
+		} else {
+			t = path;
+		}
+		++s;			/* advance to '\0' or 2nd '/' */
+	}
+	if (path[0] == '\0') {
+		path[0] = tmp[0] == '/' ? '/' : '.';
+		path[1] = '\0';
+	}
+
+	return 1;
+}
+#endif
+
+
+
+/* make a relative pathname from a file name and an optional suffix */
+u_char					/* 0=too long */
+fnm2rel(DCC_PATH new_path, const char *nm, const char *suffix)
+{
+#ifdef DCC_WIN32
+	return fnm2abs(new_path, nm, suffix);
+#else
+	DCC_PATH tmp;
+	int i;
+	const char *p, *p1;
+
+	/* the answer is the input pathname if it is null */
+	if (!nm || *nm == '\0') {
+		new_path[0] = '\0';
+		return 0;
+	}
+
+	if (suffix) {
+		p = tmp;
+		i = snprintf(tmp, sizeof(tmp), "%s%s", nm, suffix);
+	} else {
+		p = nm;
+		i = strlen(p);
+	}
+	if (i >= ISZ(tmp)) {
+		/* too long */
+		new_path[0] = '\0';
+		return 0;
+	}
+	if (p[dcc_homedir_len] == '/'
+	    && !strncmp(p, dcc_homedir, dcc_homedir_len)) {
+		p1 = p + dcc_homedir_len;
+		do {
+			++p1;
+		} while (*p1 == '/');
+		if (*p1 != '\0')
+			p = p1;
+	}
+
+	return trimpath(new_path, p);
+#endif
+}
+
+
+
+/* make pathname from an optional suffix and a file name relative to
+ *	a path or the home directory */
+u_char					/* 0=too long */
+fnm2abs(DCC_PATH path,			/* put it here */
+	const char *nm, const char *suffix)
+{
+	DCC_PATH tmp;
+	int i;
+#ifdef DCC_WIN32
+	char *p;
+	DWORD lasterror;
+#endif
+
+	/* the answer is the input pathname if it is null */
+	if (!nm || *nm == '\0') {
+		path[0] = '\0';
+		return 0;
+	}
+
+	if (!suffix)
+		suffix = "";
+
+#ifdef DCC_WIN32
+	i = snprintf(tmp, sizeof(DCC_PATH), "%s%s", nm, suffix);
+	if (i >= ISZ(DCC_PATH)) {
+		path[0] = '\0';
+		return 0;
+	}
+	lasterror = GetLastError();
+	GetFullPathName(tmp, sizeof(DCC_PATH), path, &p);
+	SetLastError(lasterror);
+	return 1;
+#else
+	if (nm[0] == '/' || dcc_homedir[0] == '\0') {
+		i = snprintf(tmp, sizeof(tmp), "%s%s", nm, suffix);
+	} else {
+		i = snprintf(tmp, sizeof(tmp), "%s/%s%s",
+			     dcc_homedir, nm, suffix);
+	}
+	if (i >= ISZ(tmp)) {
+		/* too long */
+		path[0] = '\0';
+		return 0;
+	}
+
+	return trimpath(path, tmp);
+#endif
+}
+
+
+
+/* make an absolute pathname from a file name for printing */
+const char *
+fnm2abs_err(DCC_PATH path, const char *nm)
+{
+	static DCC_PATH pathbuf;
+
+	if (!path)			/* obviously not thread safe */
+		path = pathbuf;
+
+	return fnm2abs(path, nm, 0) ? path : nm;
+}
+
+
+
+/* make a relative pathname from a file name that must be good */
+void
+fnm2rel_good(DCC_PATH path, const char *nm, const char* suffix)
+{
+	if (!fnm2rel(path, nm, suffix))
+		dcc_logbad(EX_SOFTWARE, "\"%s%s\" is too long",
+			   nm, suffix ? suffix : "");
+}
+
+
+
+/* remove initial substring of the home directory */
+const char *
+path2fnm(const char *path)
+{
+	const char *p;
+
+	if (path[dcc_homedir_len] != '/'
+	    || strncmp(path, dcc_homedir, dcc_homedir_len))
+		return path;
+
+	p = path+dcc_homedir_len;
+	do {
+		++p;
+	} while (*p == '/');
+	if (*p == '\0')
+		return path;
+	return p;
+}
+
+
+
+/* change to the DCC home directory */
+u_char					/* 0=failed 1=ok */
+dcc_cdhome(DCC_EMSG emsg, const char *newdir, u_char rdonly)
+{
+	DCC_PATH tmp;
+	u_char result;
+
+	result = 1;
+	if (!newdir)
+		newdir = DCC_HOMEDIR;
+
+	if (*newdir == '\0') {
+		dcc_pemsg(EX_NOINPUT, emsg,
+			  "invalid null DCC home directory");
+		newdir = DCC_HOMEDIR;
+		result = 0;
+	}
+
+	if (!fnm2abs(tmp, newdir, 0)) {
+		dcc_pemsg(EX_NOINPUT, emsg,
+			  "bad DCC home directory \"%s\"", newdir);
+		result = 0;
+	}
+#ifdef DCC_WIN32
+	if (!SetCurrentDirectory(tmp)) {
+		dcc_pemsg(EX_NOINPUT, emsg, "SetCurrentDirectory(%s): %s" ,
+			  tmp, ERROR_STR());
+		return 0;
+	}
+	if (!getcwd(dcc_homedir, sizeof(dcc_homedir))) {
+		BUFCPY(dcc_homedir, tmp);
+		dcc_homedir_len = strlen(dcc_homedir);
+	}
+#else
+	if (0 > chdir(tmp)) {
+		dcc_pemsg(EX_NOINPUT, emsg, "chdir(%s): %s",
+			  tmp, ERROR_STR());
+		result = 0;
+	} else if (tmp[0] == '/'
+		   || !getcwd(dcc_homedir, sizeof(dcc_homedir))) {
+		BUFCPY(dcc_homedir, tmp);
+		dcc_homedir_len = strlen(dcc_homedir);
+	}
+
+	if (!rdonly && 0 > access(dcc_homedir, W_OK)) {
+		if ((errno == EACCES || errno == EPERM)
+		    && dcc_real_uid != dcc_effective_uid) {
+#ifdef HAVE_EACCESS
+			dcc_get_priv();
+			if (0 > eaccess(dcc_homedir, W_OK)) {
+				dcc_pemsg(EX_NOINPUT, emsg, "%s: %s",
+					  dcc_homedir, ERROR_STR());
+				result = 0;
+			}
+			dcc_rel_priv();
+#endif
+		} else {
+			dcc_pemsg(EX_NOINPUT, emsg, "%s: %s",
+				  dcc_homedir, ERROR_STR());
+			result = 0;
+		}
+	}
+#endif /* !DCC_WIN32 */
+
+	return result;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/get_id.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/get_id.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,98 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * convert an ID to binary
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.18 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include "dcc_xhdr.h"
+
+
+/* get client or server-ID */
+DCC_CLNT_ID				/* ID or DCC_ID_INVALID */
+dcc_get_id(DCC_EMSG emsg, const char *id_str,
+	   const char *fnm, int lno)
+{
+	char *p;
+	unsigned long id;
+	DCC_FNM_LNO_BUF fnm_buf;
+
+	id_str += strspn(id_str, DCC_WHITESPACE);
+	id = strtoul(id_str, &p, 10);
+	if (*p != '\0'
+	    || (id != DCC_ID_ANON
+		&& (id < DCC_SRVR_ID_MIN || id > DCC_CLNT_ID_MAX))) {
+		if (!strcasecmp(id_str, DCC_XHDR_ID_ANON))
+			return DCC_ID_ANON;
+
+		dcc_pemsg(EX_DATAERR, emsg, "invalid ID \"%s\"%s",
+			  id_str, fnm_lno(&fnm_buf, fnm, lno));
+		return DCC_ID_INVALID;
+	}
+
+	return id;
+}
+
+
+
+/* get server-ID */
+const char *				/* rest of string */
+dcc_get_srvr_id(DCC_EMSG emsg,
+		DCC_SRVR_ID *result,	/* ID or DCC_ID_INVALID */
+		const char *id_str, const char *dlims,
+		const char *fnm, int lno)
+{
+	char *p;
+	unsigned long id;
+	DCC_FNM_LNO_BUF fnm_buf;
+
+	if (!dlims)
+		dlims = "";
+	id = strtoul(id_str, &p, 10);
+	if (p == id_str
+	    || (*p && !strchr(dlims, *p))
+	    || (id < DCC_SRVR_ID_MIN || id > DCC_SRVR_ID_MAX)) {
+		dcc_pemsg(EX_DATAERR, emsg, "invalid server-ID \"%s\"%s",
+			  id_str, fnm_lno(&fnm_buf, fnm, lno));
+		*result = DCC_ID_INVALID;
+		return 0;
+	}
+
+	*result = id;
+	return p;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/get_port.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/get_port.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,673 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * convert a service name to a port number
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.79 $Revision$
+ */
+
+#include "dcc_clnt.h"
+#ifndef DCC_WIN32
+#include <arpa/inet.h>			/* for AIX */
+#endif
+
+
+DCC_SOCKU dcc_hostaddrs[MAX_DCC_HOSTADDRS];
+char dcc_host_canonname[DCC_MAXDOMAINLEN];
+DCC_SOCKU *dcc_hostaddrs_end;
+
+
+/* get port number
+ *	Note that this function uses dcc_host_lock() and dcc_host_unlock() */
+u_int					/* DCC_GET_PORT_INVALID or port # */
+dcc_get_port(DCC_EMSG emsg,
+	     const char *portname,
+	     u_int def_port,		/* DCC_GET_PORT_INVALID or default */
+	     const char *fnm, int lno)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	char *p;
+	unsigned long l;
+	struct servent *sp;
+	u_int16_t port;
+
+
+	if (portname[0] == '\0'
+	    || !strcmp(portname, "-")) {
+		if (def_port != DCC_GET_PORT_INVALID)
+			return def_port;
+		dcc_pemsg(EX_USAGE, emsg, "missing port%s",
+			  fnm_lno(&fnm_buf, fnm, lno));
+		return DCC_GET_PORT_INVALID;
+	}
+
+	/* first try a numeric port number, since that is common and
+	 * the getservby* functions are so slow. */
+	l = strtoul(portname, &p,0);
+	if (*p == '\0' && l > 0 && l <= 65535)
+		return htons((u_int16_t)l);
+
+	dcc_host_lock();
+	sp = getservbyname(portname, 0);
+	if (sp) {
+		port = sp->s_port;
+		dcc_host_unlock();
+		return port;
+	}
+	dcc_host_unlock();
+
+	dcc_pemsg(EX_USAGE, emsg, "invalid port \"%s\"%s",
+		  portname, fnm_lno(&fnm_buf, fnm, lno));
+	return DCC_GET_PORT_INVALID;
+}
+
+
+
+static void
+copy_hp_to_hostaddrs(const struct hostent *hp,
+		     u_char family)
+{
+	DCC_SOCKU *sup, *sup1;
+	int i;
+	const void *v;
+
+	if (hp->h_name && dcc_host_canonname[0] == '\0')
+		BUFCPY(dcc_host_canonname, hp->h_name);
+
+	sup = dcc_hostaddrs_end;
+	for (i = 0;
+	     (v = hp->h_addr_list[i]) != 0 && sup  <= LAST(dcc_hostaddrs);
+	     ++i) {
+		dcc_mk_su(sup, family, v, 0);
+		/* deal with stuttering */
+		sup1 = dcc_hostaddrs;
+		for (;;) {
+			if (sup1 >= sup) {
+				++sup;
+				break;
+			}
+			if (DCC_SU_EQ(sup1, sup))
+				break;
+			++sup1;
+		}
+	}
+	dcc_hostaddrs_end = sup;
+}
+
+
+
+#ifdef USE_GETADDRINFO
+static void
+copy_ai_to_hostaddrs(const struct addrinfo *ai, u_char use_ipv6)
+{
+	DCC_SOCKU *sup, *sup1;
+
+	if (ai->ai_canonname && dcc_host_canonname[0] == '\0')
+		BUFCPY(dcc_host_canonname, ai->ai_canonname);
+
+	for (sup = dcc_hostaddrs_end;
+	     ai && sup  <= LAST(dcc_hostaddrs);
+	     ai = ai->ai_next) {
+		if (ai->ai_family == AF_INET) {
+			if (use_ipv6 == 1)
+				continue;
+			dcc_mk_su(sup, AF_INET,
+				  &((struct sockaddr_in *
+				     )ai->ai_addr)->sin_addr, 0);
+		} else if (ai->ai_family == AF_INET6) {
+			if (use_ipv6 == 0)
+				continue;
+			dcc_mk_su(sup, AF_INET6,
+				  &((struct sockaddr_in6 *
+				     )ai->ai_addr)->sin6_addr, 0);
+		} else {
+			continue;
+		}
+
+		/* deal with stuttering */
+		sup1 = dcc_hostaddrs;
+		for (;;) {
+			if (sup1 >= sup) {
+				++sup;
+				break;
+			}
+			if (DCC_SU_EQ(sup1, sup))
+				break;
+			++sup1;
+		}
+	}
+	dcc_hostaddrs_end = sup;
+}
+#endif /* USE_GETADDRINFO */
+
+
+
+/* Convert a host name to an IPv4 address by calling
+ *	Rgethostbyname() or gethostbyname()
+ * This must be protected with dcc_host_lock() and dcc_host_unlock(). */
+static u_char
+dcc_get_host_ipv4(const char *nm,	/* look for this name */
+		  int *ep,		/* put errno or herrno here */
+		  struct hostent *(WSAAPI fnc)(const char *))
+{
+	const struct hostent *hp;
+	struct in_addr ipv4;
+
+	if (!dcc_host_locked)
+		dcc_logbad(EX_SOFTWARE, "dcc_get_host() not locked");
+
+	dcc_host_canonname[0] = '\0';
+	dcc_hostaddrs_end = &dcc_hostaddrs[0];
+
+	/* First see if it is a number to avoid the MicroStupid stall
+	 * when doing a gethostbyname() on a number */
+	ipv4.s_addr = inet_addr(nm);
+	if (ipv4.s_addr != INADDR_NONE) {
+		dcc_mk_su(&dcc_hostaddrs[0], AF_INET, &ipv4, 0);
+		dcc_hostaddrs_end = &dcc_hostaddrs[1];
+		return 1;
+	}
+
+	hp = fnc(nm);
+	if (!hp) {
+		*ep = h_errno;
+		return 0;
+	}
+	copy_hp_to_hostaddrs(hp, AF_INET);
+	return 1;
+}
+
+
+
+/* This must be protected with dcc_host_lock()and dcc_host_unlock(). */
+u_char					/* 0=failed */
+dcc_get_host_SOCKS(const char *nm,	/* look for this name */
+		   u_char use_ipv6,	/* 0=v4 1=v6 2=prefer v6 3=prefer v4 */
+		   int *ep)		/* put errno or herrno here */
+{
+	int error1, error2;
+
+	/* since there is no Rgetaddrinfo() or equivalent,
+	 * use the normal resolver if we need an IPv6 address */
+	switch (use_ipv6) {
+	case 0:
+		return dcc_get_host_ipv4(nm, ep, Rgethostbyname);
+	case 1:
+		return dcc_get_host(nm, 1, ep);
+	case 2:
+		if (dcc_get_host(nm, 1, &error1))
+			return 1;
+		return dcc_get_host_ipv4(nm, ep, Rgethostbyname);
+	case 3:
+	default:
+		if (dcc_get_host_ipv4(nm, &error1, Rgethostbyname))
+			return 1;
+		if (dcc_get_host(nm, 1, &error2))
+			return 1;
+		*ep = error1;
+		return 0;
+	}
+}
+
+
+
+/* This must be protected with dcc_host_lock()and dcc_host_unlock().
+ *	It does not assme that gethostbyname() or whatever is thread safe.
+ *	This function is mentioned in dccifd/dccif-test/dccif-test.c
+ *	and so cannot change lightly. */
+u_char					/* 0=failed */
+dcc_get_host(const char *nm,		/* look for this name */
+	     u_char use_ipv6,		/* 0=v4 1=v6 2=prefer v6 3=prefer v4 */
+	     int *ep)			/* put errno or herrno here */
+{
+#undef EXPANDED
+#if defined(USE_GETIPNODEBYNAME) && !defined(EXPANDED) && !defined(NO_IPV6)
+#define EXPANDED
+	static struct hostent *hp;
+
+	if (!dcc_host_locked)
+		dcc_logbad(EX_SOFTWARE, "dcc_get_host() not locked");
+
+	dcc_host_canonname[0] = '\0';
+	dcc_hostaddrs_end = &dcc_hostaddrs[0];
+
+	/* given a choice, return both IPv4 and IPv6 addresses */
+	if (use_ipv6 == 0 || use_ipv6 == 3) {
+		hp = getipnodebyname(nm, AF_INET, 0, ep);
+		if (hp) {
+			copy_hp_to_hostaddrs(hp, AF_INET);
+			freehostent(hp);
+		}
+	}
+	if (use_ipv6 != 0) {
+		hp = getipnodebyname(nm, AF_INET6,0, ep);
+		if (hp) {
+			copy_hp_to_hostaddrs(hp, AF_INET6);
+			freehostent(hp);
+		}
+	}
+	if (use_ipv6 == 2) {
+		hp = getipnodebyname(nm, AF_INET, 0, ep);
+		if (hp) {
+			copy_hp_to_hostaddrs(hp, AF_INET);
+			freehostent(hp);
+		}
+	}
+	if (dcc_hostaddrs_end != &dcc_hostaddrs[0])
+		return 1;
+	*ep = h_errno;
+	return 0;
+#endif
+#if defined(USE_GETADDRINFO) && !defined(EXPANDED) && !defined(NO_IPV6)
+#define EXPANDED
+	static struct addrinfo hints;
+	struct addrinfo *ai;
+	int error = 0;
+
+	if (!dcc_host_locked)
+		dcc_logbad(EX_SOFTWARE, "dcc_get_host() not locked");
+
+	dcc_host_canonname[0] = '\0';
+	dcc_hostaddrs_end = &dcc_hostaddrs[0];
+
+	hints.ai_flags = AI_CANONNAME;
+
+	/* the FreeBSD version stutters trying to provide both UDP and
+	 * TCP if you do not choose */
+	hints.ai_protocol = IPPROTO_TCP;
+
+	/* if you think AF_UNSPEC should provide IPv4 and IPv6, you evidently
+	 * think wrong for at least the FreeBSD version, unless both flavors
+	 * are in /etc/hosts */
+	if (use_ipv6 == 0 || use_ipv6 == 3) {
+		hints.ai_family = AF_INET;
+		error = getaddrinfo(nm, 0, &hints, &ai);
+		if (!error) {
+			copy_ai_to_hostaddrs(ai, 0);
+			freeaddrinfo(ai);
+		}
+	}
+	if (use_ipv6 != 0) {
+		hints.ai_family = AF_INET6;
+		error = getaddrinfo(nm, 0, &hints, &ai);
+		if (!error) {
+			copy_ai_to_hostaddrs(ai, use_ipv6);
+			freeaddrinfo(ai);
+		}
+	}
+	if (use_ipv6 == 2) {
+		hints.ai_family = AF_INET;
+		error = getaddrinfo(nm, 0, &hints, &ai);
+		if (!error) {
+			copy_ai_to_hostaddrs(ai, 0);
+			freeaddrinfo(ai);
+		}
+	}
+	if (dcc_hostaddrs_end != &dcc_hostaddrs[0])
+		return 1;
+	*ep = error;
+	return 0;
+#endif
+#ifndef EXPANDED
+	/* this platform can only handle IPv4 */
+	if (use_ipv6 == 1) {
+		*ep = HOST_NOT_FOUND;
+		return 0;
+	}
+	return dcc_get_host_ipv4(nm, ep, gethostbyname);
+#endif
+#undef EXPANDED
+}
+
+
+
+/* make socket address from an IP address, a family, and a port number */
+DCC_SOCKU *
+dcc_mk_su(DCC_SOCKU *su,		/* put it here */
+	  int family,			/* AF_INET or AF_INET6 */
+	  const void *addrp,		/* this IP address; 0=INADDR_ANY */
+	  u_short port)
+{
+	memset(su, 0, sizeof(*su));	/* assume INADDR_ANY=0 */
+	su->sa.sa_family = family;
+	if (family == AF_INET) {
+#ifdef HAVE_SA_LEN
+		su->sa.sa_len = sizeof(struct sockaddr_in);
+#endif
+		su->ipv4.sin_port = port;
+		if (addrp)
+			memcpy(&su->ipv4.sin_addr, addrp,
+			       sizeof(su->ipv4.sin_addr));
+	} else {
+#ifdef HAVE_SA_LEN
+		su->sa.sa_len = sizeof(struct sockaddr_in6);
+#endif
+		su->ipv6.sin6_port = port;
+		if (addrp)
+			memcpy(&su->ipv6.sin6_addr, addrp,
+			       sizeof(su->ipv6.sin6_addr));
+	}
+
+	return su;
+}
+
+
+
+/* strip leading and trailing white space */
+static const char *
+dcc_strip_white(const char *str, u_int *lenp)
+{
+	const char *end;
+	char c;
+
+	str += strspn(str, DCC_WHITESPACE);
+	end = str+strlen(str);
+	while (end > str) {
+		c = *(end-1);
+		if (c != ' ' && c != '\t' && c != '\r' && c != '\n')
+			break;
+		--end;
+	}
+	*lenp = end-str;
+	return str;
+}
+
+
+
+/* get a socket address from a dotted quad or IPv6 string */
+u_char
+dcc_str2ip(DCC_SOCKU *su, const char *str)
+{
+#ifndef NO_IPV6
+	u_int len;
+	char buf[INET6_ADDRSTRLEN];
+#endif
+
+#ifdef HAVE_INET_ATON
+	if (0 < inet_aton(str, &su->ipv4.sin_addr)) {
+		su->sa.sa_family = AF_INET;
+		return 1;
+	}
+#else
+	u_int addr = inet_addr(str);
+	if (su->ipv4.sin_addr.s_addr != INADDR_NONE) {
+		su->ipv4.sin_addr.s_addr = addr;
+		su->sa.sa_family = AF_INET;
+		return 1;
+	}
+#endif
+
+#ifndef NO_IPV6
+	/* Try IPv6 only after failing to understand the address as IPv4.
+	 *
+	 * inet_pton() does not like blanks or terminal '\n'
+	 * It is also too smart by half and assumes that it is
+	 * given a pointer to struct sockaddr.  When it decodes
+	 * an IPv4 address, it sticks it 4 bytes before the
+	 * start of the IPv6 buffer it is given. */
+	str = dcc_strip_white(str, &len);
+	if (len == 0 || len >= sizeof(buf))
+		return 0;
+	memcpy(buf, str, len);
+	buf[len] = '\0';
+	if (0 < inet_pton(AF_INET6, buf, &su->ipv6.sin6_addr)) {
+		su->sa.sa_family = AF_INET6;
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+
+
+void
+dcc_bits2mask(struct in6_addr *mask, int bits)
+{
+	int wordno, i;
+
+	for (wordno = 0; wordno < 4; ++wordno) {
+		i = bits - wordno*32;
+		if (i >= 32) {
+			mask->s6_addr32[wordno] = 0xffffffff;
+			continue;
+		}
+		if (i <= 0) {
+			mask->s6_addr32[wordno] = 0;
+		} else {
+			mask->s6_addr32[wordno] = 0xffffffff << (32-i);
+		}
+		mask->s6_addr32[wordno] = htonl(mask->s6_addr32[wordno]);
+	}
+}
+
+
+
+/* get an IPv6 address and netmask */
+int					/* # of bits, 0=not address, -1 error */
+dcc_str2cidr(DCC_EMSG emsg,
+	     struct in6_addr *addr6,
+	     struct in6_addr *mask6,
+	     u_char *is_ipv6,
+	     const char *str,
+	     const char *fnm, int lno)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	char addrstr[INET6_ADDRSTRLEN];
+	DCC_SOCKU su;
+	struct in6_addr mask6_loc;
+	const char *bitsp;
+	char *p;
+	u_int str_len, addr_len, bits_len;
+	int n, bits, wordno;
+
+	str = dcc_strip_white(str, &str_len);
+	bitsp = strchr(str, '/');
+
+	if (!bitsp) {
+		addr_len = str_len;
+	} else {
+		addr_len = bitsp - str;
+	}
+	if (addr_len == 0 || addr_len >= ISZ(addrstr))
+		return 0;		/* not an IP address */
+	memcpy(addrstr, str, addr_len);
+	addrstr[addr_len] = '\0';
+	if (!dcc_str2ip(&su, addrstr))
+		return 0;		/* not an IP address */
+
+	if (!bitsp) {
+		if (su.sa.sa_family == AF_INET6) {
+			bitsp = "128";
+			bits_len = 3;
+		} else {
+			bitsp = "32";
+			bits_len = 2;
+		}
+		bits = 128;
+	} else {
+		bits_len = str_len - addr_len - 1;
+		n = strtoul(++bitsp, &p, 10);
+		bits = n;
+		if (su.sa.sa_family == AF_INET)
+			bits += 128-32;
+		if (p < bitsp+bits_len || bits > 128 || n == 0) {
+			dcc_pemsg(EX_NOHOST, emsg,
+				  "invalid CIDR block length \"%.*s\"%s",
+				  str_len, str, fnm_lno(&fnm_buf, fnm, lno));
+			return -1;
+		}
+	}
+
+	if (su.sa.sa_family == AF_INET6) {
+		*addr6 = su.ipv6.sin6_addr;
+		if (is_ipv6)
+			*is_ipv6 = 1;
+	} else {
+		dcc_ipv4toipv6(addr6, su.ipv4.sin_addr);
+		if (is_ipv6)
+			*is_ipv6 = 0;
+	}
+
+	if (!mask6)
+		mask6 = &mask6_loc;
+	dcc_bits2mask(mask6, bits);
+	for (wordno = 0; wordno < 4; ++wordno) {
+		if ((addr6->s6_addr32[wordno]
+		     & ~mask6->s6_addr32[wordno]) != 0) {
+			dcc_pemsg(EX_NOHOST, emsg,
+				  "%s does not start on a"
+				  " %.*s-bit CIDR boundary%s",
+				  addrstr, bits_len, bitsp,
+				  fnm_lno(&fnm_buf, fnm, lno));
+			return -1;
+		}
+	}
+
+	return bits;
+}
+
+
+
+/* Create and bind a UDP socket */
+int					/* -1=possible IPv6 problem, 0=failed */
+dcc_udp_bind(DCC_EMSG emsg,
+	     SOCKET *socp,		/* INVALID_SOCKET or existing socket */
+	     const DCC_SOCKU *sup,
+	     int *retry_secsp)		/* -1=1 retry for anonymous port,
+					 * 0=no retry, >0=seconds trying */
+{
+	DCC_SOCKU su;
+#ifdef DCC_WIN32
+	u_long on;
+#endif
+	int tenths, result;
+
+	if (*socp == INVALID_SOCKET) {
+#ifdef NO_IPV6
+		if (sup->sa.sa_family == AF_INET6) {
+			dcc_pemsg(EX_OSERR, emsg,
+				  "attempt to create IPv6 UDP socket");
+			return -1;
+		}
+#endif
+		*socp = socket(sup->sa.sa_family, SOCK_DGRAM, 0);
+		if (*socp == INVALID_SOCKET) {
+			/* Let the caller try again if this system does not do
+			 * IPv6 but IPv6 support has not been compiled out.
+			 * It would be reasonable to use
+			 * (errno == EPFNOSUPPORT || errno == EPROTONOSUPPORT)
+			 * but some systems including Linux are unreasonable. */
+			if (sup->sa.sa_family == AF_INET6) {
+				dcc_pemsg(EX_OSERR, emsg,
+					  "socket(UDP IPv6): %s", ERROR_STR());
+				return -1;
+			} else {
+				dcc_pemsg(EX_OSERR, emsg,
+					  "socket(UDP IPv4): %s", ERROR_STR());
+				return 0;
+			}
+		}
+	}
+
+#ifdef DCC_WIN32
+	on = 1;
+	if (SOCKET_ERROR == ioctlsocket(*socp, FIONBIO, &on)) {
+		dcc_pemsg(EX_OSERR, emsg, "ioctlsocket(UDP, FIONBIO): %s",
+			  ERROR_STR());
+		closesocket(*socp);
+		*socp = INVALID_SOCKET;
+		return 0;
+	}
+#else
+	if (0 > fcntl(*socp, F_SETFD, FD_CLOEXEC)) {
+		dcc_pemsg(EX_OSERR, emsg, "fcntl(UDP, FD_CLOEXEC): %s",
+			  ERROR_STR());
+		closesocket(*socp);
+		*socp = INVALID_SOCKET;
+		return 0;
+	}
+	if (-1 == fcntl(*socp, F_SETFL,
+			fcntl(*socp, F_GETFL, 0) | O_NONBLOCK)) {
+		dcc_pemsg(EX_OSERR, emsg, "fcntl(UDP O_NONBLOCK): %s",
+			  ERROR_STR());
+		closesocket(*socp);
+		*socp = INVALID_SOCKET;
+		return 0;
+	}
+#endif
+
+	tenths = 10;
+	for (;;) {
+		if (SOCKET_ERROR != bind(*socp, &sup->sa, DCC_SU_LEN(sup)))
+			return 1;
+
+		if (errno == EADDRINUSE
+		    && retry_secsp && *retry_secsp < 0
+		    && sup != &su
+		    && *DCC_SU_PORTP(sup) != 0) {
+			/* If the initial number of seconds to retry was <0,
+			 * then make one last try for a new ephemeral port */
+			su = *sup;
+			*DCC_SU_PORTP(&su) = 0;
+			sup = &su;
+			continue;
+		}
+
+		if (errno != EADDRINUSE
+		    || !retry_secsp || *retry_secsp <= 0) {
+			/* no retries were allowed or we have exhasuted them */
+#ifndef NO_IPV6
+			if (sup->sa.sa_family == AF_INET6
+			    && (errno == EPFNOSUPPORT
+				|| errno == EPROTONOSUPPORT))
+				result = -1;
+			else
+#endif
+				result = 0;
+			dcc_pemsg(EX_OSERR, emsg, "bind(UDP %s): %s",
+				  dcc_su2str_err(sup), ERROR_STR());
+			closesocket(*socp);
+			*socp = INVALID_SOCKET;
+			return result;
+		}
+
+		usleep(100*1000);
+		if (!--tenths) {
+			--*retry_secsp;
+			tenths = 10;
+		}
+	}
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/get_secs.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/get_secs.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,207 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.24 $Revision$
+ */
+
+#include "dcc_defs.h"
+
+
+/* because tv.tv_sec is not a time_t on all systems
+ * and to be thread safe on WIN32 */
+const struct tm *
+dcc_localtime(time_t secs, struct tm *result)
+{
+#ifdef HAVE_LOCALTIME_R
+	result = localtime_r(&secs, result);
+#else
+	dcc_localtime_lock();
+	result = localtime(&secs);
+	dcc_localtime_unlock();
+#endif
+	return result;
+}
+
+
+
+const char *
+dcc_time2str(char *buf, size_t buf_len, const char *pat, time_t secs)
+{
+	struct tm tm;
+
+	if (0 >= strftime(buf, buf_len, pat, dcc_localtime(secs, &tm)))
+		STRLCPY(buf, "?", buf_len);
+	return buf;
+}
+
+
+
+int
+dcc_get_secs(const char *s, const char **end, int min, int max, int def)
+{
+	const char *cp;
+	char *p;
+	int secs;
+
+	if (*s == '\0' || *s == ',') {
+		secs = def;
+		cp = s;
+	} else if (min > 0
+		   && !CLITCMP(s, "never")) {
+		cp = s+LITZ("never");
+		secs = max;
+	} else if ((secs = strtoul(s, &p, 10)) >= DCC_MAX_SECS/60) {
+		return -1;
+	} else if (*(cp = p) == '\0' || *cp == ',') {
+		;
+	} else if (!CLITCMP(cp, "seconds")) {
+		cp += LITZ("seconds");
+	} else if (!CLITCMP(cp, "s")) {
+		cp += LITZ("s");
+
+	} else if (!CLITCMP(cp, "minutes")) {
+		cp += LITZ("minutes");
+		secs *= 60;
+	} else if (!CLITCMP(cp, "minute")) {
+		cp += LITZ("minute");
+		secs *= 60;
+	} else if (!CLITCMP(cp, "m")) {
+		cp += LITZ("m");
+		secs *= 60;
+
+	} else if (secs >= DCC_MAX_SECS/(60*60)) {
+		return -1;
+	} else if (!CLITCMP(cp, "hours")) {
+		cp += LITZ("hours");
+		secs *= 60*60;
+	} else if (!CLITCMP(cp, "hour")) {
+		cp += LITZ("hour");
+		secs *= 60*60;
+	} else if (!CLITCMP(cp, "h")) {
+		cp += LITZ("h");
+		secs *= 60*60;
+
+	} else if (secs >= DCC_MAX_SECS/(24*60*60)) {
+		return -1;
+	} else if (!CLITCMP(cp, "days")) {
+		cp += LITZ("days");
+		secs *= 24*60*60;
+	} else if (!CLITCMP(cp, "day")) {
+		cp += LITZ("day");
+		secs *= 24*60*60;
+	} else if (!CLITCMP(cp, "d")) {
+		cp += LITZ("d");
+		secs *= 24*60*60;
+
+	} else if (secs >= DCC_MAX_SECS/(7*24*60*60)) {
+		return -1;
+	} else if (!CLITCMP(cp, "weeks")) {
+		cp += LITZ("weeks");
+		secs *= 7*24*60*60;
+	} else if (!CLITCMP(cp, "week")) {
+		cp += LITZ("week");
+		secs *= 7*24*60*60;
+	} else if (!CLITCMP(cp, "w")) {
+		cp += LITZ("w");
+		secs *= 7*24*60*60;
+
+	} else {
+		return -1;
+	}
+
+	if (secs > max)
+		return -1;
+	if (secs < min && secs != 0)
+		return -1;
+
+	if (*cp != '\0') {
+		if (*cp != ',' || !end)
+			return -1;
+		++cp;
+	}
+	if (end)
+		*end = cp;
+	return secs;
+}
+
+
+
+time_t
+tv_diff2us(const struct timeval *tv1, const struct timeval *tv2)
+{
+	time_t us;
+
+	/* prevent overflow */
+	us = tv1->tv_sec - tv2->tv_sec;
+	if (us <= -FOREVER_SECS)
+		return -FOREVER_US;
+	if (us >= FOREVER_SECS)
+		return FOREVER_US;
+	us = us*DCC_US + (tv1->tv_usec - tv2->tv_usec);
+	return us;
+}
+
+
+
+void
+tv_add(struct timeval *tgt,
+       const struct timeval *a,
+       const struct timeval *b)
+{
+	tgt->tv_sec = a->tv_sec + b->tv_sec;
+	tgt->tv_usec = a->tv_usec + b->tv_usec;
+	tgt->tv_sec += tgt->tv_usec / DCC_US;
+	tgt->tv_usec %= DCC_US;
+}
+
+
+
+void
+tv_add_us(struct timeval *tgt,
+	  time_t us)
+{
+	time_t secs;
+
+	us = tgt->tv_usec + us;
+	if (us >= 0) {
+		tgt->tv_sec += us / DCC_US;
+		tgt->tv_usec = us % DCC_US;
+	} else {
+		secs = (DCC_US - 1 - us) / DCC_US;
+		tgt->tv_sec -= secs;
+		tgt->tv_usec = (secs * DCC_US) + us;
+	}
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/getifaddrs.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/getifaddrs.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,188 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * kludge replacement for getifaddrs(3) for systems that lack it
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.19 $Revision$
+ */
+
+
+/* let the configure script decide if this might work */
+#ifndef TEST_DCC_GETIFADDRS
+#include "dcc_config.h"
+#endif
+
+#ifndef USE_DCC_GETIFADDRS
+/* global to suppress "defined but not used" warning */
+char dcc_getifaddrs_unneeded[] = "dcc_getifaddrs() unneeded";
+
+#else
+
+#include <sys/types.h>
+#define BSD_COMP			/* for SunOS */
+#define _IO_TERMIOS_H			/* kludge for OpenUNIX */
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <unistd.h>
+#include "dcc_defs.h"
+#include "dcc_heap_debug.h"
+#include "dcc_ifaddrs.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+dcc_getifaddrs(struct ifaddrs **pif)
+{
+	struct ifconf ifc;
+	int buf_size;
+	char *buf;
+	struct ifreq *ifrp, *ifrp_next, ifr_f;
+	struct ifaddrs_all *result0, *result, *rp;
+	int i, serrno, s;
+
+	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+		return -1;
+
+	/* get the list of interface names and addresses */
+	buf_size = 1024;
+	for (;;) {
+		buf = dcc_malloc(buf_size);
+		if (!buf) {
+			serrno = errno;
+			close(s);
+			errno = serrno;
+			return -1;
+		}
+		ifc.ifc_buf = buf;
+		ifc.ifc_len = buf_size;
+		memset(buf, 0, buf_size);
+		if (0 > ioctl(s, SIOCGIFCONF, (char *)&ifc)
+		    && errno != EINVAL) {
+			serrno = errno;
+			close(s);
+			errno = serrno;
+			*pif = 0;
+			return -1;
+		}
+		if (ifc.ifc_len < buf_size && ifc.ifc_len != 0)
+			break;
+		dcc_free(buf);
+		buf_size *= 2;
+	}
+
+	i = (ifc.ifc_len/sizeof(struct ifreq)) * sizeof(*result);
+	result = (struct ifaddrs_all *)dcc_malloc(i);
+	if (!result) {
+		serrno = errno;
+		dcc_free(buf);
+		close(s);
+		errno = serrno;
+		*pif = 0;
+		return -1;
+	}
+	memset(result, 0, i);
+	result0 = result;
+	*pif = &result->ifa;
+
+	for (ifrp = ifc.ifc_req;
+	     (void *)ifrp < (void *)&ifc.ifc_buf[ifc.ifc_len];
+	     ifrp = ifrp_next) {
+#ifdef HAVE_SA_LEN
+		i = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+		if (i < (int)sizeof(*ifrp))
+			ifrp_next = ifrp + 1;
+		else
+			ifrp_next = (struct ifreq *)((char *)ifrp + i);
+#else
+		ifrp_next = ifrp + 1;
+#endif
+
+		if (ifrp->ifr_addr.sa_family != AF_INET
+#ifdef AF_INET6
+		&& ifrp->ifr_addr.sa_family != AF_INET6
+#endif
+		    )
+			continue;
+
+		strncpy(ifr_f.ifr_name, ifrp->ifr_name, sizeof(ifr_f.ifr_name));
+		if (0 > ioctl(s, SIOCGIFFLAGS, (char *)&ifr_f)) {
+			if (errno == ENXIO)
+				continue;
+			serrno = errno;
+			close(s);
+			dcc_free(buf);
+			dcc_free(*pif);
+			*pif = 0;
+			errno = serrno;
+			return -1;
+		}
+
+		/* save the name only for debugging */
+		strncpy(result->name, ifrp->ifr_name, sizeof(result->name));
+		result->ifa.ifa_name = result->name;
+
+		result->addr.sa = ifrp->ifr_addr;
+		result->ifa.ifa_addr = &result->addr.sa;
+		result->ifa.ifa_flags = ifr_f.ifr_flags;
+		result->ifa.ifa_next = &(result+1)->ifa;
+
+		/* skip duplicate addresses */
+		rp = result0;
+		for (;;) {
+			if (rp >= result) {
+				++result;
+				break;
+			}
+			if (DCC_SU_EQ(&rp->addr, &result->addr))
+				break;
+			++rp;
+		}
+	}
+
+	if (&result->ifa == *pif) {
+		*pif = 0;
+	} else {
+		(result-1)->ifa.ifa_next = 0;
+	}
+	dcc_free(buf);
+	close(s);
+
+	return 0;
+}
+#endif /* USE_DCC_GETIFADDRS */
diff -r 000000000000 -r c7f6b056b673 dcclib/getopt.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/getopt.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,123 @@
+/* copy of getopt(3) for Windows */
+
+#include "dcc_defs.h"
+
+/*
+ * Copyright (c) 1987, 1993, 1994
+ *	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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)getopt.c	8.3 (Berkeley) 4/27/95";
+#endif
+static const char rcsid[] = "$FreeBSD: src/lib/libc/stdlib/getopt.c,v 1.2.2.2 2001/08/26 03:36:04 jkoshy Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int	opterr = 0,		/* if error message should be printed */
+	optind = 1,		/* index into parent argv vector */
+	optopt,			/* character checked for validity */
+	optreset;		/* reset getopt */
+char	*optarg;		/* argument associated with option */
+
+#define	BADCH	(int)'?'
+#define	BADARG	(int)':'
+#define	EMSG	""
+
+/*
+ * getopt --
+ *	Parse argc/argv argument vector.
+ */
+int
+getopt(nargc, nargv, ostr)
+	int nargc;
+	char * const *nargv;
+	const char *ostr;
+{
+	static char *place = EMSG;		/* option letter processing */
+	char *oli;				/* option letter list index */
+
+	if (optreset || !*place) {		/* update scanning pointer */
+		optreset = 0;
+		if (optind >= nargc || *(place = nargv[optind]) != '-') {
+			place = EMSG;
+			return (-1);
+		}
+		if (place[1] && *++place == '-') {	/* found "--" */
+			++optind;
+			place = EMSG;
+			return (-1);
+		}
+	}					/* option letter okay? */
+	if ((optopt = (int)*place++) == (int)':' ||
+	    (oli = strchr(ostr, optopt)) == 0) {
+		/*
+		 * if the user didn't specify '-' as an option,
+		 * assume it means -1.
+		 */
+		if (optopt == (int)'-')
+			return (-1);
+		if (!*place)
+			++optind;
+		if (opterr && *ostr != ':' && optopt != BADCH)
+			(void)fprintf(stderr,
+			    "%s: illegal option -- %c\n", __progname, optopt);
+		return (BADCH);
+	}
+	if (*++oli != ':') {			/* don't need argument */
+		optarg = NULL;
+		if (!*place)
+			++optind;
+	}
+	else {					/* need an argument */
+		if (*place)			/* no white space */
+			optarg = place;
+		else if (nargc <= ++optind) {	/* no arg */
+			place = EMSG;
+			if (*ostr == ':')
+				return (BADARG);
+			if (opterr)
+				(void)fprintf(stderr,
+				    "%s: option requires an argument -- %c\n",
+				    __progname, optopt);
+			return (BADCH);
+		}
+		else				/* white space */
+			optarg = nargv[optind];
+		place = EMSG;
+		++optind;
+	}
+	return (optopt);			/* dump back option letter */
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/heap_debug.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/heap_debug.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,232 @@
+/* Distributed Checksum Clearinghouse heap debugging
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.20 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include "dcc_heap_debug.h"
+#include <stdio.h>
+
+void dcc_malloc_lock(void);
+void dcc_malloc_unlock(void);
+
+#ifdef UNIX
+#include <unistd.h>
+#else /* !UNIX */
+#include "malloc.h"
+#endif /* !UNIX */
+
+
+typedef u_long SENTINEL;
+#define HEAD_SENTINEL_VALUE 0xdeadbeaf
+#define TAIL_SENTINEL_VALUE 0xbeafdead
+
+typedef struct mdbg{
+    SENTINEL	head;
+    struct mdbg *fwd, *bak;
+    SENTINEL	*tail;
+} MDBG;
+
+u_int dcc_num_mdbg;			/* global to suppress warnings */
+MDBG *dcc_mdbg_chain;			/*	when not used */
+MDBG *bad_mp;
+int bad_i;
+
+/* leave a few names outside to make some `ar`s happy */
+#ifdef DCC_DEBUG_HEAP
+
+
+/* this needs to do as little as possible to avoid calling malloc()
+ *	global and no NRATTRIB to avoid confusion in dumps */
+void
+dcc_heap_abort(const char *m)
+{
+#ifdef UNIX
+	static int die;			/* suppress no-retun warning */
+
+	write(STDERR_FILENO, m, strlen(m));
+	if (++die)
+		abort();
+#else
+	bad_message_box("heap_debug", 1, m);
+#endif
+}
+
+
+
+char *
+dcc_strdup(const char *s)
+{
+	char *p;
+
+	p = dcc_malloc(strlen(s)+1);
+	strcpy(p, s);
+	return p;
+}
+
+
+
+void
+dcc_malloc_check(void)
+{
+	MDBG *mp;
+	u_int i;
+
+	i = dcc_num_mdbg;
+	if (!i)
+		return;
+
+	mp = dcc_mdbg_chain;
+	for (;;) {
+		if (mp->head != HEAD_SENTINEL_VALUE
+		    || *mp->tail != TAIL_SENTINEL_VALUE)
+			dcc_heap_abort("trashed heap sentinel");
+		if (mp->bak->fwd != mp
+		    || mp->fwd->bak != mp) {
+			bad_mp = mp;
+			bad_i = i;
+			dcc_heap_abort("malloc chain trashed");
+		}
+		if (--i == 0) {
+			if (mp->fwd != dcc_mdbg_chain) {
+				bad_mp = mp;
+				bad_i = i;
+				dcc_heap_abort("wrong malloc chain too long");
+			}
+#ifndef UNIX
+			i = _heapchk();
+			if (i != _HEAPOK && i != _HEAPEMPTY)
+				dcc_heap_abort("heapchk() failed");
+#endif
+			return;
+		} else if (mp->fwd == dcc_mdbg_chain) {
+			bad_mp = mp;
+			bad_i = i;
+			dcc_heap_abort("wrong malloc chain too short");
+		}
+		mp = mp->fwd;
+	}
+}
+
+
+
+void *
+dcc_malloc(size_t len)
+{
+	MDBG *mp;
+
+	dcc_malloc_lock();
+	dcc_malloc_check();
+
+	if (!len)
+		dcc_heap_abort("malloc(0)");
+
+	len += sizeof(MDBG) + sizeof(mp->tail);
+	len += (sizeof(mp->tail) - len) & (sizeof(mp->tail)-1); /* align tail */
+
+	mp = malloc(len);
+	if (!mp)
+		return mp;
+
+	if (!dcc_num_mdbg) {
+		mp->fwd = mp->bak = mp;
+	} else {
+		mp->bak = dcc_mdbg_chain;
+		mp->fwd = dcc_mdbg_chain->fwd;
+		mp->bak->fwd = mp;
+		mp->fwd->bak = mp;
+	}
+	dcc_mdbg_chain = mp;
+	mp->head = HEAD_SENTINEL_VALUE;
+	mp->tail = (SENTINEL *)((u_char *)mp+len-sizeof(mp->tail));
+	*mp->tail = TAIL_SENTINEL_VALUE;
+	dcc_num_mdbg++;
+	dcc_malloc_unlock();
+
+	return (mp+1);
+}
+
+
+
+void *
+dcc_calloc(size_t n, size_t s)
+{
+	void *p;
+
+	s *= n;
+	if (s == 0)
+		dcc_heap_abort("zero calloc() size");
+	p = dcc_malloc(s);
+	if (!p)
+		return p;
+	memset(p, 0, s);
+	return p;
+}
+
+
+
+void
+dcc_free(void *p)
+{
+	int i;
+	MDBG *mp;
+
+	dcc_malloc_lock();
+	dcc_malloc_check();
+	i = dcc_num_mdbg;
+	mp = dcc_mdbg_chain;
+	for (;;) {
+		if (!i)
+			dcc_heap_abort("freeing non-free");
+		if (mp+1 == p)
+			break;
+		mp = mp->fwd;
+		i--;
+	}
+
+	if (dcc_mdbg_chain == mp)
+		dcc_mdbg_chain = mp->fwd;
+	mp->bak->fwd = mp->fwd;
+	mp->fwd->bak = mp->bak;
+	dcc_num_mdbg--;
+	memset(mp, 0xf1, (u_char *)mp->tail+sizeof(mp->tail)-(u_char *)mp);
+	dcc_malloc_check();
+	dcc_malloc_unlock();
+
+	free(mp);
+}
+#endif /* DCC_DEBUG_HEAP */
diff -r 000000000000 -r c7f6b056b673 dcclib/helper.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/helper.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,935 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * helper processes for DNS blacklists
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.42 $Revision$
+ */
+
+#include "helper.h"
+#include "dcc_heap_debug.h"
+#include <signal.h>
+#include <sys/wait.h>
+
+
+#ifdef HAVE_HELPERS
+
+#define HELPER_MAX_FAILURES 5		/* shutdown & restart these total */
+
+#define MAX_SLOW	    2		/* 50% excess for slow DNS resolver */
+
+
+/* add to the argv list for the helper processes */
+void
+helper_save_arg(const char *flag, const char *value)
+{
+	char const **new_arg;
+	int i;
+
+	if (helper.free_args <= 1) {
+		/* reserve space for the argv[0] and the null terminator */
+		helper.free_args += 5;
+		i = (helper.argc + 2*helper.free_args) * sizeof(char *);
+		new_arg = dcc_malloc(i);
+		memset(new_arg, 0, i);
+		if (helper.argv) {
+			for (i = 0; i < helper.argc; ++i)
+				new_arg[i] = helper.argv[i];
+			dcc_free(helper.argv);
+		} else {
+			++helper.argc;
+		}
+		helper.argv = new_arg;
+	}
+
+	helper.argv[helper.argc] = flag;
+	helper.argv[++helper.argc] = value;
+	helper.argv[++helper.argc] = 0;
+	--helper.free_args;
+}
+
+
+
+/* initialize things for the parent or one of the helping children */
+void
+helper_init(int max_helpers)
+{
+	helper.sn = getpid() + time(0);
+
+	helper.pipe_write = -1;
+	helper.pipe_read = -1;
+	helper.soc = INVALID_SOCKET;
+	helper.req_len = sizeof(DNSBL_REQ);
+
+	if (max_helpers) {
+		/* max_helpers=0 if we are starting a child,
+		 * but != 0 in the parent after parsing all args
+		 *
+		 * default to dccifd or dccm max_work */
+		if (!helper.max_helpers)
+			helper.max_helpers = max_helpers;
+
+		helper.pids = dcc_malloc(sizeof(pid_t) * helper.max_helpers);
+		memset(helper.pids, 0, sizeof(pid_t) * helper.max_helpers);
+	}
+
+	have_helpers = helper_lock_init();
+}
+
+
+
+/* collect zombies of helpers that died from boredom or otherwise */
+void
+reap_helpers(u_char locked)
+{
+	int status;
+	pid_t pid;
+	int pid_inx;
+
+	if (!locked)
+		helper_lock();
+
+	for (;;) {
+wait_again:;
+		pid = waitpid(0, &status, WNOHANG);
+		if (0 >= pid) {
+			if (!locked)
+				helper_unlock();
+			return;
+		}
+
+		for (pid_inx = 0; ; ++pid_inx) {
+			if (pid_inx >= helper.max_helpers) {
+				/* not an acknowledged child */
+				if (helper.debug >= 1)
+					dcc_trace_msg("not a DNSBL"
+						      " helper process reaped");
+				goto wait_again;
+			}
+
+			if (helper.pids[pid_inx] == pid)
+				break;
+		}
+
+		helper.pids[pid_inx] = 0;
+
+		/* races with dying helpers can confuse us */
+		if (--helper.total_helpers < 0) {
+			if (helper.debug)
+				dcc_trace_msg("DNSBL total helpers=%d",
+					      helper.total_helpers);
+			helper.total_helpers = 0;
+		}
+
+		if (helper.slow_helpers > helper.total_helpers/MAX_SLOW)
+			helper.slow_helpers = helper.total_helpers/MAX_SLOW;
+
+		if (--helper.idle_helpers < 0) {
+			/* We cannot be certain that a helper that quit was
+			 * idle, but it should have been. */
+			if (helper.debug)
+				dcc_trace_msg("DNSBL idle helpers=%d",
+					      helper.idle_helpers);
+			helper.idle_helpers = 0;
+		} else if (helper.idle_helpers > helper.total_helpers) {
+			/* The limit on the total_helpers can let us
+			 * drive idle_helpers<0
+			 * which can make the previous fix wrong */
+			if (helper.debug)
+				dcc_trace_msg("DNSBL idle helpers=%d"
+					      "  total helpers=%d",
+					      helper.idle_helpers,
+					      helper.total_helpers);
+			helper.idle_helpers = helper.total_helpers;
+		}
+
+		/* this is called from the "totals" thread
+		 * and so cannot use thr_error_msg() */
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+		if (WIFEXITED(status)) {
+			if (WEXITSTATUS(status) == 0) {
+				if (helper.debug > 1)
+					dcc_trace_msg("DNSBL helper %d quit,"
+						      " leaving %d helpers,"
+						      " %d idle",
+						      (int)pid,
+						      helper.total_helpers,
+						      helper.idle_helpers);
+				continue;
+			}
+			dcc_error_msg("DNSBL helper %d quit with exit(%d),"
+				      " leaving %d helpers, %d idle",
+				      (int)pid, WEXITSTATUS(status),
+				      helper.total_helpers,
+				      helper.idle_helpers);
+			continue;
+		}
+#endif
+#if defined(WTERMSIG) && defined(WIFSIGNALED)
+		if (WIFSIGNALED(status)) {
+			dcc_error_msg("DNSBL helper %d quit with signal %d,"
+				      " leaving %d helpers, %d idle",
+				      (int)pid, WTERMSIG(status),
+				      helper.total_helpers,
+				      helper.idle_helpers);
+			continue;
+		}
+#endif
+		dcc_error_msg("DNSBL helper %d quit with %d,"
+			      " leaving %d helpers, %d idle",
+			      (int)pid, status,
+			      helper.total_helpers, helper.idle_helpers);
+	}
+}
+
+
+
+/* must be called with the counter mutex */
+static void
+terminate_helpers(void)
+{
+	if (helper.pipe_write != -1) {
+		close(helper.pipe_write);
+		helper.pipe_write = -1;
+	}
+	if (helper.pipe_read != -1) {
+		close(helper.pipe_read);
+		helper.pipe_read = -1;
+	}
+	if (helper.soc != INVALID_SOCKET) {
+		closesocket(helper.soc);
+		helper.soc = INVALID_SOCKET;
+	}
+
+	reap_helpers(1);
+	++helper.gen;
+	memset(helper.pids, 0, sizeof(pid_t) * helper.max_helpers);
+	helper.total_helpers = 0;
+	helper.idle_helpers = 0;
+	helper.slow_helpers = 0;
+
+	helper.failures = 0;
+}
+
+
+
+static void
+help_finish(u_int gen, u_char ok, u_char counted, u_char locked)
+{
+	if (!locked)
+		helper_lock();
+
+	/* forget it if the children have been restarted */
+	if (gen == helper.gen) {
+		if (counted)
+			++helper.idle_helpers;
+
+		if (!ok) {
+			if (++helper.failures >= HELPER_MAX_FAILURES) {
+				if (helper.debug)
+					dcc_trace_msg("restart DNSBL helpers");
+				terminate_helpers();
+			} else {
+				reap_helpers(1);
+			}
+		}
+	}
+
+	if (!locked)
+		helper_unlock();
+}
+
+
+
+static u_char
+helper_soc_open(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt)
+{
+	u_char result;
+
+	if (ctxt->soc != INVALID_SOCKET)
+		return 1;
+
+	dcc_ctxts_lock();
+	if (!dcc_info_lock(emsg)) {
+		dcc_ctxts_unlock();
+		return 0;
+	}
+	result = dcc_clnt_soc_reopen(emsg, ctxt);
+	if (!dcc_info_unlock(emsg))
+		result = 0;
+	dcc_ctxts_unlock();
+	return result;
+}
+
+
+
+static u_char
+helper_soc_connect(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt, const DCC_SOCKU *su)
+{
+	u_char result;
+
+#ifdef linux
+	/* since at least some versions of Linux refuse to reconnect,
+	 * just disconnect */
+	su = 0;
+#endif
+
+	dcc_ctxts_lock();
+	if (!dcc_info_lock(emsg)) {
+		dcc_ctxts_unlock();
+		return 0;
+	}
+	result = dcc_clnt_connect(emsg, ctxt, su);
+	if (!dcc_info_unlock(emsg))
+		result = 0;
+	dcc_ctxts_unlock();
+
+	return result;
+}
+
+
+
+/* open the helper socket used to send requests to helper processes
+ *	must be called with the counter mutex */
+static u_char
+open_helper(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt, void *lp)
+{
+	struct in6_addr ipv6_loopback;
+	struct in_addr ipv4_loopback;
+	DCC_SOCKLEN_T soc_len;
+	static int rcvbuf = 32*1024;
+	static u_char rcvbuf_set = 0;
+
+	/* We want to create a new socket with the same choice of
+	 * IPv4 or IPv6 as the DCC client context's socket.  To do that,
+	 * we must ensure that the context's socket is healthy. */
+	if (!helper_soc_open(emsg, ctxt)) {
+		thr_error_msg(lp, "DNSBL helper soc_open(): %s", emsg);
+		return 0;
+	}
+
+	if (dcc_clnt_info->src.family != AF_UNSPEC) {
+		/*  bind to the send-from address if available */
+		dcc_mk_su(&helper.su, dcc_clnt_info->src.family,
+			  &dcc_clnt_info->src.u, 0);
+	} else if (ctxt->flags & DCC_CTXT_USING_IPV4) {
+		ipv4_loopback.s_addr = ntohl(0x7f000001);
+		dcc_mk_su(&helper.su, AF_INET, &ipv4_loopback, 0);
+	} else {
+		memset(&ipv6_loopback, 0, sizeof(ipv6_loopback));
+		ipv6_loopback.s6_addr32[3] = ntohl(1);
+		dcc_mk_su(&helper.su, AF_INET6, &ipv6_loopback, 0);
+	}
+	clean_stdio();
+	if (0 >= dcc_udp_bind(emsg, &helper.soc, &helper.su, 0)) {
+		thr_error_msg(lp, "DNSBL helper bind(%s): %s",
+			      dcc_su2str_err(&helper.su), emsg);
+		terminate_helpers();
+		return 0;
+	}
+	soc_len = sizeof(helper.su);
+	if (0 > getsockname(helper.soc, &helper.su.sa, &soc_len)) {
+		thr_error_msg(lp, "DNSBL helper getsockname(%d, %s): %s",
+			      helper.soc, dcc_su2str_err(&helper.su),
+			      ERROR_STR());
+		terminate_helpers();
+		return 0;
+	}
+	for (;;) {
+		if (!setsockopt(helper.soc, SOL_SOCKET, SO_RCVBUF,
+				&rcvbuf, sizeof(rcvbuf)))
+			break;
+		if (rcvbuf_set || rcvbuf <= 4096) {
+			thr_error_msg(lp,
+				      "DNSBL setsockopt(%s,SO_RCVBUF=%d): %s",
+				      dcc_su2str_err(&helper.su),
+				      rcvbuf, ERROR_STR());
+			break;
+		}
+		rcvbuf -= 4096;
+	}
+	rcvbuf_set = 1;
+	return 1;
+}
+
+
+
+/* Create the pipe used to awaken and terminate the helper processes
+ *	must be called with the counter mutex */
+static u_char
+ready_helpers(void *lp)
+{
+	int fds[2];
+
+	if (helper.pipe_write >= 0
+	    && helper.pipe_read >= 0)
+		return 1;
+
+	terminate_helpers();
+
+	/* give the helper child processes an FD that will go dead
+	 * if the parent dies or otherwise closes the other end */
+	clean_stdio();
+	if (0 > pipe(fds)) {
+		thr_error_msg(lp, "DNSBL parent helper pipe(): %s",
+			      ERROR_STR());
+		terminate_helpers();
+		return 0;
+	}
+	helper.pipe_read = fds[0];
+	helper.pipe_write = fds[1];
+	if (0 > fcntl(helper.pipe_write, F_SETFD, FD_CLOEXEC)) {
+		thr_error_msg(lp, "DNSBL helper fcntl(FD_CLOEXEC): %s",
+			      ERROR_STR());
+		terminate_helpers();
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+/* Start a new helper process.
+ *	The counter mutex must be locked */
+static u_char
+new_helper(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt, void *lp)
+{
+	pid_t pid;
+	char arg_buf[sizeof("set:")+sizeof(HELPER_PAT)+8+8+8];
+	char trace_buf[200];
+	char *bufp;
+	SOCKET soc;
+	int pid_inx, buf_len, i, j;
+
+	/* open the socket if necessary */
+	if (helper.soc == INVALID_SOCKET) {
+		if (!ready_helpers(lp))
+			return 0;
+		if (!open_helper(emsg, ctxt, lp))
+			return 0;
+	}
+
+	reap_helpers(1);
+	for (pid_inx = 0; ; ++pid_inx) {
+		if (pid_inx >= helper.max_helpers)
+			dcc_logbad(EX_SOFTWARE, "no free DNSBL pids[] entry");
+		if (helper.pids[pid_inx] == 0)
+			break;
+	}
+
+	fflush(stdout);
+	fflush(stderr);
+	pid = fork();
+	if (pid < 0) {
+		thr_error_msg(lp, "DNSBL helper fork(): %s", ERROR_STR());
+		return 0;
+	}
+
+	if (pid != 0) {
+		/* this is the parent */
+		helper.pids[pid_inx] = pid;
+		++helper.total_helpers;
+		return 1;
+	}
+
+	dcc_rel_priv();			/* no fun or games */
+	clean_stdio();
+
+	/* reset FD_CLOEXEC without affecting parent */
+	soc = dup(helper.soc);
+	if (soc == INVALID_SOCKET)
+		dcc_logbad(EX_UNAVAILABLE, "DNSBL helper soc dup(%d): %s",
+			   helper.soc, ERROR_STR());
+
+	snprintf(arg_buf, sizeof(arg_buf), "set:"HELPER_PAT,
+		 soc, helper.pipe_read, helper.total_helpers);
+	helper_save_arg("-B", arg_buf);
+	helper.argv[0] = dnsbl_progpath;
+	buf_len = sizeof(trace_buf);
+	bufp = trace_buf;
+	for (i = 0; i < helper.argc && buf_len > 2; ++i) {
+		j = snprintf(bufp, buf_len, "%s ", helper.argv[i]);
+		buf_len -= j;
+		bufp += j;
+	}
+	if (helper.debug >= 4)
+		dcc_trace_msg("DNSBL helper exec %s", trace_buf);
+
+	execv(helper.argv[0], (char * const *)helper.argv);
+	/* This process should continue at helper_child() */
+
+	dcc_logbad(EX_UNAVAILABLE, "exec(%s): %s",
+		   trace_buf, ERROR_STR());
+}
+
+
+
+static void NRATTRIB
+helper_exit(const char *reason)
+{
+	if (helper.debug > 1)
+		dcc_trace_msg("helper process on %s %s",
+			      dcc_su2str_err(&helper.su), reason);
+
+	exit(0);
+}
+
+
+
+static u_char helper_alarm_hit;
+static void
+helper_alarm(int s UATTRIB)
+{
+	helper_alarm_hit = 1;
+}
+
+
+
+/* helper processes start here via fork()/exec() in the parent  */
+void NRATTRIB
+helper_child(SOCKET soc, int fd, int total_helpers)
+{
+	sigset_t sigs;
+	DCC_SOCKLEN_T soc_len;
+	DNSBL_REQ req;
+	int req_len;
+	DNSBL_RESP resp;
+	DCC_SOCKU req_su;
+	DCC_SOCKLEN_T su_len;
+	struct timeval now;
+	u_char wake_buf;
+	int secs, i;
+
+	/* this process inherits via exec() by dccm or dccifd odd signal
+	 * blocking from some pthreads implementations including FreeBSD 5.* */
+	signal(SIGHUP, SIG_IGN);
+	signal(SIGINT, SIG_IGN);
+	signal(SIGTERM, SIG_IGN);
+	sigemptyset(&sigs);
+	sigaddset(&sigs, SIGALRM);
+	sigprocmask(SIG_UNBLOCK, &sigs, 0);
+
+	helper_init(0);
+	if (have_helpers)
+		dcc_logbad(EX_SOFTWARE, "no threads for DNSBL helpers");
+
+	helper.total_helpers = total_helpers;
+
+	helper.pipe_read = fd;
+	helper.soc = soc;
+	soc_len = sizeof(helper.su);
+	if (0 > getsockname(helper.soc, &helper.su.sa, &soc_len))
+		dcc_logbad(EX_IOERR, "DNSBL helper getsockname(%d): %s",
+			   helper.soc, ERROR_STR());
+
+	if (helper.debug > 1)
+		dcc_trace_msg("DNSBL helper process starting on %s",
+			      dcc_su2str_err(&helper.su));
+
+	for (;;) {
+		/* Use read() and SIGALRM to watch for a wake-up byte
+		 * from the parent, the parent ending and closing the pipe,
+		 * or enough idle time to require our retirement.  This
+		 * tactic awakens a single child for each wake-up call
+		 * from the parent.  Using select() or poll() on the main
+		 * socket awakens a thundering herd of children */
+		secs = HELPER_IDLE_STOP_SECS+1;
+		if (helper.total_helpers > 0)
+			secs /= helper.total_helpers+1;
+		if (secs < 5)
+			secs = 5;
+		signal(SIGALRM, helper_alarm);
+#ifdef HAVE_SIGINTERRUPT
+		siginterrupt(SIGALRM, 1);
+#endif
+		helper_alarm_hit = 0;
+		alarm(secs);
+		for (;;) {
+			su_len = sizeof(req_su);
+			req_len = recvfrom(helper.soc, &req, ISZ(req), 0,
+					   &req_su.sa, &su_len);
+
+			/* sleep until awakened if no work is ready */
+			if (req_len <= 0) {
+				if (req_len == 0)
+					dcc_logbad(EX_IOERR,
+						   "DNSBL helper recvfrom()=0");
+				if (!DCC_BLOCK_ERROR())
+					dcc_logbad(EX_IOERR,
+						   "DNSBL helper recvfrom():"
+						   " %s",
+						   ERROR_STR());
+				if (helper_alarm_hit)
+					helper_exit("idle helper exit");
+
+				i = read(helper.pipe_read, &wake_buf, 1);
+
+				/* The other end of the pipe can be marked
+				 * non-blocking by some pthreads
+				 * implementations.  That makes read() on this
+				 * end fail with EAGAIN.  When that happens,
+				 * fall back on select() or poll().
+				 * Even on such pthread implementations,
+				 * it rarely happens. */
+				if (i < 0 && DCC_BLOCK_ERROR()) {
+					DCC_EMSG emsg;
+					i = dcc_select_poll(emsg,
+							helper.pipe_read, 0,
+							-1);
+					if (i < 0)
+					    dcc_logbad(EX_IOERR,
+						       "dnsbl HELPER select():"
+						       " %s", emsg);
+				}
+
+				/* loof for work after a wake-up call */
+				if (i > 0)
+					continue;
+
+				if (helper_alarm_hit)
+					continue;
+				if (i == 0)
+					helper_exit("shutdown");
+				if (i < 0) {
+					dcc_logbad(EX_OSERR,
+						   "DNSBL read(terminate): %s",
+						   ERROR_STR());
+				}
+			}
+			if (req_len != helper.req_len) {
+				if (helper.debug)
+					dcc_trace_msg("DNSBL helper"
+						      " recvfrom(parent %s)=%d"
+						      " instead of %d",
+						      dcc_su2str_err(&req_su),
+						      req_len,
+						      helper.req_len);
+				continue;
+			}
+
+			/* we might get stray packets because we cannot
+			 * connect to a single port */
+			if (!DCC_SU_EQ(&helper.su, &helper.su)) {
+				if (helper.debug)
+					dcc_trace_msg("DNSBL helper"
+						    " request from"
+						    " %s instead of %s",
+						    dcc_su2str_err(&req_su),
+						    dcc_su2str_err(&helper.su));
+				continue;
+			}
+
+			if (req.hdr.magic != HELPER_MAGIC_REQ
+			    || req.hdr.version != HELPER_VERSION) {
+				if (helper.debug)
+					dcc_trace_msg("DNSBL helper"
+						      " recvfrom(parent %s)"
+						      " magic=%#08x",
+						      dcc_su2str_err(&req_su),
+						      req.hdr.magic);
+				continue;
+			}
+			break;
+		}
+		gettimeofday(&now, 0);
+		alarm(0);
+
+		/* do not bother working if it is already too late to answer,
+		 * perhaps because a previous helper died */
+		if (tv_diff2us(&now, &req.hdr.start) >= req.hdr.avail_us) {
+			if (helper.debug > 1)
+				dcc_trace_msg("%s DNSBL helper"
+					      " already too late to answer",
+					      req.hdr.id);
+			continue;
+		}
+
+		memset(&resp, 0, sizeof(resp));
+		resp.hdr.magic = HELPER_MAGIC_RESP;
+		resp.hdr.version = HELPER_VERSION;
+		resp.hdr.sn = req.hdr.sn;
+
+		/* do the work and send an answer if we have one */
+		if (!dnsbl_work(&req, &resp))
+			continue;
+
+		/* do not answer if it is too late */
+		gettimeofday(&now, 0);
+		if (tv_diff2us(&now, &req.hdr.start) > (req.hdr.avail_us
+							+ DCC_US/2)) {
+			if (helper.debug > 1)
+				dcc_trace_msg("%s DNSBL helper"
+					      " too late to answer",
+					      req.hdr.id);
+			continue;
+		}
+
+		i = sendto(helper.soc, &resp, sizeof(resp), 0,
+			   &req_su.sa, DCC_SU_LEN(&req_su));
+		if (i != sizeof(resp)) {
+			if (i < 0)
+				dcc_error_msg("%s helper sendto(%s): %s",
+					      req.hdr.id,
+					      dcc_su2str_err(&req_su),
+					      ERROR_STR());
+			else
+				dcc_error_msg("%s helper sendto(%s)=%d",
+					      req.hdr.id,
+					      dcc_su2str_err(&req_su), i);
+		}
+	}
+}
+
+
+
+/* ask a helper process to do some filtering */
+u_char					/* 1=got an answer */
+ask_helper(DCC_CLNT_CTXT *ctxt, void *log_ctxt,
+	   time_t avail_us,		/* spend at most this much time */
+	   HELPER_REQ_HDR *req,		/* request sent to helper */
+	   int req_len,
+	   HELPER_RESP_HDR *resp,	/* put answer here */
+	   int resp_len)
+{
+	DCC_EMSG emsg;
+	DCC_SOCKU send_su;
+	DCC_SOCKLEN_T su_len;
+	DCC_SOCKU recv_su;
+	char sustr[DCC_SU2STR_SIZE];
+	u_char counted;
+	u_int gen;
+	struct timeval now;
+	time_t us;
+	int i;
+
+	emsg[0] = '\0';
+
+	/* keep the lock until we have sent our request and wake-up call
+	 * to ensure that some other thread does not shut down all of
+	 * the helpers. */
+	helper_lock();
+	gettimeofday(&now, 0);
+
+	/* If it has been a long time since we used a helper, then the last
+	 * of them might be about to die of boredom.  Fix that race by
+	 * restarting all of them.
+	 * Most dying helpers should be reaped by the totals timer thread. */
+	if (helper.idle_helpers > 0
+	    && DCC_IS_TIME(now.tv_sec, helper.idle_restart,
+			   HELPER_IDLE_RESTART)) {
+		reap_helpers(1);
+		if (helper.idle_helpers > 0)
+			terminate_helpers();
+		gettimeofday(&now, 0);
+	}
+	helper.idle_restart = now.tv_sec + HELPER_IDLE_RESTART;
+
+	if (helper.idle_helpers - helper.slow_helpers > 0) {
+		/* avoid taking the last idle helper because there are
+		 * usually fewer truly idle helpers than we think because
+		 * we don't always wait for them to finish */
+		if (helper.idle_helpers > 2
+		    || helper.total_helpers >= helper.max_helpers
+		    || !new_helper(emsg, ctxt, log_ctxt))
+			--helper.idle_helpers;
+		counted = 1;
+	} else if (helper.total_helpers >= helper.max_helpers) {
+		if (helper.debug > 0)
+			thr_trace_msg(log_ctxt, "%s DNSBL %d idle, %d slow, and"
+				      " %d total DNSBL helpers", req->id,
+				      helper.idle_helpers, helper.slow_helpers,
+				      helper.total_helpers);
+		counted = 0;
+	} else {
+		if (!new_helper(emsg, ctxt, log_ctxt)) {
+			helper_unlock();
+			return 0;
+		}
+		counted = 1;
+	}
+
+	 /* The resolution of the BIND timeout limits is seconds, so even on
+	  * systems where the timeout limits work, the helper might delay
+	  * a second or two.  To keep the count of idle helpers as accurate
+	  * as possible, always wait at least 1 second for an answer
+	  * and 2 seconds for an answer to reach the parent. */
+	req->avail_us = avail_us;
+	avail_us += DCC_US;
+	req->start = now;
+	req->magic = HELPER_MAGIC_REQ;
+	req->version = HELPER_VERSION;
+
+	req->sn = ++helper.sn;
+	gen = helper.gen;
+
+	/* snapshot the address in case another thread restarts the helpers */
+	send_su = helper.su;
+
+	/* Use sendto() if the socket is not already conencted.
+	 * If it is already connected, then on many systems other than Linux,
+	 * it is possible and presumably cheap to reconnected it, so do so. */
+	if (!helper_soc_open(emsg, ctxt)) {
+		thr_trace_msg(log_ctxt, "DNSBL reopen(): %s", emsg);
+		help_finish(gen, 0, counted, 1);
+		helper_unlock();
+		return 0;
+	}
+	if (ctxt->conn_su.sa.sa_family != AF_UNSPEC
+	    && memcmp(&ctxt->conn_su, &send_su, sizeof(ctxt->conn_su))
+	    && !helper_soc_connect(emsg, ctxt, &send_su)) {
+		thr_trace_msg(log_ctxt, "DNSBL soc_connect(): %s", emsg);
+		help_finish(gen, 0, counted, 1);
+		helper_unlock();
+		return 0;
+	}
+	if (ctxt->conn_su.sa.sa_family == AF_UNSPEC) {
+		i = sendto(ctxt->soc, req, req_len, 0,
+			   &send_su.sa, DCC_SU_LEN(&send_su));
+	} else {
+		i = send(ctxt->soc, req, req_len, 0);
+	}
+	if (i != req_len) {
+		if (i < 0)
+			thr_trace_msg(log_ctxt, "%s DNSBL sendto(%s): %s",
+				      req->id, dcc_su2str(sustr, sizeof(sustr),
+							&send_su),
+				      ERROR_STR());
+		else
+			thr_trace_msg(log_ctxt, "%s DNSBL sendto(%s)=%d",
+				      req->id, dcc_su2str(sustr, sizeof(sustr),
+							&send_su),
+				      i);
+		help_finish(gen, 0, counted, 1);
+		helper_unlock();
+		return 0;
+	}
+
+	/* awaken a helper */
+	i = write(helper.pipe_write, "x", 1);
+	if (i != 1) {
+		if (i < 0)
+			thr_trace_msg(log_ctxt,
+				      "%s DNSBL write(pipe_write=%d): %s",
+				      req->id, helper.pipe_write, ERROR_STR());
+		else
+			thr_trace_msg(log_ctxt,
+				      "%s DNSBL write(pipe_write)=%d",
+				      req->id, i);
+		help_finish(gen, 0, counted, 1);
+		helper_unlock();
+		return 0;
+	}
+	helper_unlock();
+
+	for (;;) {
+		us = avail_us - tv_diff2us(&now, &req->start);
+		if (us < 0)
+			us = 0;
+		i = dcc_select_poll(0, ctxt->soc, 1, us);
+		if (i < 0) {
+			thr_error_msg(log_ctxt, "%s DNSBL select_poll: %s",
+				      req->id, ERROR_STR());
+			help_finish(gen, 0, counted, 0);
+			return 0;
+		}
+		gettimeofday(&now, 0);
+
+		if (i == 0) {
+			if (helper.debug)
+				thr_trace_msg(log_ctxt,
+					      "%s DNSBL no helper answer after"
+					      " %1.f sec", req->id,
+					      tv_diff2us(&now, &req->start)
+					      / (DCC_US*1.0));
+			helper_lock();
+			if (helper.slow_helpers<=helper.total_helpers/MAX_SLOW)
+				++helper.slow_helpers;
+			help_finish(gen, 0, counted, 1);
+			helper_unlock();
+			return 0;
+		}
+
+
+		su_len = sizeof(recv_su);
+		i = recvfrom(ctxt->soc, resp, resp_len,
+			     0, &recv_su.sa, &su_len);
+		/* because we are using UDP, we might get stray packets */
+		if (i != resp_len) {
+			if (i < 0) {
+				thr_trace_msg(log_ctxt,
+					      "%s DNSBL recvfrom(): %s",
+					      req->id, ERROR_STR());
+				if (DCC_BLOCK_ERROR())
+					continue;
+				help_finish(gen, 0, counted, 0);
+				return 0;
+			}
+			if (helper.debug > 1)
+				thr_trace_msg(log_ctxt,
+					      "%s DNSBL recvfrom(%s)=%d",
+					      req->id,
+					      dcc_su2str_err(&recv_su), i);
+			continue;
+		}
+		if (!DCC_SU_EQ(&send_su, &recv_su)) {
+			if (helper.debug != 0)
+				thr_trace_msg(log_ctxt,
+					      "%s DNSBL recvfrom(%s)"
+					      " instead of %s",
+					      req->id,
+					      dcc_su2str_err(&recv_su),
+					      dcc_su2str_err(&send_su));
+			continue;
+		}
+		if (resp->magic != HELPER_MAGIC_RESP
+		    || resp->version != HELPER_VERSION
+		    || resp->sn != req->sn) {
+			if (helper.debug >1 )
+				thr_trace_msg(log_ctxt,
+					      "%s DNSBL recvfrom(%s)"
+					      " magic=%#08x sn=%d",
+					      req->id, dcc_su2str_err(&recv_su),
+					      resp->magic, resp->sn);
+			continue;
+		}
+
+		help_finish(gen, 1, counted, 0);
+		return 1;
+	}
+}
+#endif /* HAVE_HELPERS */
diff -r 000000000000 -r c7f6b056b673 dcclib/hstrerror.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/hstrerror.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,26 @@
+/* compatibility hack for old systems that don't have hstrerror() */
+
+#include "dcc_defs.h"
+
+#include <stdio.h>
+
+static const char *h_errlist[] = {
+	"Resolver Error 0 (no error)",
+	"Unknown host",			/* 1 HOST_NOT_FOUND */
+	"Host name lookup failure",	/* 2 TRY_AGAIN */
+	"Unknown server error",		/* 3 NO_RECOVERY */
+	"No address associated with name",  /* 4 NO_ADDRESS */
+};
+#define H_NERR ((int)(sizeof(h_errlist)/sizeof( h_errlist[0])))
+
+const char *
+dcc_hstrerror(int err)
+{
+    static char buf[64];
+
+    if (err < 0 || err > H_NERR || h_errlist[err] == NULL) {
+	snprintf(buf, sizeof(buf), "Error %d", err);
+	return buf;
+    }
+    return h_errlist[err];
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/id2str.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/id2str.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,81 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.15 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include "dcc_xhdr.h"
+
+
+const char *
+id2str(char *buf, u_int buf_len, DCC_CLNT_ID id_auth)
+{
+	switch (id_auth) {
+	case DCC_ID_INVALID:
+		STRLCPY(buf, DCC_XHDR_ID_INVALID, buf_len);
+		return buf;
+
+	case DCC_ID_WHITE:
+		*buf = '\0';
+		break;
+	case DCC_ID_COMP:
+		STRLCPY(buf, DCC_XHDR_ID_COMP, buf_len);
+		return buf;
+
+	case DCC_ID_SRVR_SIMPLE:
+		STRLCPY(buf, DCC_XHDR_ID_SIMPLE, buf_len);
+		return buf;
+	case DCC_ID_SRVR_REP_OK:
+		STRLCPY(buf, DCC_XHDR_ID_REP_OK, buf_len);
+		return buf;
+	case DCC_ID_SRVR_IGNORE:
+		STRLCPY(buf, DCC_XHDR_ID_IGNORE, buf_len);
+		return buf;
+	case DCC_ID_SRVR_ROGUE:
+		STRLCPY(buf, DCC_XHDR_ID_ROGUE, buf_len);
+		return buf;
+
+	default:
+		if (id_auth & DCC_SRVR_ID_AUTH)
+			snprintf(buf, buf_len, "auth %d",
+				 id_auth & ~DCC_SRVR_ID_AUTH);
+		else
+			snprintf(buf, buf_len, "%d",
+				 id_auth & ~DCC_SRVR_ID_AUTH);
+	}
+	return buf;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/inet_ntop.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/inet_ntop.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,216 @@
+/* compatibility hack for systems without any IPv6 support */
+
+/*
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM 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.
+ */
+
+/*  Rhyolite Software DCC 1.3.103-1.6 $Revision$ */
+
+
+#include "dcc_defs.h"
+
+#include <sys/types.h>
+
+#ifndef DCC_WIN32
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+
+
+#ifndef NS_IN6ADDRSZ
+#define NS_IN6ADDRSZ 16
+#endif
+#ifndef NS_INT16SZ
+#define NS_INT16SZ 2
+#endif
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static const char *inet_ntop4(const u_char *src, char *dst, size_t size);
+static const char *inet_ntop6(const u_char *src, char *dst, size_t size);
+
+/* char *
+ * inet_ntop(af, src, dst, size)
+ *	convert a network format address to presentation format.
+ * return:
+ *	pointer to presentation format address (`dst'), or NULL (see errno).
+ * author:
+ *	Paul Vixie, 1996.
+ */
+const char *
+dcc_inet_ntop(int af, const void *src, char *dst, size_t size)
+{
+	switch (af) {
+	case AF_INET:
+		return (inet_ntop4(src, dst, size));
+	case AF_INET6:
+		return (inet_ntop6(src, dst, size));
+	default:
+#ifdef DCC_WIN32
+		WSASetLastError(WSAEPROTONOSUPPORT);
+#else
+		errno = EAFNOSUPPORT;
+#endif
+		return (NULL);
+	}
+	/* NOTREACHED */
+}
+
+/* const char *
+ * inet_ntop4(src, dst, size)
+ *	format an IPv4 address
+ * return:
+ *	`dst' (as a const)
+ * notes:
+ *	(1) uses no statics
+ *	(2) takes a u_char* not an in_addr as input
+ * author:
+ *	Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop4(src, dst, size)
+	const u_char *src;
+	char *dst;
+	size_t size;
+{
+	static const char fmt[] = "%u.%u.%u.%u";
+	char tmp[sizeof "255.255.255.255"];
+
+	if (SPRINTF((tmp, fmt, src[0], src[1], src[2], src[3])) >= size) {
+#ifdef DCC_WIN32
+		WSASetLastError(ENOSPC);
+#else
+		errno = ENOSPC;
+#endif
+		return (NULL);
+	}
+	strcpy(dst, tmp);
+	return (dst);
+}
+
+/* const char *
+ * inet_ntop6(src, dst, size)
+ *	convert IPv6 binary address into presentation (printable) format
+ * author:
+ *	Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop6(src, dst, size)
+	const u_char *src;
+	char *dst;
+	size_t size;
+{
+	/*
+	 * Note that int32_t and int16_t need only be "at least" large enough
+	 * to contain a value of the specified size.  On some systems, like
+	 * Crays, there is no such thing as an integer variable with 16 bits.
+	 * Keep this in mind if you think this function should have been coded
+	 * to use pointer overlays.  All the world's not a VAX.
+	 */
+	char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
+	struct { int base, len; } best, cur;
+	u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
+	int i;
+
+	/*
+	 * Preprocess:
+	 *	Copy the input (bytewise) array into a wordwise array.
+	 *	Find the longest run of 0x00's in src[] for :: shorthanding.
+	 */
+	memset(words, '\0', sizeof words);
+	for (i = 0; i < NS_IN6ADDRSZ; i++)
+		words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+	best.base = -1;
+	best.len = 0;
+	cur.base = -1;
+	cur.len = 0;
+	for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+		if (words[i] == 0) {
+			if (cur.base == -1)
+				cur.base = i, cur.len = 1;
+			else
+				cur.len++;
+		} else {
+			if (cur.base != -1) {
+				if (best.base == -1 || cur.len > best.len)
+					best = cur;
+				cur.base = -1;
+			}
+		}
+	}
+	if (cur.base != -1) {
+		if (best.base == -1 || cur.len > best.len)
+			best = cur;
+	}
+	if (best.base != -1 && best.len < 2)
+		best.base = -1;
+
+	/*
+	 * Format the result.
+	 */
+	tp = tmp;
+	for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+		/* Are we inside the best run of 0x00's? */
+		if (best.base != -1 && i >= best.base &&
+		    i < (best.base + best.len)) {
+			if (i == best.base)
+				*tp++ = ':';
+			continue;
+		}
+		/* Are we following an initial run of 0x00s or any real hex? */
+		if (i != 0)
+			*tp++ = ':';
+		/* Is this address an encapsulated IPv4? */
+		if (i == 6 && best.base == 0 &&
+		    (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
+			if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
+				return (NULL);
+			tp += strlen(tp);
+			break;
+		}
+		tp += SPRINTF((tp, "%x", words[i]));
+	}
+	/* Was it a trailing run of 0x00's? */
+	if (best.base != -1 && (best.base + best.len) ==
+	    (NS_IN6ADDRSZ / NS_INT16SZ))
+		*tp++ = ':';
+	*tp++ = '\0';
+
+	/*
+	 * Check for overflow, copy, and we're done.
+	 */
+	if ((size_t)(tp - tmp) > size) {
+#ifdef DCC_WIN32
+		WSASetLastError(ENOSPC);
+#else
+		errno = ENOSPC;
+#endif
+		return (NULL);
+	}
+	strcpy(dst, tmp);
+	return (dst);
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/ipv6_conv.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/ipv6_conv.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,135 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.21 $Revision$
+ */
+
+#include "dcc_defs.h"
+
+
+/* convert IPv4 address to IPv6 */
+void
+dcc_ipv4toipv6(struct in6_addr *dst, const struct in_addr src)
+{
+	/* do it in an order that allows dst==src */
+	dst->s6_addr32[3] = src.s_addr;
+	dst->s6_addr32[0] = 0;
+	dst->s6_addr32[1] = 0;
+	dst->s6_addr32[2] = htonl(0xffff);
+}
+
+
+
+/* try to convert IPv6 address to IPv4 address */
+u_char					/* 1=did it */
+dcc_ipv6toipv4(struct in_addr *dst, const struct in6_addr *src)
+{
+	if (DCC_IN6_ADDR_V4MAPPED(src)) {
+		dst->s_addr = src->s6_addr32[3];
+		return 1;
+	}
+
+	if (src->s6_addr32[0] != 0
+	    || src->s6_addr32[1] != 0
+	    || src->s6_addr32[2] != 0)
+		return 0;
+
+	if (src->s6_addr32[3] == ntohl(1))
+		dst->s_addr = ntohl(0x7f000001);
+	else
+		dst->s_addr = src->s6_addr32[3];
+	return 1;
+}
+
+
+
+void
+dcc_su2ip(DCC_IP *ip, const DCC_SOCKU *su)
+{
+	memset(ip, 0, sizeof(*ip));
+	if ((ip->family = su->sa.sa_family) == AF_UNSPEC)
+		return;
+	ip->port = DCC_SU_PORT(su);
+	if (su->sa.sa_family == AF_INET)
+		ip->u.v4 = su->ipv4.sin_addr;
+	else
+		ip->u.v6 = su->ipv6.sin6_addr;
+}
+
+
+
+/* try to convert IPv6 DCC_SOCKU to IPv4 */
+u_char					/* 1=did it */
+dcc_ipv6sutoipv4(DCC_SOCKU *dst, const DCC_SOCKU *src)
+{
+	struct in_addr addr4;
+
+	if (src->sa.sa_family != AF_INET6) {
+		if (src != dst)
+			*dst = *src;
+		return (src->sa.sa_family == AF_INET);
+	}
+
+	if (!dcc_ipv6toipv4(&addr4, &src->ipv6.sin6_addr)) {
+		if (src != dst)
+			*dst = *src;
+		return 0;
+	}
+
+	dcc_mk_su(dst, AF_INET, &addr4, *DCC_SU_PORTP(src));
+	return 1;
+}
+
+
+
+/* try to convert IPv4 DCC_SOCKU to IPv6
+ *	This function is mentioned in dccifd/dccif-test/dccif-test.c
+ *	and so cannot change lightly. */
+u_char					/* 1=did it */
+dcc_ipv4sutoipv6(DCC_SOCKU *dst, const DCC_SOCKU *src)
+{
+	struct in6_addr addr6;
+
+	if (src->sa.sa_family != AF_INET) {
+		if (src != dst)
+			*dst = *src;
+		return (src->sa.sa_family == AF_INET6);
+	}
+
+	dcc_ipv4toipv6(&addr6, src->ipv4.sin_addr);
+	dcc_mk_su(dst, AF_INET6, &addr6, *DCC_SU_PORTP(src));
+	return 1;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/load_ids.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/load_ids.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,457 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * client-ID and password parsing
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.50 $Revision$
+ */
+
+#include "dcc_clnt.h"
+#include "dcc_ids.h"
+#include "dcc_heap_debug.h"
+
+/* must be in dcclib for WIN32 */
+
+DCC_PATH ids_path;
+
+time_t ids_mtime;
+
+
+/* authenticated client database
+ * assume there will be at most 500 clients and servers known to each server */
+#define ID_TBL_LEN 509			/* must be prime */
+#define ID_TBL_MAX (ID_TBL_LEN*4)
+#define ID_HASH(id) (id % ID_TBL_LEN)
+#define ID_HASH_ENTRY(id) id_tbl_hash[ID_HASH(id)]
+static ID_TBL *id_tbl_hash[ID_TBL_LEN];
+static int id_tbl_len = 0;
+static ID_TBL *id_tbl_free;
+
+
+/* find an ID_TBL entry	*/
+ID_TBL *
+find_id_tbl(DCC_CLNT_ID id)
+{
+	ID_TBL *tp;
+
+	for (tp = ID_HASH_ENTRY(id); tp != 0; tp = tp->fwd) {
+		if (tp->id == id)
+			return tp;
+	}
+	return 0;
+}
+
+
+
+/* add an ID_TBL entry that is known to be absent */
+ID_TBL *
+add_id_tbl(DCC_CLNT_ID id)
+{
+	ID_TBL *tp, **tpp;
+	int i;
+
+	/* make more entries if necessary */
+	if (!id_tbl_free) {
+		i = 16;
+		if (id_tbl_len <= ID_TBL_MAX
+		    && id_tbl_len+i > ID_TBL_MAX)
+			dcc_error_msg("ID table overflow");
+		id_tbl_len += i;
+		tp = dcc_malloc(i*sizeof(*tp));
+		do {
+			tp->fwd = id_tbl_free;
+			id_tbl_free = tp;
+		} while (++tp, --i > 0);
+	}
+
+	tp = id_tbl_free;
+	id_tbl_free = tp->fwd;
+
+	memset(tp, 0, sizeof(*tp));
+	tp->id = id;
+	tpp = &ID_HASH_ENTRY(id);
+	tp->fwd = *tpp;
+	*tpp = tp;
+
+	return tp;
+}
+
+
+
+ID_TBL *
+enum_ids(ID_TBL *tp)
+{
+	ID_TBL **tpp;
+
+	if (!tp) {
+		tpp = id_tbl_hash;
+	} else {
+		if (tp->fwd)
+			return tp->fwd;
+		tpp = &ID_HASH_ENTRY(tp->id)+1;
+	}
+	while (tpp <= LAST(id_tbl_hash)) {
+		tp = *tpp;
+		if (tp)
+			return tp;
+		++tpp;
+	}
+	return 0;
+}
+
+
+
+u_char					/* 0=bad 1=ok 2=forever */
+parse_dccd_delay(DCC_EMSG emsg, time_t *delay_usp, u_int *delay_inflatep,
+		 const char *val,
+		 const char *fnm, int lno)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	time_t delay_ms;
+	u_long l;
+	char *p1, *p2;
+
+	*delay_inflatep = DCC_ANON_INFLATE_OFF;
+	*delay_usp = DCC_ANON_DELAY_US_BLACKLIST;
+	if (!strcasecmp(val, "forever"))
+		return 2;
+
+	delay_ms = strtoul(val, &p1, 10);
+
+	if (delay_ms > DCC_ANON_DELAY_US_BLACKLIST/1000) {
+		dcc_pemsg(EX_DATAERR, emsg, "invalid delay \"%s\"%s > %d", val,
+			  fnm_lno(&fnm_buf, fnm, lno),
+			  DCC_ANON_DELAY_US_BLACKLIST/1000);
+		return 0;
+	} else if (*p1 == '\0') {
+		*delay_inflatep = DCC_ANON_INFLATE_OFF;
+	} else if (*p1 != ',' && *p1 != '*') {
+		dcc_pemsg(EX_DATAERR, emsg, "unrecognized delay \"%s\"%s", val,
+			  fnm_lno(&fnm_buf, fnm, lno));
+		return 0;
+	} else {
+		l = strtoul(++p1, &p2, 10);
+		if (*p2 != '\0') {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "unrecognized delay inflation \"%s\"%s",
+				  p1,
+				  fnm_lno(&fnm_buf, fnm, lno));
+			return 0;
+		}
+		if (l == 0)
+			l = DCC_ANON_INFLATE_OFF;
+		*delay_inflatep = l;
+	}
+
+	*delay_usp = delay_ms*1000;
+	return 1;
+}
+
+
+
+u_char
+set_ids_path(DCC_EMSG emsg, const char *ids_nm)
+{
+	if (!ids_nm || ids_nm[0] == '\0')
+		ids_nm = IDS_NM_DEF;
+	if (!fnm2rel(ids_path, ids_nm, 0)) {
+		dcc_pemsg(EX_DATAERR, emsg, "\"%s\" is a bad ids file name",
+			  ids_nm);
+		return 0;
+	}
+	return 1;
+}
+
+
+
+/* (re)load the client-ID and password database
+ * -1=failed to find target,  0=sick file,  1=ok,  2=file unchanged */
+int
+load_ids(DCC_EMSG emsg,
+	 DCC_CLNT_ID tgt_id,		/* DCC_ID_ANON or needed ID */
+	 const ID_TBL **tgt_tbl,
+	 u_char force)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	ID_TBL t, *tp, **tpp;
+	FILE *f;
+	int lno, passwords;
+	u_char found_it;
+	int result;
+	char buf[sizeof(ID_TBL)*2+1];
+	const char *bufp;
+	char id_buf[30];
+	struct stat sb;
+	char *p, *p1;
+
+	if (tgt_tbl)
+		*tgt_tbl = 0;
+
+	if (!set_ids_path(emsg, 0))
+		return -1;
+
+	if (!force) {
+		if (!dcc_ck_private(emsg, &sb, ids_path, -1)) {
+			ids_mtime = 0;
+			return -1;
+		}
+
+		if (ids_mtime == sb.st_mtime)
+			return 2;
+	}
+
+	f = fopen(ids_path, "r");
+	if (!f) {
+		dcc_pemsg(EX_NOINPUT, emsg, "fopen(%s): %s",
+			  fnm2abs_err(0, ids_path), ERROR_STR());
+		return -1;
+	}
+
+	/* the file contains passwords, so refuse to use it if anyone else
+	 * can read it */
+	if (!dcc_ck_private(emsg, &sb, ids_path, fileno(f))) {
+		fclose(f);
+		ids_mtime = 0;
+		return -1;
+	}
+
+	/* empty the table */
+	for (tpp = id_tbl_hash; tpp <= LAST(id_tbl_hash); ++tpp) {
+		while ((tp = *tpp) != 0) {
+			*tpp = tp->fwd;
+			memset(tp, 0, sizeof(*tp));
+			tp->fwd = id_tbl_free;
+			id_tbl_free = tp;
+		}
+	}
+
+	ids_mtime = sb.st_mtime;
+
+	passwords = 0;
+	lno = 0;
+	result = 1;
+	found_it = (tgt_id == DCC_ID_ANON);
+	for (;;) {
+		/* read and parse a line contain a client-ID and key(s) */
+		bufp = fgets(buf, sizeof(buf), f);
+		if (!bufp) {
+			if (ferror(f))
+				dcc_pemsg(EX_IOERR, emsg, "fgets(%s): %s",
+					  fnm2abs_err(0, ids_path),
+					  ERROR_STR());
+			break;
+		}
+		++lno;
+
+		/* Ignore blank lines and lines starting with '#'.
+		 * Note that '#' flags a comment only at the start of
+		 * the line to avoid dealing with the escaping hassles
+		 * of allowing '#' in passwords. */
+		bufp += strspn(bufp, DCC_WHITESPACE);
+		if (*bufp == '\0' || *bufp == '#')
+			continue;
+
+		memset(&t, 0, sizeof(t));
+		t.delay_inflate = DCC_ANON_INFLATE_OFF;
+
+		/* Each substantive line has the form:
+		 *
+		 *	ID[,rpt-ok][,delay=ms[*inflate]] password1 password2
+		 *	ID,delay=forever
+		 *
+		 *  Both passwords are always accepted.  They are intended
+		 *  to be the previous and current or the current and
+		 *  next to allow the password to be changed at both the
+		 *  client and the server without loss of service. */
+
+		bufp = dcc_parse_word(emsg, id_buf, sizeof(id_buf),
+				      bufp, "ID", ids_path, lno);
+		if (!bufp) {
+			result = 0;
+			continue;
+		}
+
+		if (*bufp)
+			t.flags |= ID_FLG_SETTINGS;
+
+		p = strchr(id_buf, ',');
+		if (p) {
+			*p++ = '\0';
+			t.flags |= ID_FLG_SETTINGS;
+		}
+		t.id = dcc_get_id(emsg, id_buf, ids_path, lno);
+		if (t.id == DCC_ID_INVALID) {
+			result = 0;
+			continue;
+		}
+		if (t.id == DCC_ID_ANON) {
+			if (result) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "invalid ID \"%s\"%s",
+					  id_buf,
+					  fnm_lno(&fnm_buf, ids_path, lno));
+				result = 0;
+			}
+			continue;
+		}
+
+		for (; p; p = p1) {
+			p1 = strchr(p, ',');
+			if (p1)
+				*p1++ = '\0';
+
+			if (t.id >= DCC_CLNT_ID_MIN
+			    && t.id <= DCC_CLNT_ID_MAX) {
+				if (!strcasecmp(p, "rpt-ok")
+				    || !strcasecmp(p, "rpt_ok")) {
+					t.flags |= ID_FLG_RPT_OK;
+					continue;
+				}
+
+				if (!CLITCMP(p, "delay=")) {
+					if (!parse_dccd_delay(emsg, &t.delay_us,
+							&t.delay_inflate,
+							p+LITZ("delay="),
+							ids_path, lno)) {
+					    result = 0;
+					}
+					continue;
+				}
+			}
+			if (t.id >= DCC_SRVR_ID_MIN
+			    && t.id <= DCC_SRVR_ID_MAX
+			    && t.srvr_type == 0) {
+				if (!strcasecmp(p, "simple")) {
+					continue;
+				}
+				if (!strcasecmp(p, "ignore")) {
+					continue;
+				}
+				if (!strcasecmp(p, "rogue")) {
+					continue;
+				}
+				if (!strcasecmp(p, "commercial")) {
+					continue;
+				}
+			}
+			if (result) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "invalid option \"%s\"%s", p,
+					  fnm_lno(&fnm_buf, ids_path, lno));
+				result = 0;
+			}
+		}
+
+		bufp = parse_passwd(emsg, t.cur_passwd,
+				    bufp, "current password", ids_path, lno);
+		if (!bufp) {
+			result = 0;
+			continue;
+		}
+		bufp = parse_passwd(emsg, t.next_passwd,
+				    bufp, "next password", ids_path, lno);
+		if (!bufp) {
+			result = 0;
+			continue;
+		}
+		if (*bufp != '\0') {
+			if (result) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "invalid next password for ID %d%s",
+					  t.id,
+					  fnm_lno(&fnm_buf, ids_path, lno));
+				result = 0;
+			}
+			continue;
+		}
+
+		/* put the entry into the hash table if not already present */
+		tp = find_id_tbl(t.id);
+		if (!tp)
+			tp = add_id_tbl(t.id);
+
+		/* If the ID is already present, the file is bad unless
+		 * the previous or current line is
+		 * only a placeholder showing that the ID exists. */
+		if ((tp->flags & ID_FLG_SETTINGS)
+		    && (t.flags & ID_FLG_SETTINGS)) {
+			if (result) {
+				dcc_pemsg(EX_DATAERR, emsg, "duplicate ID %d%s",
+					  t.id, fnm_lno(&fnm_buf,
+							ids_path, lno));
+				result = 0;
+			}
+		}
+
+		/* copy settings to the hash table */
+		if (t.flags & ID_FLG_SETTINGS) {
+			tp->flags = t.flags;
+			tp->srvr_type = t.srvr_type;
+			tp->delay_us = t.delay_us;
+			tp->delay_inflate = t.delay_inflate;
+
+			if (t.cur_passwd[0] != '\0') {
+				++passwords;
+				memcpy(tp->cur_passwd, t.cur_passwd,
+				       sizeof(tp->cur_passwd));
+				memcpy(tp->next_passwd, t.next_passwd,
+				       sizeof(tp->next_passwd));
+
+				/* remember target password */
+				if (t.id == tgt_id) {
+					found_it = 1;
+					if (tgt_tbl)
+					    *tgt_tbl = tp;
+				}
+			}
+		}
+	}
+	fclose(f);
+
+	if (result && !passwords) {
+		dcc_pemsg(EX_DATAERR, emsg, "%s contains no passwords",
+			  fnm2abs_err(0, ids_path));
+		result = -1;
+	}
+	if (!found_it && result >= 0) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s does not contain the password for ID %d",
+			  fnm2abs_err(0, ids_path), tgt_id);
+		result = -1;
+	}
+
+	return result;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/lock_open.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/lock_open.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,550 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * open and lock database and whitelist files
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.60 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include <signal.h>
+
+
+#ifndef DCC_WIN32
+uid_t dcc_real_uid, dcc_effective_uid;
+gid_t dcc_real_gid, dcc_effective_gid;
+#endif
+
+/* We must be SUID to read and write the system's common
+ *	connection parameter memory mapped file.
+ *	If the real UID is 0, then forget any SUID stuff and run as root.
+ *	Otherwise remember the powerful UID and the real UID, and
+ *	release the privilege of using the powerful UID */
+void
+dcc_init_priv(void)
+{
+#ifndef DCC_WIN32
+	dcc_real_uid = getuid();
+	if (dcc_real_uid == 0) {
+		dcc_effective_uid = 0;
+	} else {
+		dcc_effective_uid = geteuid();
+	}
+
+	if (0 > seteuid(dcc_real_uid))
+		dcc_error_msg("seteuid(%d): %s",
+			      (int)dcc_real_uid, ERROR_STR());
+
+	dcc_real_gid = getgid();
+	if (dcc_real_gid == 0) {
+		dcc_effective_gid = 0;
+	} else {
+		dcc_effective_gid = getegid();
+	}
+
+	if (0 > setegid(dcc_real_gid))
+		dcc_error_msg("setegid(%d): %s",
+			      (int)dcc_real_gid, ERROR_STR());
+#endif
+}
+
+
+
+#ifndef DCC_WIN32
+void
+dcc_get_priv(void)
+{
+	if (dcc_real_uid != dcc_effective_uid
+	    && 0 > seteuid(dcc_effective_uid))
+		dcc_error_msg("seteuid(%d): %s",
+			      (int)dcc_effective_uid, ERROR_STR());
+	if (dcc_real_gid != dcc_effective_gid
+	    && 0 > setegid(dcc_effective_gid))
+		dcc_error_msg("setegid(%d): %s",
+			      (int)dcc_effective_gid, ERROR_STR());
+}
+
+
+
+/* get set-UID privilege if the file is in the DCC home directory */
+u_char					/* 0=bad idea, 1=have privilege */
+dcc_get_priv_home(const char *nm)
+{
+	DCC_PATH abs_nm;
+
+	if (dcc_real_uid == dcc_effective_uid
+	    && dcc_real_gid == dcc_effective_gid)
+		return 0;
+
+	if (!fnm2abs(abs_nm, nm, 0)
+	    || strncmp(abs_nm, DCC_HOMEDIR, LITZ(DCC_HOMEDIR)))
+		return 0;
+
+	dcc_get_priv();
+	return 1;
+}
+#endif
+
+
+
+void
+dcc_rel_priv(void)
+{
+#ifndef DCC_WIN32
+	int serrno;
+
+	if (dcc_real_uid != dcc_effective_uid
+	    || dcc_real_gid != dcc_effective_gid) {
+		serrno = errno;
+		if (0 > seteuid(dcc_real_uid))
+			dcc_error_msg("seteuid(%d): %s",
+				      (int)dcc_real_uid, ERROR_STR());
+		if (0 > setegid(dcc_real_gid))
+			dcc_error_msg("setegid(%d): %s",
+				      (int)dcc_real_gid, ERROR_STR());
+		errno = serrno;
+	}
+#endif
+}
+
+
+
+/* see if a file is private */
+u_char
+dcc_ck_private(DCC_EMSG emsg, struct stat *sb, const char *nm, int fd)
+{
+	struct stat sb0;
+	DCC_PATH path;
+	int i;
+
+	if (!sb)
+		sb = &sb0;
+
+	if (fd == -1) {
+		i = stat(nm, sb);
+	} else {
+		i = fstat(fd, sb);
+	}
+	if (i < 0) {
+		dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s",
+			  fnm2abs_err(path, nm), ERROR_STR());
+		return 0;
+	}
+#ifdef HAVE_PRIVATE_FILES
+	/* even on systems like Windows without private files,
+	 * some callers want the results of the stat() */
+	if ((sb->st_mode & (S_IRGRP|S_IWGRP|S_IWOTH|S_IXOTH)) != 0) {
+		dcc_pemsg(EX_NOPERM, emsg,
+			  "%s is not private", fnm2abs_err(path, nm));
+		return 0;
+	}
+#endif
+	return 1;
+}
+
+
+
+#ifndef DCC_WIN32
+static void
+set_fl(struct flock *fl, int type, int byte_num)
+{
+	memset(fl, 0, sizeof(*fl));
+	fl->l_type = type;
+	fl->l_whence = SEEK_SET;
+	if (byte_num != DCC_LOCK_ALL_FILE) {
+		fl->l_start = byte_num;
+		fl->l_len = 1;
+	}
+}
+#endif /* DCC_WIN32 */
+
+
+
+/* open a file with a lock,
+ *	The lock can optionally be a pretense. */
+int					/* -1=failed & emsg set, >=0 if done */
+dcc_lock_open(DCC_EMSG emsg,
+	      const char *nm,
+	      int open_flags,		/* eg. O_CREAT */
+	      u_char lock_mode,		/* DCC_LOCK_OPEN_* */
+	      int lock_num,		/* which byte of the file to lock */
+	      u_char *busyp)		/* 1=file is busy */
+{
+#ifdef DCC_WIN32
+/* Win95 and Win98 do not include UNIX style file locking,
+ * including non-blocking locks and upgrading locks from shared to exclusive.
+ * In principle they could be constructed from the WIN32 synchronization
+ * primitives and a few bytes of shared memory.  Win98 does not even
+ * have a blocking file lock function.
+ */
+	HANDLE h;
+	int fd;
+	DCC_PATH path;
+
+	h = CreateFile(nm,
+		       (open_flags == O_RDONLY
+			? GENERIC_READ
+			: (GENERIC_READ | GENERIC_WRITE)),
+		       FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
+		       ((open_flags & O_EXCL)
+			? CREATE_NEW
+			: (open_flags & O_CREAT)
+			? CREATE_ALWAYS
+			: OPEN_EXISTING),
+		       FILE_ATTRIBUTE_NORMAL, 0);
+	if (h == INVALID_HANDLE_VALUE) {
+		dcc_pemsg(EX_IOERR, emsg, "CreateFile(%s): %s",
+			  fnm2abs_err(path, nm), ERROR_STR());
+		if (busyp)
+			*busyp = 0;
+		return -1;
+	}
+	fd = _open_osfhandle((long)h, 0);
+	if (fd < 0) {
+		dcc_pemsg(EX_IOERR, emsg, "_open_osfhandle(%s): %s",
+			  nm, ERROR_STR());
+		CloseHandle(h);
+		if (busyp)
+			*busyp = 0;
+		return -1;
+	}
+	if (!(lock_mode & DCC_LOCK_OPEN_NOLOCK)) {
+		if (!win32_lock(h,
+				((lock_mode & DCC_LOCK_OPEN_SHARE)
+				 ? 0 : LOCKFILE_EXCLUSIVE_LOCK)
+				| (lock_mode & DCC_LOCK_OPEN_NOWAIT
+				   ? 0 : LOCKFILE_FAIL_IMMEDIATELY))) {
+			dcc_pemsg(EX_IOERR, emsg, "open LockFileEx(%s): %s",
+				  fnm2abs_err(path, nm), ERROR_STR());
+			if (busyp)
+				*busyp = (GetLastError() == ERROR_LOCK_VIOLATION
+					  || GetLastError()==ERROR_LOCK_FAILED);
+			close(fd);
+			return -1;
+		}
+	}
+
+#else /* !DCC_WIN32 */
+	static u_char checked_stdio = 0;
+	int fd;
+	struct flock fl;
+	DCC_PATH path;
+
+	/* ensure 0, 1, and 2 are open so none of our real files get
+	 * those file descriptors and are used as stdin, stdout, or stderr */
+	if (!checked_stdio) {
+		clean_stdio();
+		checked_stdio = 1;
+	}
+
+	fd = open(nm, open_flags, 0666);
+	if (fd < 0 && dcc_get_priv_home(nm)) {
+		fd = open(nm, open_flags, 0666);
+		dcc_rel_priv();
+	}
+	if (fd < 0) {
+		if (errno == EACCES)
+			dcc_pemsg(EX_NOINPUT, emsg,
+				  "lock_open(%s): %s;"
+				  " file not writeable for locking",
+				  fnm2abs_err(path, nm), ERROR_STR());
+		else
+			dcc_pemsg(EX_NOINPUT, emsg,
+				  "lock_open(%s): %s",
+				  fnm2abs_err(path, nm), ERROR_STR());
+		if (busyp)
+			*busyp = DCC_BLOCK_ERROR();
+		return -1;
+	}
+
+	if (0 > fcntl(fd, F_SETFD, FD_CLOEXEC)) {
+		if (busyp)
+			*busyp = 0;
+		dcc_pemsg(EX_IOERR, emsg,
+			  "fcntl(F_SETFD FD_CLOEXEC %s %d): %s",
+			  fnm2abs_err(path, nm), lock_num, ERROR_STR());
+		close(fd);
+		return -1;
+	}
+
+	/* We may already have a lock on the file under a different file
+	 * descriptor.  If not, try to get a lock */
+	if (lock_mode != DCC_LOCK_OPEN_NOLOCK) {
+		set_fl(&fl,
+		       (lock_mode & DCC_LOCK_OPEN_SHARE) ? F_RDLCK : F_WRLCK,
+		       lock_num);
+		if (0 > fcntl(fd, (lock_mode & DCC_LOCK_OPEN_NOWAIT
+				   ? F_SETLK : F_SETLKW),
+			      &fl)) {
+			dcc_pemsg(EX_NOINPUT, emsg,
+				  "open fcntl(%s F_WRLCK %s %d): %s",
+				  (lock_mode & DCC_LOCK_OPEN_NOWAIT
+				   ? "F_SETLK" : "F_SETLKW"),
+				  fnm2abs_err(path, nm), lock_num,
+				  ERROR_STR());
+			if (busyp)
+				*busyp = (DCC_BLOCK_ERROR()
+					 || errno == EACCES);	/* for SunOS */
+			close(fd);
+			return -1;
+		}
+	}
+#endif /* !DCC_WIN32 or UNIX */
+
+	if (busyp)
+		*busyp = 0;
+	return fd;
+}
+
+
+
+u_char					/* 1=done 0=failed */
+dcc_unlock_fd(DCC_EMSG emsg UATTRIB, int fd,
+	      int lock_num,		/* which byte of the file to unlock */
+	      const char *str, const char *nm)
+{
+#ifdef DCC_WIN32
+	DCC_PATH path;
+
+	if (!win32_unlock((HANDLE)_get_osfhandle(fd))) {
+		dcc_pemsg(EX_NOINPUT, emsg,
+			  "UnlockFileEx(%s%s): %s",
+			  str, fnm2abs_err(path, nm), ERROR_STR());
+		return 0;
+	}
+	return 1;
+#else
+	struct flock fl;
+	DCC_PATH path;
+
+	set_fl(&fl, F_UNLCK, lock_num);
+	if (0 > fcntl(fd, F_SETLK, &fl)) {
+#ifdef DCC_DEBUG_CLNT_LOCK
+		dcc_logbad(EX_SOFTWARE,
+			   "fcntl(F_SETLK F_UNLCK %s%s %d): %s",
+			   str, fnm2abs_err(path, nm), lock_num, ERROR_STR());
+#else
+		dcc_pemsg(EX_NOINPUT, emsg,
+			  "fcntl(F_SETLK F_UNLCK %s%s %d): %s",
+			  str, fnm2abs_err(path, nm), lock_num, ERROR_STR());
+#endif
+		return 0;
+	}
+	return 1;
+#endif /* DCC_WIN32 */
+}
+
+
+
+#ifndef DCC_WIN32
+/* cause EINTR if we cannot get the lock */
+static void
+deadman(int s UATTRIB)
+{
+#if 0
+	dcc_logbad(EX_SOFTWARE, "exclusive lock deadman timer fired");
+#endif
+}
+#endif
+
+
+
+u_char					/* 1=done 0=failed */
+dcc_exlock_fd(DCC_EMSG emsg, int fd,
+	      int lock_num,		/* which byte of the file to lock */
+	      int max_delay,
+	      const char *str, const char *nm)
+{
+#ifdef DCC_WIN32
+	DCC_PATH path;
+
+	if (!win32_lock((HANDLE)_get_osfhandle(fd), LOCKFILE_EXCLUSIVE_LOCK)) {
+		dcc_pemsg(EX_IOERR, emsg, "LockFileEx(%s %s): %s",
+			  str, fnm2abs_err(path, nm), ERROR_STR());
+		return 0;
+	}
+	return 1;
+
+#else /* !DCC_WIN32 */
+	struct flock fl;
+	DCC_PATH path;
+	time_t start;
+	u_char result;
+
+	start = time(0);
+	if (max_delay) {
+#ifdef HAVE_SIGINTERRUPT
+		siginterrupt(SIGALRM, 1);
+#endif
+		signal(SIGALRM, deadman);
+		alarm(max_delay+1);
+	}
+
+	for (;;) {
+
+		set_fl(&fl, F_WRLCK, lock_num);
+		if (0 <= fcntl(fd, F_SETLKW, &fl)) {
+			result = 1;
+			break;
+		}
+
+		/* EINTR means something is hogging the lock */
+		if (DCC_SELECT_NERROR()) {
+			time_t delta = time(0) - start;
+			if (delta < max_delay) {
+				if (delta < 0) {    /* handle time jump */
+					start = time(0);
+					delta = 0;
+				}
+				alarm(max_delay-delta+1);
+				continue;
+			}
+			dcc_pemsg(EX_NOINPUT, emsg,
+				  "fcntl(F_SETLKW F_WRLCK %s%s %d)"
+				  " failed after %d seconds",
+				  str, fnm2abs_err(path, nm), lock_num,
+				  (int)delta);
+			result = 0;
+			break;
+		}
+#ifdef DCC_DEBUG_CLNT_LOCK
+		if (errno == EDEADLK) {
+			struct flock fl2;
+			pid_t our_pid;
+
+			our_pid = getpid();
+			fl2 = fl;
+			if (0 > fcntl(fd, F_GETLK, &fl2))
+				dcc_pemsg(EX_NOINPUT, emsg,
+					  "fcntl(F_GETLK): %s", ERROR_STR());
+			dcc_logbad(EX_SOFTWARE,
+				   "%d %d fcntl(F_SETLKW F_WRLCK %s%s %d): %s",
+				   (int)our_pid, (int)fl2.l_pid,
+				   str, fnm2abs_err(path, nm), lock_num,
+				   strerror(EDEADLK));
+		}
+		if (errno == EBADF) {
+			dcc_logbad(EX_SOFTWARE,
+				   "fcntl(F_SETLKW F_WRLCK %s%s %d): %s",
+				   str, fnm2abs_err(path, nm), lock_num,
+				   ERROR_STR());
+		}
+#endif
+		dcc_pemsg(EX_NOINPUT, emsg,
+			  "fcntl(F_SETLKW F_WRLCK %s%s %d): %s",
+			  str, fnm2abs_err(path, nm), lock_num,
+			  ERROR_STR());
+		result = 0;
+		break;
+	}
+
+	if (max_delay) {
+		signal(SIGALRM, SIG_DFL);
+		alarm(0);
+	}
+	return result;
+#endif /* DCC_WIN32 */
+}
+
+
+
+/* Several systems do not update the mtimes of files modified with mmap().
+ * Some like BSD/OS delay changing the mtime until the file accessed with
+ * read().  Others including filesystems on some versions of Linux
+ * apparently never change the mtime. */
+u_char
+dcc_set_mtime(DCC_EMSG emsg, const char *nm, int fd UATTRIB,
+	      const struct timeval *mtime)
+{
+#undef DIDIT
+#if defined(HAVE_FUTIMES) && !defined(linux) && !defined(DIDIT)
+	/* some Linux systems have a broken futimes implementation that does
+	 * not work for programs started as root but that release privileges */
+	struct timeval tbuf[2], *tbufp;
+	DCC_PATH path;
+	int result;
+
+	if (mtime) {
+		tbuf[0] = *mtime;
+		tbuf[1] = *mtime;
+		tbufp = tbuf;
+	} else {
+		tbufp = 0;
+	}
+	result = futimes(fd, tbufp);
+	if (result < 0
+	    && (errno == EACCES || errno == EPERM)
+	    && dcc_real_uid != dcc_effective_uid) {
+		dcc_get_priv();
+		result = futimes(fd, tbufp);
+		dcc_rel_priv();
+	}
+	if (result < 0)
+		dcc_pemsg(EX_IOERR, emsg,"futimes(%s): %s",
+			  fnm2abs_err(path, nm), ERROR_STR());
+#define DIDIT
+#endif /* HAVE_FUTIMES */
+#if defined(HAVE_UTIME_H) && !defined(DIDIT)
+	struct utimbuf tbuf, *tbufp;
+	DCC_PATH path;
+	int result;
+
+	if (mtime) {
+		tbuf.actime = tbuf.modtime = mtime->tv_sec;
+		tbufp = &tbuf;
+	} else {
+		tbufp = 0;
+	}
+	result = utime(nm, tbufp);
+	if (result < 0
+	    && dcc_real_uid != dcc_effective_uid) {
+		dcc_get_priv();
+		result = utime(nm, tbufp);
+		dcc_rel_priv();
+	}
+	if (result < 0)
+		dcc_pemsg(EX_IOERR, emsg,"utime(%s): %s",
+			  fnm2abs_err(path, nm), ERROR_STR());
+#define DIDIT
+#endif /* HAVE_UTIME_H */
+
+#ifdef DIDIT
+	return result >= 0;
+#undef DIDIT
+#else
+	return 1;			/* WIN32 */
+#endif
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/logbad.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/logbad.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,62 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.16 $Revision$
+ */
+
+#include "dcc_defs.h"
+
+
+/* things are so sick that we must bail out */
+void NRATTRIB
+dcc_logbad(int ex_code, const char *p, ...)
+{
+	static u_char looping = 0;
+	va_list args;
+
+	if (*p >= ' ') {
+		va_start(args, p);
+		dcc_vfatal_msg(p, args);
+		va_end(args);
+	}
+
+	if (ex_code == EX_SOFTWARE)
+		abort();
+	if (looping++)
+		abort();
+
+	exit(ex_code);
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/md5.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/md5.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,317 @@
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ * rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+/* "Rhyolite Software DCC 1.3.103-1.5 $Revision$" */
+
+#include "dcc_defs.h"
+#undef HAVE_MD5
+#include "dcc_md5.h"
+
+#undef bcopy
+#define bcopy(src,dst,len) memcpy(dst,src,len)
+#undef bzero
+#define bzero(dst,len) memset(dst,0,len)
+
+/* UINT4 defines a four byte word */
+#define UINT4 u_int32_t
+
+
+#define MD5_memcpy(d,s,l) memcpy(d,s,l)
+
+/* Constants for MD5Transform routine.
+ */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform(UINT4[4], const unsigned char [64]);
+static void Encode(unsigned char *, UINT4 *, unsigned int);
+static void Decode(UINT4 *, const unsigned char *, unsigned int);
+
+static unsigned char PADDING[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+ * Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+	(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+	(a) = ROTATE_LEFT ((a), (s)); \
+	(a) += (b); \
+}
+#define GG(a, b, c, d, x, s, ac) { \
+	(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+	(a) = ROTATE_LEFT ((a), (s)); \
+	(a) += (b); \
+}
+#define HH(a, b, c, d, x, s, ac) { \
+	(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+	(a) = ROTATE_LEFT ((a), (s)); \
+	(a) += (b); \
+}
+#define II(a, b, c, d, x, s, ac) { \
+	(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+	(a) = ROTATE_LEFT ((a), (s)); \
+	(a) += (b); \
+}
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void
+MD5Init(MD5_CTX *context)
+{
+	context->count[0] = context->count[1] = 0;
+	/* Load magic initialization constants.
+	 */
+	context->state[0] = 0x67452301;
+	context->state[1] = 0xefcdab89;
+	context->state[2] = 0x98badcfe;
+	context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+ * operation, processing another message block, and updating the
+ * context.
+ */
+void
+MD5Update(MD5_CTX *context,		/* context */
+	  const void *input0,		/* input block */
+	  unsigned int inputLen)	/* length of input block */
+{
+	const unsigned char *input = input0;
+	unsigned int i, indx, partLen;
+
+	/* Compute number of bytes mod 64 */
+	indx = ((context->count[0] >> 3) & 0x3F);
+
+	/* Update number of bits */
+	if ((context->count[0] += ((UINT4)inputLen << 3))
+	    < ((UINT4)inputLen << 3))
+		context->count[1]++;
+	context->count[1] += ((UINT4)inputLen >> 29);
+
+	partLen = 64 - indx;
+
+	/* Transform as many times as possible.
+	 */
+	if (inputLen >= partLen) {
+		bcopy(input, &context->buffer[indx], partLen);
+		MD5Transform (context->state, context->buffer);
+
+		for (i = partLen; i + 63 < inputLen; i += 64)
+			MD5Transform (context->state, &input[i]);
+
+		indx = 0;
+	} else {
+		i = 0;
+	}
+
+	/* Buffer remaining input */
+	bcopy(&input[i], &context->buffer[indx], inputLen-i);
+}
+
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+  the message digest and zeroizing the context.
+ */
+void
+MD5Final(MD5_DIGEST digest,		/* message digest */
+	 MD5_CTX *context)		/* context */
+{
+	unsigned char bits[8];
+	unsigned int indx, padLen;
+
+	/* Save number of bits */
+	Encode (bits, context->count, 8);
+
+	/* Pad out to 56 mod 64.
+	 */
+	indx = (unsigned int)((context->count[0] >> 3) & 0x3f);
+	padLen = (indx < 56) ? (56 - indx) : (120 - indx);
+	MD5Update(context, PADDING, padLen);
+
+	/* Append length (before padding) */
+	MD5Update(context, bits, 8);
+
+	/* Store state in digest */
+	Encode(digest, context->state, MD5_DIGEST_LEN);
+
+	/* Zeroize sensitive information.
+	 */
+	bzero(context, sizeof(*context));
+}
+
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void
+MD5Transform(UINT4 state[4],
+	     const unsigned char block[64])
+{
+	UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+	Decode (x, block, 64);
+
+	/* Round 1 */
+	FF (a, b, c, d, x[ 0], S11, 0xd76aa478);    /* 1 */
+	FF (d, a, b, c, x[ 1], S12, 0xe8c7b756);    /* 2 */
+	FF (c, d, a, b, x[ 2], S13, 0x242070db);    /* 3 */
+	FF (b, c, d, a, x[ 3], S14, 0xc1bdceee);    /* 4 */
+	FF (a, b, c, d, x[ 4], S11, 0xf57c0faf);    /* 5 */
+	FF (d, a, b, c, x[ 5], S12, 0x4787c62a);    /* 6 */
+	FF (c, d, a, b, x[ 6], S13, 0xa8304613);    /* 7 */
+	FF (b, c, d, a, x[ 7], S14, 0xfd469501);    /* 8 */
+	FF (a, b, c, d, x[ 8], S11, 0x698098d8);    /* 9 */
+	FF (d, a, b, c, x[ 9], S12, 0x8b44f7af);    /* 10 */
+	FF (c, d, a, b, x[10], S13, 0xffff5bb1);    /* 11 */
+	FF (b, c, d, a, x[11], S14, 0x895cd7be);    /* 12 */
+	FF (a, b, c, d, x[12], S11, 0x6b901122);    /* 13 */
+	FF (d, a, b, c, x[13], S12, 0xfd987193);    /* 14 */
+	FF (c, d, a, b, x[14], S13, 0xa679438e);    /* 15 */
+	FF (b, c, d, a, x[15], S14, 0x49b40821);    /* 16 */
+
+	/* Round 2 */
+	GG (a, b, c, d, x[ 1], S21, 0xf61e2562);    /* 17 */
+	GG (d, a, b, c, x[ 6], S22, 0xc040b340);    /* 18 */
+	GG (c, d, a, b, x[11], S23, 0x265e5a51);    /* 19 */
+	GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa);    /* 20 */
+	GG (a, b, c, d, x[ 5], S21, 0xd62f105d);    /* 21 */
+	GG (d, a, b, c, x[10], S22,  0x2441453);    /* 22 */
+	GG (c, d, a, b, x[15], S23, 0xd8a1e681);    /* 23 */
+	GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8);    /* 24 */
+	GG (a, b, c, d, x[ 9], S21, 0x21e1cde6);    /* 25 */
+	GG (d, a, b, c, x[14], S22, 0xc33707d6);    /* 26 */
+	GG (c, d, a, b, x[ 3], S23, 0xf4d50d87);    /* 27 */
+	GG (b, c, d, a, x[ 8], S24, 0x455a14ed);    /* 28 */
+	GG (a, b, c, d, x[13], S21, 0xa9e3e905);    /* 29 */
+	GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8);    /* 30 */
+	GG (c, d, a, b, x[ 7], S23, 0x676f02d9);    /* 31 */
+	GG (b, c, d, a, x[12], S24, 0x8d2a4c8a);    /* 32 */
+
+	/* Round 3 */
+	HH (a, b, c, d, x[ 5], S31, 0xfffa3942);    /* 33 */
+	HH (d, a, b, c, x[ 8], S32, 0x8771f681);    /* 34 */
+	HH (c, d, a, b, x[11], S33, 0x6d9d6122);    /* 35 */
+	HH (b, c, d, a, x[14], S34, 0xfde5380c);    /* 36 */
+	HH (a, b, c, d, x[ 1], S31, 0xa4beea44);    /* 37 */
+	HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9);    /* 38 */
+	HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60);    /* 39 */
+	HH (b, c, d, a, x[10], S34, 0xbebfbc70);    /* 40 */
+	HH (a, b, c, d, x[13], S31, 0x289b7ec6);    /* 41 */
+	HH (d, a, b, c, x[ 0], S32, 0xeaa127fa);    /* 42 */
+	HH (c, d, a, b, x[ 3], S33, 0xd4ef3085);    /* 43 */
+	HH (b, c, d, a, x[ 6], S34,  0x4881d05);    /* 44 */
+	HH (a, b, c, d, x[ 9], S31, 0xd9d4d039);    /* 45 */
+	HH (d, a, b, c, x[12], S32, 0xe6db99e5);    /* 46 */
+	HH (c, d, a, b, x[15], S33, 0x1fa27cf8);    /* 47 */
+	HH (b, c, d, a, x[ 2], S34, 0xc4ac5665);    /* 48 */
+
+	/* Round 4 */
+	II (a, b, c, d, x[ 0], S41, 0xf4292244);    /* 49 */
+	II (d, a, b, c, x[ 7], S42, 0x432aff97);    /* 50 */
+	II (c, d, a, b, x[14], S43, 0xab9423a7);    /* 51 */
+	II (b, c, d, a, x[ 5], S44, 0xfc93a039);    /* 52 */
+	II (a, b, c, d, x[12], S41, 0x655b59c3);    /* 53 */
+	II (d, a, b, c, x[ 3], S42, 0x8f0ccc92);    /* 54 */
+	II (c, d, a, b, x[10], S43, 0xffeff47d);    /* 55 */
+	II (b, c, d, a, x[ 1], S44, 0x85845dd1);    /* 56 */
+	II (a, b, c, d, x[ 8], S41, 0x6fa87e4f);    /* 57 */
+	II (d, a, b, c, x[15], S42, 0xfe2ce6e0);    /* 58 */
+	II (c, d, a, b, x[ 6], S43, 0xa3014314);    /* 59 */
+	II (b, c, d, a, x[13], S44, 0x4e0811a1);    /* 60 */
+	II (a, b, c, d, x[ 4], S41, 0xf7537e82);    /* 61 */
+	II (d, a, b, c, x[11], S42, 0xbd3af235);    /* 62 */
+	II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb);    /* 63 */
+	II (b, c, d, a, x[ 9], S44, 0xeb86d391);    /* 64 */
+
+	state[0] += a;
+	state[1] += b;
+	state[2] += c;
+	state[3] += d;
+
+	/* Zeroize sensitive information.
+	 */
+	bzero(x, sizeof(x));
+}
+
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ * a multiple of 4.
+ */
+static void
+Encode(unsigned char *output,
+       UINT4 *input,
+       unsigned int len)
+{
+	unsigned int i, j;
+
+	for (i = 0, j = 0; j < len; i++, j += 4) {
+		output[j] = (unsigned char)(input[i] & 0xff);
+		output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+		output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+		output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+	}
+}
+
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ * a multiple of 4.
+ */
+static void
+Decode(UINT4 *output,
+	const unsigned char *input,
+	unsigned int len)
+{
+	unsigned int i, j;
+
+	for (i = 0, j = 0; j < len; i++, j += 4)
+		output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+	(((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/mkstemp.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/mkstemp.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,618 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.54 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include "dcc_paths.h"
+#ifdef DCC_WIN32
+#include <direct.h>
+#else
+#include <dirent.h>
+#endif
+
+static struct {
+    DCC_PATH path;			/* has trailing '/' */
+} tmp[3];
+static int num_tmp_paths;
+
+static u_int32_t gen;
+
+
+static const char *
+mkstr(char str[DCC_MKSTEMP_LEN+1])
+{
+	static const char digits[] = "0123456789"
+#ifndef DCC_WIN32
+				    "abcdefghijklmnopqrstuvwxyz"
+#endif
+				    "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	u_int32_t n;
+	int i;
+
+	/* (re)start the generator and take a number
+	 * There is a race among threads here to compute ++gen.
+	 * However, it is rare and the worst that happens is that one thread
+	 * will lose when it tries to create the file, just as it might lose
+	 * to another process, and so need to come here again. */
+	if (gen == 0)
+		 gen = (getpid() << 16) + time(0);
+	n = ++gen;
+
+	i = DCC_MKSTEMP_LEN;
+	str[i] = '\0';
+	do {
+		str[--i] = digits[n % (LITZ(digits))];
+		n /= (LITZ(digits));
+	} while (i > 0);
+	return str;
+}
+
+
+
+/* Some platforms have broken implementations of mkstemp() that generate
+ * only 32 different names.
+ * Given the main uses for mkstemp() in the DCC for log files in directories
+ * used only by the DCC, it is nice to try to make the names sort of
+ * sequential for a given dccm process.  That means nothing more than putting
+ * the random bits in the most significant bits of the seed and using
+ * a small constant for the addition in the random number generator that is
+ * commonly used, and remembering the generator. */
+int					/* -1 or FD of temporary file */
+dcc_mkstemp(DCC_EMSG emsg,
+	    char *nm, int nm_len,	/* put the generated name here */
+	    char *id, int id_len,	/* put unique name string here */
+	    const char *old,		/* try this name first */
+	    const char *tgtdir,		/* directory or 0 for tmp_path */
+	    const char *p2,		/* rest of the name */
+	    u_char close_del,		/* 1=delete on close */
+	    int mode)			/* add these mode bits to 0600 */
+{
+	char str[DCC_MKSTEMP_LEN+1];
+	int fd, limit, serrno;
+	const char *p1;
+	int i;
+#ifdef DCC_WIN32
+	HANDLE h;
+	DWORD flags;
+#else
+	mode_t old_mask;
+#endif
+
+	if (!p2)
+		p2 = "";
+	p1 = tgtdir;
+	if (!p1) {
+		if (!num_tmp_paths)
+			tmp_path_init(0, 0);
+		p1 = tmp[0].path;
+	}
+
+	/* this loop should almost always need only a single pass */
+	limit = 0;
+	for (;;) {
+		if (*p2 == '/') {	/* avoid "//" */
+			i = strlen(p1);
+			if (i > 0 && p1[i-1] == '/')
+				++p2;
+		}
+
+		if (old) {
+			i = snprintf(nm, nm_len,
+				     "%s%s%."DCC_MKSTEMP_LEN_STR"s",
+				     p1, p2, old);
+		} else {
+			i = snprintf(nm, nm_len,
+				     "%s%s%s",
+				     p1, p2, mkstr(str));
+		}
+		if (i >= nm_len) {
+			dcc_pemsg(EX_SOFTWARE, emsg,
+				  "temporary file name \"%s\" too big", nm);
+			*nm = '\0';
+			return -1;
+		}
+
+#ifdef DCC_WIN32
+		/* Given the uses of this function, if the temporary
+		 * file is open, then it has been abandoned.  All valid
+		 * uses have the file renamed.  Windows does not allow
+		 * open files to be unlinked.  FILE_FLAGS_DELETE_ON_CLOSE
+		 * does not seem to be supported everywhere or it is
+		 * unreliable.  So open existing files without sharing
+		 * but truncated.
+		 * Use CreateFile() because the Borland and Microsoft
+		 * open(), _open(), and _sopen() functions differ */
+		flags = (close_del
+			 ? FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE
+			 : FILE_ATTRIBUTE_NORMAL);
+		h = CreateFile(nm, GENERIC_READ | GENERIC_WRITE, 0,
+			       0, CREATE_NEW, flags, 0);
+		if (h == INVALID_HANDLE_VALUE)
+			h = CreateFile(nm, GENERIC_READ | GENERIC_WRITE, 0,
+				       0, TRUNCATE_EXISTING, flags, 0);
+		if (h != INVALID_HANDLE_VALUE) {
+			fd = _open_osfhandle((long)h, 0);
+			if (fd >= 0) {
+				if (id && *id == '\0')
+					STRLCPY(id, str, id_len);
+				return fd;
+			}
+			dcc_pemsg(EX_IOERR, emsg, "_open_osfhandle(%s): %s",
+				  nm, ERROR_STR());
+			CloseHandle(h);
+			*nm = '\0';
+			return -1;
+		}
+		serrno = errno;
+#else
+		old_mask = umask(02);
+		fd = open(nm, O_RDWR | O_CREAT | O_EXCL, (mode & 0777) | 0600);
+		serrno = errno;
+		umask(old_mask);
+		if (fd >= 0) {
+			if (close_del
+			    && 0 > unlink(nm)) {
+				dcc_pemsg(EX_IOERR, emsg, "unlink(%s): %s",
+					  nm, ERROR_STR1(serrno));
+				close(fd);
+				*nm = '\0';
+				return -1;
+			}
+			if (id && *id == '\0')
+				STRLCPY(id, str, id_len);
+			return fd;
+		}
+#endif
+
+		if (errno != EEXIST) {
+			if (tgtdir != 0 || p1 == tmp[num_tmp_paths-1].path) {
+				dcc_pemsg(EX_IOERR, emsg, "open(%s): %s",
+					  nm, ERROR_STR1(serrno));
+				*nm = '\0';
+				return -1;
+			}
+			p1 += sizeof(tmp[num_tmp_paths].path);
+			continue;
+		}
+		if (++limit > 100) {
+			dcc_pemsg(EX_IOERR, emsg, "open(%s) %d times: %s",
+				  nm, limit, ERROR_STR1(EEXIST));
+			*nm = '\0';
+			return -1;
+		}
+
+		if (old) {
+			old = 0;
+		} else {
+			/* There is already a file of the generated name.
+			 * That implies that the current sequence of names
+			 * has collided with an older sequence.
+			 * So start another sequence. */
+			gen = 0;
+		}
+	}
+}
+
+
+
+
+DCC_PATH dcc_main_logdir;
+static LOG_MODE dcc_main_log_mode;
+
+/* see if a directory is a suitable for DCC client log files */
+static int				/* 1=ok, -2=non-existent, -1=bad */
+stat_logdir(DCC_EMSG emsg,
+	    const char *logdir,
+	    struct stat *sb)
+{
+	int result;
+
+	if (0 > stat(logdir, sb)) {
+		if (errno == ENOTDIR || errno == ENOENT) {
+			result = -2;
+		} else {
+			result = -1;
+		}
+		dcc_pemsg(EX_IOERR, emsg, "stat(log directory \"%s\"): %s",
+			  logdir, ERROR_STR());
+		return result;
+	}
+
+	if (!S_ISDIR(sb->st_mode)) {
+		dcc_pemsg(EX_IOERR, emsg, "\"%s\" is not a log directory",
+			  logdir);
+		return -1;
+	}
+	return 1;
+}
+
+
+
+static void
+tmpdir_ck(const char *dir, u_char complain)
+{
+#ifndef DCC_WIN32
+	struct stat sb;
+#endif
+	char *path;
+
+	if (!dir || *dir == '\0') {
+		if (complain)
+			dcc_error_msg("null -T temporary directory");
+		return;
+	}
+
+	path = tmp[num_tmp_paths].path;
+	if (!fnm2abs(path, dir, "")
+	    || strlen(path) > ISZ(DCC_PATH)-DCC_MKSTEMP_LEN-2) {
+		if (complain)
+			dcc_error_msg("-T \"%s\" too long", dir);
+		path[0] = '\0';
+		return;
+	}
+	++num_tmp_paths;
+
+#ifndef DCC_WIN32		/* stat(directory) fails on Windows XP */
+	if (complain) {
+		if (0 > stat(path, &sb)) {
+			dcc_error_msg("stat(temporary directory \"%s\"): %s",
+				      path, ERROR_STR());
+			return;
+		}
+		if (!S_ISDIR(sb.st_mode)) {
+			dcc_error_msg("\"%s\" is not a temporary directory",
+				      path);
+			return;
+		}
+#ifdef HAVE_EACCESS
+		if (0 > eaccess(path, W_OK)) {
+			dcc_error_msg("temporary directory \"%s\": %s",
+				      path, ERROR_STR());
+			return;
+		}
+#endif
+	}
+#endif /* !DCC_WIN32 */
+
+	strcat(path, "/");
+}
+
+
+
+/* get the temporary directory ready */
+void
+tmp_path_init(const char *dir1,		/* prefer this & complain if bad */
+	      const char *dir2)		/* then this but no complaint */
+{
+	if (dir1)
+		tmpdir_ck(dir1, 1);
+
+	if (dir2)
+		tmpdir_ck(dir2, 0);
+
+	tmpdir_ck(_PATH_TMP, 1);
+}
+
+
+/* Initialize the main DCC client log directory including parsing the
+ *	prefix of subdirectory type.  In case the path is relative,
+ *	this should be called after the change to the DCC home directory */
+u_char					/* 0=failed 1=ok */
+dcc_main_logdir_init(DCC_EMSG emsg, const char *arg)
+{
+	struct stat sb;
+
+	if (!arg)
+		return 1;
+
+	/* if the directory name starts with "D?", "H?", or "M?",
+	 * automatically add subdirectories */
+	if (!CLITCMP(arg, "D?")) {
+		dcc_main_log_mode = LOG_MODE_DAY;
+		arg += LITZ("D?");
+
+	} else if (!CLITCMP(arg, "H?")) {
+		dcc_main_log_mode = LOG_MODE_HOUR;
+		arg += LITZ("H?");
+
+	} else if (!CLITCMP(arg, "M?")) {
+		dcc_main_log_mode = LOG_MODE_MINUTE;
+		arg += LITZ("M?");
+
+	} else {
+		dcc_main_log_mode = LOG_MODE_FLAT;
+	}
+
+	if (!fnm2rel(dcc_main_logdir, arg, 0)) {
+		dcc_pemsg(EX_DATAERR, emsg, "bad log directry \"%s\"", arg);
+		return 0;
+	}
+
+	return stat_logdir(emsg, dcc_main_logdir, &sb) > 0;
+}
+
+
+
+static u_char
+mklogsubdir(DCC_EMSG emsg,
+	    DCC_PATH dir,		/* put directory name here */
+	    struct stat *dir_sb,
+	    const char *grandparent,
+	    const char *parent,
+	    const struct stat *parent_sb,
+	    const char *pat, u_int dirnum)
+{
+	int stat_result;
+	int i;
+
+	i = snprintf(dir, sizeof(DCC_PATH), pat, parent, dirnum);
+	if (i >= ISZ(DCC_PATH)) {
+		dcc_pemsg(EX_DATAERR, emsg, "long log directry name \"%s\"",
+			  grandparent);
+		return 0;
+	}
+
+	/* see if it already exists */
+	stat_result = stat_logdir(emsg, dir, dir_sb);
+	if (stat_result == -1)
+		return 0;
+
+#ifdef DCC_WIN32
+	if (stat_result == -2 && 0 > mkdir(dir)) {
+		dcc_pemsg(EX_IOERR, emsg, "mkdir(%s): %s",
+			  dir, ERROR_STR());
+		return 0;
+	}
+	return 1;
+#else
+	if (stat_result == -2) {
+		mode_t old_mask;
+
+		old_mask = umask(02);
+		if (0 > mkdir(dir, (parent_sb->st_mode & 0775) | 0700)) {
+			dcc_pemsg(EX_IOERR, emsg, "mkdir(%s): %s",
+				  dir, ERROR_STR());
+			umask(old_mask);
+			return 0;
+		}
+		umask(old_mask);
+		stat_result = stat_logdir(emsg, dir, dir_sb);
+		if (stat_result < 0)
+			return 0;
+	}
+	/* set GID if necessary */
+	if (dir_sb->st_gid != parent_sb->st_gid) {
+		if (0 > chown(dir, dir_sb->st_uid, parent_sb->st_gid)) {
+			/* this is expected to be needed and to work
+			 * only for root */
+			if (errno == EPERM)
+				return 1;
+			dcc_pemsg(EX_IOERR, emsg, "chown(%s,%d,%d): %s",
+				  dir, (int)dir_sb->st_uid,
+				  (int)parent_sb->st_gid,
+				  ERROR_STR());
+			return 0;
+		}
+		dir_sb->st_gid = parent_sb->st_gid;
+	}
+	return 1;
+#endif /* DCC_WIN32 */
+}
+
+
+
+/* create and open a DCC client log file */
+int					/* fd -1=no directory -2=serious */
+dcc_log_open(DCC_EMSG emsg,
+	     DCC_PATH log_path,		/* put the log file name here */
+	     char *id, int id_len,	/* put log ID string here */
+	     const char *old,		/* try this name first */
+	     const char *logdir,
+	     const char *prefix,	/* DCC_*_LOG_PREFIX */
+	     LOG_MODE log_mode)
+{
+	time_t now;
+	struct tm tm;
+	DCC_PATH dir_a, dir_b;
+	struct stat sb_a, sb_b, sb_f;
+	const struct stat *sb;
+	int stat_result, fd;
+
+	log_path[0] = '\0';
+
+	stat_result = stat_logdir(emsg, logdir, &sb_b);
+	if (stat_result < 0)
+		return stat_result;
+
+	if (log_mode == LOG_MODE_FLAT) {
+		sb = &sb_b;
+
+	} else {
+		now = time(0);
+		dcc_localtime(now, &tm);
+
+		if (!mklogsubdir(emsg, dir_a, &sb_a,
+				 logdir, logdir, &sb_b,
+				 "%s/%03d", tm.tm_yday+1))
+			return -2;
+
+		if (log_mode == LOG_MODE_DAY) {
+			logdir = dir_a;
+			sb = &sb_a;
+		} else {
+			if (!mklogsubdir(emsg, dir_b, &sb_b,
+					 logdir, dir_a, &sb_a,
+					 "%s/%02d", tm.tm_hour))
+				return -2;
+
+			if (log_mode == LOG_MODE_HOUR) {
+				logdir = dir_b;
+				sb = &sb_b;
+			} else {
+				if (!mklogsubdir(emsg, dir_a, &sb_a,
+						 logdir, dir_b, &sb_b,
+						 "%s/%02d", tm.tm_min))
+					return -2;
+				logdir = dir_a;
+				sb = &sb_a;
+			}
+		}
+	}
+
+	fd = dcc_mkstemp(emsg, log_path, sizeof(DCC_PATH), id, id_len, old,
+			 logdir, prefix, 0, sb->st_mode & 0640);
+	if (fd < 0)
+		return -2;
+
+	/* give it the right GID */
+	if (0 > fstat(fd, &sb_f)) {
+		dcc_pemsg(EX_IOERR, emsg, "fstat(%s): %s",
+			  log_path, ERROR_STR());
+		close(fd);
+		return -2;
+	}
+#ifndef DCC_WIN32
+	if (sb->st_gid != sb_f.st_gid
+	    && 0 > fchown(fd, sb_f.st_uid, sb->st_gid)
+	    && errno != EPERM) {
+		/* this is expected to be needed and to work only for root */
+		dcc_pemsg(EX_IOERR, emsg, "chown(%s,%d,%d): %s",
+			  log_path, (int)sb_f.st_uid, (int)sb->st_gid,
+			  ERROR_STR());
+		close(fd);
+		return -2;
+	}
+#endif
+	return fd;
+}
+
+
+
+/* create and open a main DCC client log file */
+int					/* fd -1=no directory -2=serious */
+dcc_main_log_open(DCC_EMSG emsg,
+		  DCC_PATH log_path,	/* put the log file name here */
+		  char *id, int id_len)	/* put log ID string here */
+{
+	return dcc_log_open(emsg, log_path, id, id_len, 0,
+			    dcc_main_logdir, DCC_TMP_LOG_PREFIX,
+			    dcc_main_log_mode);
+}
+
+
+/* rename a DCC client log file to a unique name */
+u_char
+dcc_log_keep(DCC_EMSG emsg, DCC_PATH cur_path)
+{
+	DCC_PATH new_path;
+	char str[DCC_MKSTEMP_LEN+1];
+	int path_len;
+	int limit;
+
+	path_len = strlen(cur_path);
+	if (path_len < LITZ(DCC_TMP_LOG_PREFIX)+DCC_MKSTEMP_LEN)
+		dcc_logbad(EX_SOFTWARE,
+			   "dcc_log_keep(%s): path too short", cur_path);
+	if (path_len >= ISZ(new_path))
+		dcc_logbad(EX_SOFTWARE,
+			   "dcc_log_keep(%s): path too long", cur_path);
+
+	/* start by trying the current name with "tmp" replaced by "msg" */
+	memcpy(new_path, cur_path, path_len+1);
+	memcpy(new_path+(path_len - (LITZ(DCC_TMP_LOG_PREFIX)+DCC_MKSTEMP_LEN)),
+	       DCC_FIN_LOG_PREFIX, LITZ(DCC_FIN_LOG_PREFIX));
+	limit = 0;
+	for (;;) {
+#ifdef DCC_WIN32
+		/* Windows does not have hard links */
+		if (!rename(cur_path, new_path)) {
+			memcpy(cur_path, new_path, path_len);
+			return 1;
+		}
+		if (limit > 100 || errno != EACCES) {
+			dcc_pemsg(EX_IOERR, emsg, "rename(%s,%s) #%d: %s",
+				  cur_path, new_path, limit, ERROR_STR());
+			return 0;
+		}
+#else
+		/* rename() on some UNIX systems deletes a pre-existing
+		 * target, so we must use link() and unlink() */
+		if (link(cur_path, new_path) >= 0) {
+			/* we made the link, so unlink the old name */
+			if (unlink(cur_path) < 0) {
+				dcc_pemsg(EX_IOERR, emsg, "unlink(%s): %s",
+					  cur_path, ERROR_STR());
+				return 0;
+			}
+			memcpy(cur_path, new_path, path_len);
+			return 1;
+		}
+		if (limit > 100 || errno != EEXIST) {
+			dcc_pemsg(EX_IOERR, emsg, "link(%s,%s): %s",
+				  cur_path, new_path, ERROR_STR());
+			return 0;
+		}
+#endif
+
+		 /* look for another sequence of names
+		  * if our new choice for a name was taken */
+		if (++limit > 1)
+			gen = 0;
+		memcpy(new_path+path_len-DCC_MKSTEMP_LEN,
+		       mkstr(str), DCC_MKSTEMP_LEN);
+	}
+}
+
+
+
+u_char
+dcc_log_close(DCC_EMSG emsg, const char *nm, int fd,
+	      const struct timeval *ldate)
+{
+	DCC_PATH path;
+	u_char result;
+
+	result = dcc_set_mtime(emsg, nm, fd, ldate);
+	if (0 > close(fd)
+	    && result) {
+		dcc_pemsg(EX_IOERR, emsg,"close(%s): %s",
+			  fnm2abs_err(path, nm), ERROR_STR());
+		result = 0;
+	}
+	return result;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/op2str.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/op2str.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,159 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * convert a DCC opcode to a string
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.37 $Revision$
+ */
+
+#include "dcc_defs.h"
+
+
+
+const char *
+dcc_aop2str(char *buf, int buf_len,
+	    DCC_AOPS aop,
+	    u_int32_t val1)		/* host byte order */
+{
+	switch (aop) {
+	case DCC_AOP_OK:	    return "ADMN";
+	case DCC_AOP_STOP:	    return "ADMN STOP";
+	case DCC_AOP_DB_UNLOAD:	    return "ADMN DB UNLOAD";
+	case DCC_AOP_FLOD:
+		switch ((DCC_AOP_FLODS)(val1 % 256)) {
+#			define RFS(s) return "ADMN FLOD "s
+		case DCC_AOP_FLOD_CHECK:    RFS("CHECK");
+		case DCC_AOP_FLOD_SHUTDOWN: RFS("SHUTDOWN");
+		case DCC_AOP_FLOD_HALT:	    RFS("HALT");
+		case DCC_AOP_FLOD_RESUME:   RFS("RESUME");
+		case DCC_AOP_FLOD_REWIND:   RFS("REWIND");
+		case DCC_AOP_FLOD_LIST:	    RFS("LIST");
+		case DCC_AOP_FLOD_STATS:    RFS("STATS");
+		case DCC_AOP_FLOD_STATS_CLEAR:RFS("STATS CLEAR");
+		case DCC_AOP_FLOD_FFWD_IN:  RFS("FFWD IN");
+		case DCC_AOP_FLOD_FFWD_OUT: RFS("FFWD OUT");
+#			undef RFS
+		}
+		break;
+	case DCC_AOP_DB_CLEAN:	    return "ADMN DB CLEAN";
+	case DCC_AOP_DB_NEW:	    return "ADMN DB NEW";
+	case DCC_AOP_STATS:	    return "ADMN STATS";
+	case DCC_AOP_STATS_CLEAR:   return "ADMN STATS CLEAR";
+	case DCC_AOP_TRACE_ON:
+		if (!buf || !buf_len)
+			return "ADMN TRACE ON";
+		snprintf(buf, buf_len, "ADMN TRACE ON %#x", val1);
+		return buf;
+	case DCC_AOP_TRACE_OFF:
+		if (!buf || !buf_len)
+			return "ADMN TRACE OFF";
+		snprintf(buf, buf_len, "ADMN TRACE OFF %#x", val1);
+		return buf;
+	case DCC_AOP_unused1:	    break;
+	case DCC_AOP_CLIENTS:	    return "ADMN CLIENTS";
+	case DCC_AOP_CLIENTS_ID:    return "ADMN CLIENTS BY ID";
+	case DCC_AOP_ANON_DELAY:    return "ADMN ANON DELAY";
+	case DCC_AOP_CLOCK_CHECK:   return "ADMN CLOCK CHECK";
+	}
+
+	if (!buf || !buf_len)
+		return "ADMN UNKNOWN ???";
+	snprintf(buf, buf_len,
+		 "ADMN UNKNOWN #%d %#x", aop, val1);
+	return buf;
+}
+
+
+
+const char *
+dcc_hdr_op2str(char *buf, int buf_len,
+	       const DCC_HDR *hdr)	/* all in network byte order */
+{
+	const char *p;
+	int len, slen, i;
+
+	switch ((DCC_OPS)hdr->op) {
+	case DCC_OP_INVALID:	return "INVALID";
+	case DCC_OP_NOP:	return "NOP";
+	case DCC_OP_REPORT:	return "REPORT";
+	case DCC_OP_QUERY:	return "QUERY";
+	case DCC_OP_ANSWER:	return "ANSWER";
+	case DCC_OP_ADMN:
+		/* display ASCII results */
+		len = ntohs(hdr->len);
+		slen = len - (sizeof(*hdr) + sizeof(DCC_SIGNATURE));
+		if (slen > 0) {
+			for (p = (char *)(hdr+1), i = slen;
+			     i > 0;
+			     --i, ++p) {
+				if ((*p < ' ' || *p >= 0x7f)
+				    && *p != '\n' && *p != '\t')
+					break;
+			}
+			if (i == 0) {
+				if (!buf || buf_len <= ISZ("ADMN \"...\""))
+					return "ADMN \"...\"";
+				if (slen+ISZ("ADMN \"\"") > buf_len) {
+					slen = buf_len - ISZ("ADMN \"...\"");
+					snprintf(buf, buf_len,
+						 "ADMN \"%.*s...\"",
+						 slen, (char *)(hdr+1));
+				} else {
+					snprintf(buf, buf_len,
+						 "ADMN \"%.*s\"",
+						 slen, (char *)(hdr+1));
+				}
+				return buf;
+			}
+		}
+		if (len < DCC_ADMN_REQ_MIN_SIZE)
+			return "ADMIN ???";
+		return dcc_aop2str(buf, buf_len, ((DCC_ADMN_REQ *)hdr)->aop,
+				   ntohl(((DCC_ADMN_REQ *)hdr)->val1));
+	case DCC_OP_OK:		return "OK";
+	case DCC_OP_ERROR:	return "ERROR";
+	case DCC_OP_DELETE:	return "DELETE";
+	case DCC_OP_GREY_REPORT:return "GREYLIST REPORT";
+	case DCC_OP_GREY_QUERY:	return "GREYLIST QUERY";
+	case DCC_OP_GREY_SPAM:	return "GREYLIST SPAM";
+	case DCC_OP_GREY_WHITE:	return "GREYLIST WHITELIST";
+	}
+
+	if (!buf || !buf_len)
+		return "unknown op ???";
+	snprintf(buf, buf_len, "unknown op #%d", hdr->op);
+	return buf;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/parse_log_opt.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/parse_log_opt.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,176 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.19 $Revision$
+ */
+
+#include "dcc_clnt.h"
+#include "dcc_heap_debug.h"
+#include <syslog.h>
+
+
+static int
+parse_level(const char *level)
+{
+	static struct {
+	    const char *str;
+	    int level;
+	} level_tbl[] = {
+	    {"EMERG",	LOG_EMERG},
+	    {"ALERT",	LOG_ALERT},
+	    {"CRIT",	LOG_CRIT},
+	    {"ERR",	LOG_ERR},
+	    {"WARNING",	LOG_WARNING},
+	    {"NOTICE",	LOG_NOTICE},
+	    {"INFO",	LOG_INFO},
+	    {"DEBUG",	LOG_DEBUG},
+	};
+	int i;
+
+	for (i = 0; i < DIM(level_tbl); ++i) {
+		if (!strcasecmp(level, level_tbl[i].str))
+			return level_tbl[i].level;
+	}
+	return -1;
+}
+
+
+
+static int
+parse_facility(const char *facility)
+{
+	static struct {
+	    const char *str;
+	    int facility;
+	} facility_tbl[] = {
+	    {"AUTH",	LOG_AUTH},
+#ifdef LOG_AUTHPRIV
+	    {"AUTHPRIV",LOG_AUTHPRIV},
+#endif
+	    {"CRON",	LOG_CRON},
+	    {"DAEMON",	LOG_DAEMON},
+#ifdef LOG_FTP
+	    {"FTP",	LOG_FTP},
+#endif
+	    {"KERN",	LOG_KERN},
+	    {"LPR",	LOG_LPR},
+	    {"MAIL",	LOG_MAIL},
+	    {"NEWS",	LOG_NEWS},
+	    {"USER",	LOG_USER},
+	    {"UUCP",	LOG_UUCP},
+	    {"LOCAL0",	LOG_LOCAL0},
+	    {"LOCAL1",	LOG_LOCAL1},
+	    {"LOCAL2",	LOG_LOCAL2},
+	    {"LOCAL3",	LOG_LOCAL3},
+	    {"LOCAL4",	LOG_LOCAL4},
+	    {"LOCAL5",	LOG_LOCAL5},
+	    {"LOCAL6",	LOG_LOCAL6},
+	    {"LOCAL7",	LOG_LOCAL7},
+	};
+	int i;
+
+	for (i = 0; i < DIM(facility_tbl); ++i) {
+		if (!strcasecmp(facility, facility_tbl[i].str))
+			return facility_tbl[i].facility;
+	}
+	return -1;
+}
+
+
+
+u_char
+dcc_parse_log_opt(const char *arg)
+{
+	char *duparg, *facility, *level;
+	int priority, *resultp;
+
+	if (!LITCMP(arg, "off")) {
+		dcc_no_syslog = 1;
+		return 1;
+	}
+
+	duparg = dcc_strdup(arg);
+	facility = strpbrk(duparg, ".,");
+	if (!facility) {
+		dcc_error_msg("missing syslog facility in \"-L %s\"", arg);
+		dcc_free(duparg);
+		return 0;
+	}
+	*facility++ = '\0';
+
+	if (!strcasecmp(duparg, "info")) {
+		resultp = &dcc_trace_priority;
+	} else if (!strcasecmp(duparg, "error")) {
+		resultp = &dcc_error_priority;
+	} else {
+		dcc_error_msg("\"%s\" in \"-L %s\""
+			      " is neither \"info\" nor \"error\"",
+			      duparg,arg);
+		dcc_free(duparg);
+		return 0;
+	}
+
+	level = strpbrk(facility, ".,");
+	if (!level) {
+		dcc_error_msg("missing syslog level in \"-L %s\"", arg);
+		dcc_free(duparg);
+		return 0;
+	}
+	*level++ = '\0';
+
+	/* allow both level.facility and facility.level */
+	priority = parse_facility(facility);
+	if (priority >= 0) {
+		priority |= parse_level(level);
+	} else {
+		priority = parse_facility(level);
+		if (priority < 0) {
+			dcc_error_msg("unrecognized facility in \"%s\"", arg);
+			dcc_free(duparg);
+			return 0;
+		}
+		priority |= parse_level(facility);
+	}
+	if (priority < 0) {
+		dcc_error_msg("unrecognized level in \"%s\"", arg);
+		dcc_free(duparg);
+		return 0;
+	}
+
+	*resultp = priority;
+	dcc_free(duparg);
+	return 1;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/parse_srvr_nm.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/parse_srvr_nm.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,264 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.47 $Revision$
+ */
+
+#include "dcc_clnt.h"
+
+
+/* open a file of server names, ports, and so forth */
+FILE *
+dcc_open_srvr_nm(DCC_EMSG emsg, const char *nm)
+{
+	FILE *f;
+	DCC_PATH path;
+
+	f = fopen(nm, "r");
+	if (!f) {
+		dcc_pemsg(EX_NOINPUT, emsg,
+			  "fopen(%s): %s", fnm2abs_err(path, nm), ERROR_STR());
+		return 0;
+	}
+
+	/* the file contains passwords,
+	 * so refuse to use it if everyone can read it */
+	if (!dcc_ck_private(emsg, 0, nm, fileno(f))) {
+		fclose(f);
+		return 0;
+	}
+	return f;
+}
+
+
+
+/* get "hostname,port" string from the start of a line */
+const char *				/* 0=bad, rest of line if ok */
+dcc_parse_nm_port(DCC_EMSG emsg,
+		  const char *line0,
+		  u_int def_port,	/* default or DCC_GET_PORT_INVALID */
+		  char *hostname,	/* put host name here */
+		  u_int hostname_len,
+		  u_int16_t *portp,	/* put port # in network byte order */
+		  char *portname,	/* put port name here */
+		  u_int portname_len,
+		  const char *fnm, int lno) /* configuration file source */
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	char buf[DCC_MAXDOMAINLEN+1+MAXPORTNAMELEN+1];	/* "name,#\0" */
+	const char *line;
+	const char *pstr;
+	u_int port;
+	u_int hlen;
+
+
+	/* get both parameters */
+	line = dcc_parse_word(emsg, buf, sizeof(buf), line0,
+			      "hostname,port", fnm, lno);
+	if (!line)
+		return 0;
+
+	/* get the hostname and separate the port number */
+	pstr = strchr(buf, ',');
+	if (!pstr) {
+		if (def_port == DCC_GET_PORT_INVALID) {
+			dcc_pemsg(EX_USAGE, emsg, "missing port in \"%s\"%s",
+				  buf, fnm_lno(&fnm_buf, fnm, lno));
+			return 0;
+		}
+		hlen = strlen(buf);
+		pstr = "-";
+	} else {
+		hlen = pstr++ - buf;
+	}
+
+	if (hostname_len) {
+		memset(hostname, 0, hostname_len);
+		if (hlen >= hostname_len) {
+			dcc_pemsg(EX_NOHOST, emsg,
+				  "hostname \"%.16s...\" too long%s",
+				  buf, fnm_lno(&fnm_buf, fnm, lno));
+			return 0;
+		}
+		if (hlen)
+			memcpy(hostname, buf, hlen);
+	}
+	if (portname_len) {
+		memset(portname, 0, portname_len);
+		hlen = strlen(pstr);
+		if (hlen >= portname_len)
+			hlen = portname_len-1;
+		if (hlen)
+			memcpy(portname, pstr, hlen);
+	}
+
+	/* get the port number */
+	port = dcc_get_port(emsg, pstr, def_port, fnm, lno);
+	if (port == DCC_GET_PORT_INVALID)
+		return 0;
+
+	if (portp)
+		*portp = port;
+	return line;
+}
+
+
+
+/* parse a line of the following form
+ *	hostname[,port-#] [RTT+adj] [Greylist] [client-ID [password]]
+ *  The port-# can be "-" to specifiy the default DCC server port.
+ *  If both the client-ID and the password are absent, then the anonymous
+ *  client-ID is used.
+ *  A null string is assumed if the password is missing.
+ */
+int					/* 1=parsed, 0=bad, -1=unknown name */
+dcc_parse_srvr_nm(DCC_EMSG emsg,
+		  DCC_SRVR_NM *nmp,	/* build this entry */
+		  u_char *pgrey,	/* 1=for greylisting */
+		  const char *line,	/* from this string */
+		  const char *fnm, int lno) /* that came from here */
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	char id_buf[12];
+	char port_buf[3];
+	char *p;
+	long l;
+
+	memset(nmp, 0, sizeof(DCC_SRVR_NM));
+
+	line = dcc_parse_nm_port(emsg, line, DCC_GREY2PORT(pgrey && *pgrey),
+				 nmp->hostname, sizeof(nmp->hostname),
+				 &nmp->port, port_buf, sizeof(port_buf),
+				 fnm, lno);
+	if (!line)
+		return 0;
+
+	for (;;) {
+		/* look for greylist flag */
+		if (!CLITCMP(line, "greylist")
+		    && (line[LITZ("greylist")] == '\0'
+			|| line[LITZ("greylist")] == ' '
+			|| line[LITZ("greylist")] == '\t')) {
+			line += LITZ("greylist")+strspn(line+LITZ("greylist"),
+							DCC_WHITESPACE);
+			if (pgrey)
+				*pgrey = 1;
+			if (port_buf[0] == '\0' || !strcmp(port_buf, "-"))
+				nmp->port = htons(DCC_GREY_PORT);
+			continue;
+		}
+
+		/* look for optional RTT adjustment */
+		if (CLITCMP(line, "rtt"))
+			break;
+		line += LITZ("rtt")+strspn(line+LITZ("rtt"), DCC_WHITESPACE);
+		l = strtol(line, &p, 10);
+		if (p != line) {
+			int wsp = strspn(p, DCC_WHITESPACE);
+			if (!CLITCMP(p+wsp, "ms"))
+				p += wsp+LITZ("ms");
+		}
+		if (p == line
+		    || (*p != '\0' && *p != ' ' && *p != '\t')) {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "invalid RTT adjustment%s",
+				  fnm_lno(&fnm_buf, fnm, lno));
+			return 0;
+		}
+		if (l < -DCC_RTT_ADJ_MAX/1000) {
+			l = -DCC_RTT_ADJ_MAX/1000;
+		} else if (l > DCC_RTT_ADJ_MAX/1000) {
+			l = DCC_RTT_ADJ_MAX/1000;
+		}
+		nmp->rtt_adj = l*1000;
+		line = p+strspn(p, DCC_WHITESPACE);
+	}
+
+	/* get the client-ID */
+	line = dcc_parse_word(emsg, id_buf, sizeof(id_buf),
+			      line, "client-ID", fnm, lno);
+	if (!line)
+		return 0;
+	if (id_buf[0] == '\0') {
+		nmp->clnt_id = DCC_ID_ANON;
+	} else {
+		nmp->clnt_id = dcc_get_id(emsg, id_buf, fnm, lno);
+		if (nmp->clnt_id == DCC_ID_INVALID)
+			return 0;
+		if (nmp->clnt_id < DCC_CLNT_ID_MIN
+		    && nmp->clnt_id != DCC_ID_ANON) {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "server-ID %d is not a client-ID",
+				  nmp->clnt_id);
+			return 0;
+		}
+	}
+
+	/* allow null password only for anonymous clients
+	 * clients of greylist servers cannot be anonymous */
+	if (nmp->clnt_id == DCC_ID_ANON) {
+		if (*line != '\0') {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "password invalid for %s"
+				  " with anonymous client-ID%s",
+				  nmp->hostname,
+				  fnm_lno(&fnm_buf, fnm, lno));
+			return 0;
+		}
+		return 1;
+	}
+
+	if (*line == '\0') {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "invalid null password for client-ID %d for %s%s",
+			  nmp->clnt_id, nmp->hostname,
+			  fnm_lno(&fnm_buf, fnm, lno));
+		return 0;
+	}
+
+	line = parse_passwd(emsg, nmp->passwd, line, "passwd", fnm, lno);
+	if (!line)
+		return 0;
+	if (nmp->passwd[0] == '\0' || *line != '\0') {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "invalid password server %s, client-ID %d%s",
+			  nmp->hostname, nmp->clnt_id,
+			  fnm_lno(&fnm_buf, fnm, lno));
+		return 0;
+	}
+
+	return 1;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/parse_whitefile.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/parse_whitefile.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,720 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.71 $Revision$
+ */
+
+#include "dcc_ck.h"
+#include "dcc_xhdr.h"
+
+
+const char *
+wf_fnm(const DCC_WF *wf, int fno)
+{
+	if (!fno) {
+		return wf->ascii_nm;
+	} else {
+		return wf->wtbl->hdr.white_incs[fno-1].nm;
+	}
+}
+
+
+
+const char *
+wf_fnm_lno(DCC_FNM_LNO_BUF *buf, const DCC_WF *wf)
+{
+	int fno;
+
+	if (!wf)
+		return "";
+
+	fno = wf->fno;
+	if (!fno)
+		return fnm_lno(buf, wf->ascii_nm, wf->lno);
+
+	snprintf(buf->b, sizeof(buf->b),
+		 DCC_FNM_LNO_PAT" included from %s",
+		 wf->lno, path2fnm(wf->wtbl->hdr.white_incs[fno-1].nm),
+		 wf->ascii_nm);
+	return buf->b;
+}
+
+
+
+DCC_TGTS
+dcc_str2thold(DCC_CK_TYPES type, const char *str)
+{
+	u_long l;
+	char *p;
+
+	l = strtoul(str, &p, 10);
+	if (*p != '\0') {
+		if (*p == '%') {
+			if (*++p != '\0' || type != DCC_CK_REP_BULK)
+				return DCC_TGTS_INVALID;
+		} else if (!strcasecmp(str, DCC_XHDR_TOO_MANY)) {
+			l = DCC_TGTS_TOO_MANY;
+		} else if (!strcasecmp(str, DCC_XHDR_THOLD_NEVER)) {
+			l = DCC_THOLD_NEVER;
+		} else {
+			return DCC_TGTS_INVALID;
+		}
+	}
+
+	if (l <= DCC_TGTS_TOO_MANY) {
+		/* You cannot have a reputation worse than 100%.
+		 * Use "never" to turn off reputation rejections or logging */
+		if (type == DCC_CK_REP_BULK && l > 100)
+			return DCC_TGTS_INVALID;
+		/* the reputation threshold for "bulk" must be finite */
+		if (type == DCC_CK_REP_TOTAL && l >= DCC_TGTS_TOO_MANY)
+			return DCC_TGTS_INVALID;
+	}
+
+	return l;
+}
+
+
+
+/* look for a word followed by whitespace */
+static const char *
+ck_word_white(const char *p,
+	      const char *word,
+	      int word_len)
+{
+	int sps;
+
+	if (strncasecmp(p, word, word_len))
+		return 0;
+
+	p += word_len;
+	sps = strspn(p, DCC_WHITESPACE);
+	if (sps == 0)
+		return 0;
+
+	return p+sps;
+}
+
+
+
+/* parse DCC thresholds */
+static u_char
+whitefile_option_thold(DCC_EMSG emsg,
+		       DCC_WF *wf,
+		       const char *opt,
+		       DCC_FNM_LNO_BUF *fnm_buf,
+		       DCC_FNM_LNO_BUF *thold_fnm_buf)
+{
+	const char *thold_type, *thold;
+	DCC_TGTS thold_tgts;
+	DCC_CK_TYPES type, t2;
+	u_char result;
+
+	thold_type = ck_word_white(opt, "threshold", LITZ("threshold"));
+	if (!thold_type) {
+		/* it is not a threshold setting */
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "unrecognized \"option %s\"%s",
+			  opt, wf_fnm_lno(fnm_buf, wf));
+		return 0;
+	}
+
+	thold = strchr(thold_type, ',');
+	if (!thold_type) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "no comma in \"option %s\"%s",
+			  opt, wf_fnm_lno(fnm_buf, wf));
+		return 0;
+	}
+
+	type = dcc_str2type_thold(thold_type, thold - thold_type);
+	if (type == DCC_CK_INVALID) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "unknown checksum type in \"option %s\"%s",
+			  opt, wf_fnm_lno(fnm_buf, wf));
+		return 0;
+	}
+	thold_tgts = dcc_str2thold(type, ++thold);
+	if (thold_tgts == DCC_TGTS_INVALID) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "unrecognized threshold in \"option %s\"%s",
+			  opt, wf_fnm_lno(fnm_buf, wf));
+		return 0;
+	}
+
+
+	wf_fnm_lno(thold_fnm_buf, wf);
+	result = 1;
+	for (t2 = DCC_CK_TYPE_FIRST; t2 <= DCC_CK_TYPE_LAST; ++t2) {
+		if (!(t2 == type
+		      || (type == SET_ALL_THOLDS && IS_ALL_CKSUM(t2))
+		      || (type == SET_CMN_THOLDS && IS_CMN_CKSUM(t2))))
+			continue;
+		if (wf->wtbl->hdr.tholds_rej[t2] != DCC_THOLD_UNSET
+		    && wf->wtbl->hdr.tholds_rej[t2] != thold_tgts) {
+			if (result)
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "conflicting threshold setting%s",
+					  thold_fnm_buf->b);
+			result = 0;
+		} else {
+			wf->wtbl->hdr.tholds_rej[t2] = thold_tgts;
+		}
+	}
+
+	return result;
+}
+
+
+
+/* honor controls in client whitelists of the forms:
+ *	option log-{all,normal}
+ *	option log-subdirectory-{day,hour,minute}
+ *	option greylist-{on,off,log-on,log-off}
+ *	option DCC-{on,off}
+ *	option DCC-rep-{on,off}
+ *	option dnsbl {1,2,3}-{on,off}
+ *	option dnsbl-{on,off}		obsolete
+ *	option MTA-{first,last}
+ *	option forced-discard-ok
+ *	option no-forced-discard
+ *	option forced-discard-nok	obsolete
+ *	option threshold CKSUM,THOLD
+ *	option spam-trap-accept
+ *	option spam-trap-reject
+ * change the sample whiteclnt file when this changes
+ */
+static u_char
+parse_option(DCC_EMSG emsg,
+	     DCC_WF *wf,
+	     const char *opt,
+	     DCC_FNM_LNO_BUF *fnm_buf,
+	     DCC_FNM_LNO_BUF *thold_fnm_buf)
+{
+	static const struct {
+	    const char	*str;
+	    int		len;
+	    DCC_WHITE_FGS on;		/* turn on this bit */
+	    DCC_WHITE_FGS conflicts;	/* conflict with these bits */
+	} *tp, *tp1, tbl[] = {
+#	    define DE(s,on,conflicts) {s, LITZ(s), on, conflicts},
+	    DE("log-all",
+	       DCC_WHITE_FG_LOG_ALL,
+	       DCC_WHITE_FG_LOG_NORMAL)
+	    DE("log-normal",
+	       DCC_WHITE_FG_LOG_NORMAL,
+	       DCC_WHITE_FG_LOG_ALL)
+
+	    DE("log-subdirectory-day",
+	       DCC_WHITE_FG_LOG_D,
+	       DCC_WHITE_FG_LOG_H | DCC_WHITE_FG_LOG_M)
+	    DE("log-subdirectory-hour",
+	       DCC_WHITE_FG_LOG_H,
+	       DCC_WHITE_FG_LOG_D | DCC_WHITE_FG_LOG_M)
+	    DE("log-subdirectory-minute",
+	       DCC_WHITE_FG_LOG_M,
+	       DCC_WHITE_FG_LOG_D | DCC_WHITE_FG_LOG_H)
+
+	    DE("greylist-on",
+	       DCC_WHITE_FG_GREY_ON,
+	       DCC_WHITE_FG_GREY_OFF)
+	    DE("greylist-off",
+	       DCC_WHITE_FG_GREY_OFF,
+	       DCC_WHITE_FG_GREY_ON)
+
+	    DE("greylist-log-on",
+	       DCC_WHITE_FG_GREY_LOG_ON,
+	       DCC_WHITE_FG_GREY_LOG_OFF)
+	    DE("greylist-log-off",
+	       DCC_WHITE_FG_GREY_LOG_OFF,
+	       DCC_WHITE_FG_GREY_LOG_ON)
+
+	    DE("DCC-on",
+	       DCC_WHITE_FG_DCC_ON,
+	       DCC_WHITE_FG_DCC_OFF)
+	    DE("DCC-off",
+	       DCC_WHITE_FG_DCC_OFF,
+	       DCC_WHITE_FG_DCC_ON)
+
+	    DE("forced-discard-ok",
+	       DCC_WHITE_FG_DISCARD_OK,
+	       DCC_WHITE_FG_NO_DISCARD)
+	    DE("no_forced-discard",
+	       DCC_WHITE_FG_NO_DISCARD,
+	       DCC_WHITE_FG_DISCARD_OK)
+	    DE("forced-discard-nok",	/* obsolete */
+	       DCC_WHITE_FG_NO_DISCARD,
+	       DCC_WHITE_FG_DISCARD_OK)
+
+	    DE("MTA-first",
+	       DCC_WHITE_FG_MTA_FIRST,
+	       DCC_WHITE_FG_MTA_LAST)
+	    DE("MTA-last",
+	       DCC_WHITE_FG_MTA_LAST,
+	       DCC_WHITE_FG_MTA_FIRST)
+
+	    DE("DCC-rep-on",
+	       DCC_WHITE_FG_REP_ON,
+	       DCC_WHITE_FG_REP_OFF)
+	    DE("DCC-rep-off",
+	       DCC_WHITE_FG_REP_OFF,
+	       DCC_WHITE_FG_REP_ON)
+	    DE("DCC-reps-on",		/* obsolete */
+	       DCC_WHITE_FG_REP_ON,
+	       DCC_WHITE_FG_REP_OFF)
+	    DE("DCC-reps-off",		/* obsolete */
+	       DCC_WHITE_FG_REP_OFF,
+	       DCC_WHITE_FG_REP_ON)
+
+	    DE("DNSBL-on",		/* obsolete */
+	       DCC_WHITE_FG_DNSBL_ON(0),
+	       DCC_WHITE_FG_DNSBL_OFF(0))
+	    DE("DNSBL1-on",
+	       DCC_WHITE_FG_DNSBL_ON(0),
+	       DCC_WHITE_FG_DNSBL_OFF(0))
+	    DE("DNSBL-off",		/* obsolete */
+	       DCC_WHITE_FG_DNSBL_OFF(0),
+	       DCC_WHITE_FG_DNSBL_ON(0))
+	    DE("DNSBL1-off",
+	       DCC_WHITE_FG_DNSBL_OFF(0),
+	       DCC_WHITE_FG_DNSBL_ON(0))
+
+	    DE("DNSBL2-on",
+	       DCC_WHITE_FG_DNSBL_ON(1),
+	       DCC_WHITE_FG_DNSBL_OFF(1))
+	    DE("DNSBL2-off",
+	       DCC_WHITE_FG_DNSBL_OFF(1),
+	       DCC_WHITE_FG_DNSBL_ON(1))
+
+	    DE("DNSBL3-on",
+	       DCC_WHITE_FG_DNSBL_ON(2),
+	       DCC_WHITE_FG_DNSBL_OFF(2))
+	    DE("DNSBL3-off",
+	       DCC_WHITE_FG_DNSBL_OFF(2),
+	       DCC_WHITE_FG_DNSBL_ON(2))
+
+	    DE(DCC_XHDR_TRAP_ACC,
+	       DCC_WHITE_FG_TRAP_ACC,
+	       DCC_WHITE_FG_TRAP_REJ)
+
+	    DE(DCC_XHDR_TRAP_REJ,
+	       DCC_WHITE_FG_TRAP_REJ,
+	       DCC_WHITE_FG_TRAP_ACC)
+
+#	    undef DE
+	};
+
+	int i;
+	DCC_WHITE_FGS conflicts;
+
+	i = strlen(opt);
+	for (tp = tbl; ; ++tp) {
+		/* try a threshold if not a boolean option */
+		if (tp > LAST(tbl))
+			return whitefile_option_thold(emsg, wf, opt,
+						      fnm_buf, thold_fnm_buf);
+		if (i == tp->len && !strcasecmp(tp->str, opt))
+			break;
+	}
+
+	conflicts = (wf->wtbl_flags & tp->conflicts);
+	if (conflicts != 0) {
+		for (tp1 = tbl; tp1 <= LAST(tbl); ++tp1) {
+			if ((tp1->on & conflicts) != 0) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "\"option %s\"%s conflicts with "
+					  "\"option %s\"",
+					  opt, wf_fnm_lno(fnm_buf, wf),
+					  tp1->str);
+				return 0;
+			}
+		}
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "conflicting \"option %s\"%s",
+			  opt, wf_fnm_lno(fnm_buf, wf));
+		return 0;
+	}
+
+	wf->wtbl->hdr.flags = (wf->wtbl_flags |= tp->on);
+	return 1;
+}
+
+
+
+int					/* -1=fatal 0=problems 1=ok */
+dcc_parse_whitefile(DCC_EMSG emsg,
+		    DCC_WF *wf,
+		    int main_fd,	/* main file */
+		    DCC_PARSED_CK_FNC add_fnc,
+		    DCC_PARSED_CK_CIDR_FNC cidr_fnc)
+{
+	struct f {
+	    int	    fd;
+	    char    *start;
+	    char    *eob;
+	    DCC_PATH path;
+	    char    c[1024];
+	} main_buf, inc_buf, *cur_buf;
+	char tgts_buf[16];
+	char *bol, *eol, *type_nm, *ck;
+	const char *nmp, *opt;
+	DCC_FNM_LNO_BUF fnm_buf;
+	DCC_FNM_LNO_BUF white_fnm_buf;
+	DCC_FNM_LNO_BUF thold_fnm_buf;
+	DCC_TGTS new_tgts;
+	DCC_CK_TYPES type;
+	struct stat sb;
+	int white_fno;
+	int main_lno;
+	u_char result, hex;
+	int i, j;
+
+	result = 1;
+	white_fnm_buf.b[0] = '\0';
+	thold_fnm_buf.b[0] = '\0';
+	main_buf.fd = main_fd;
+	main_buf.eob = main_buf.c;
+	main_buf.start = main_buf.c;
+	cur_buf = &main_buf;
+	wf->fno = white_fno = 0;
+	wf->lno = main_lno = 0;
+	new_tgts = DCC_TGTS_INVALID;
+	for (;;) {
+next_line:;
+		/* Each substantive line has one of the forms:
+		 *	tgts	[hex] type	string
+		 *		[hex] type	string
+		 *	include	pathname
+		 *	option ...
+		 * A missing number of targets means the line has the
+		 * same number of targets as the previous line */
+
+		++wf->lno;
+		for (;;) {
+			/* continue getting more text until we have an
+			 * end-of-line in the buffer */
+			if (cur_buf->start < cur_buf->eob) {
+				eol = memchr(cur_buf->start, '\n',
+					     cur_buf->eob - cur_buf->start);
+				if (eol)
+					break;
+			}
+
+			if (cur_buf->start != cur_buf->c) {
+				i = cur_buf->eob - cur_buf->start;
+				if (i > 0)
+					memmove(cur_buf->c, cur_buf->start, i);
+				cur_buf->start = cur_buf->c;
+				cur_buf->eob = &cur_buf->c[i];
+			}
+			j = &cur_buf->c[sizeof(cur_buf->c)] - cur_buf->eob;
+			if (j <= 0) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "line too long%s",
+					  wf_fnm_lno(&fnm_buf, wf));
+				result = 0;
+			} else {
+				i = read(cur_buf->fd, cur_buf->eob, j);
+				if (i > 0) {
+					cur_buf->eob += i;
+					continue;
+				}
+
+				if (i < 0) {
+					dcc_pemsg(EX_IOERR, emsg,
+						  "read(%s, %d): %s",
+						  wf_fnm(wf, wf->fno), j,
+						  ERROR_STR());
+					result = 0;
+				}
+				/* act as if the last line in the file ends
+				 * with '\n' even if it does not */
+				if (cur_buf->start < cur_buf->eob) {
+					eol = cur_buf->eob++;
+					break;
+				}
+			}
+			if (cur_buf == &main_buf)
+				return result;
+			if (0 > close(cur_buf->fd)) {
+				dcc_pemsg(EX_IOERR, emsg, "close(%s): %s",
+					  wf_fnm(wf, wf->fno), ERROR_STR());
+				result = 0;
+			}
+			/* return to the main file at end of included file */
+			cur_buf = &main_buf;
+			wf->fno = 0;
+			wf->lno = main_lno;
+			new_tgts = DCC_TGTS_INVALID;
+			continue;
+		}
+		bol = cur_buf->start;
+		cur_buf->start = eol+1;
+
+		/* trim trailing blanks */
+		do {
+			*eol-- = '\0';
+		} while (eol > bol
+			 && (*eol == ' ' || *eol == '\t' || *eol == '\r'));
+
+		/* Ignore blank lines and lines starting with '#' */
+		type_nm = bol+strspn(bol, DCC_WHITESPACE);
+		if (*type_nm == '\0' || *type_nm == '#')
+			goto next_line;
+
+		/* parse
+		 *	include	pathname */
+		nmp = ck_word_white(type_nm, "include", LITZ("include"));
+		if (nmp) {
+			DCC_PATH nm;
+
+			/* can't continue list of identical/missing target
+			 * counts into included file */
+			new_tgts = DCC_TGTS_INVALID;
+
+			if (cur_buf != &main_buf) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "nested \"include\" not allowed%s",
+					  wf_fnm_lno(&fnm_buf, wf));
+				result = 0;
+				goto next_line;
+			}
+
+			/* trim quotes if present from the file name */
+			i = strlen(nmp);
+			if (i > 1
+			    && ((nmp[0] == '"' && nmp[i-1] == '"')
+				|| (nmp[0] == '<' &&
+				    i > 1 && nmp[i-1] == '>'))) {
+				++nmp;
+				i -= 2;
+			}
+
+			if (i == 0) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "missing file name%s",
+					  wf_fnm_lno(&fnm_buf, wf));
+				result = 0;
+				goto next_line;
+			}
+			if (i >= ISZ(DCC_PATH)) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "file name too long in"
+					  " \"include %.32s...\"%s",
+					  nmp, wf_fnm_lno(&fnm_buf, wf));
+				result = 0;
+				goto next_line;
+			}
+			memcpy(nm, nmp, i);
+			nm[i] = '\0';
+			if (white_fno >= DIM(wf->wtbl->hdr.white_incs)) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "too many \"include\" files%s",
+					  wf_fnm_lno(&fnm_buf, wf));
+				result = 0;
+				goto next_line;
+			}
+
+			if (!fnm2rel(wf->wtbl->hdr.white_incs[white_fno].nm,
+				     nm, 0)) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "name \"%s\" too long%s",
+					  nm, wf_fnm_lno(&fnm_buf, wf));
+				result = 0;
+				goto next_line;
+			}
+
+			inc_buf.fd = open(nm, O_RDONLY, 0);
+			if (inc_buf.fd < 0) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "\"include %s\": %s%s",
+					  nm, ERROR_STR(),
+					  wf_fnm_lno(&fnm_buf, wf));
+				result = 0;
+				goto next_line;
+			}
+			inc_buf.eob = inc_buf.c;
+			inc_buf.start = inc_buf.c;
+			cur_buf = &inc_buf;
+
+			if (0 > fstat(inc_buf.fd, &sb)) {
+				wf->wtbl->hdr.white_incs[white_fno].mtime = 0;
+			} else {
+				wf->wtbl->hdr.white_incs[white_fno
+							].mtime = sb.st_mtime;
+			}
+
+			wf->fno = ++white_fno;
+			main_lno = wf->lno+1;
+			wf->lno = 0;
+			goto next_line;
+		}
+
+		/* honor "option" lines in whiteclnt files */
+		opt = ck_word_white(type_nm, "option", LITZ("option"));
+		if (opt) {
+			/* stop continuation lines when we hit an option */
+			new_tgts = DCC_TGTS_INVALID;
+
+			if (!wf->wtbl) {
+				dcc_pemsg(EX_DATAERR, emsg, "\"%s\""
+					  " not legal in server whitelist%s",
+					  type_nm, wf_fnm_lno(&fnm_buf, wf));
+				result = 0;
+				goto next_line;
+			}
+
+			if (!parse_option(emsg, wf, opt, &fnm_buf,
+					  &thold_fnm_buf))
+				result = 0;
+			goto next_line;
+		}
+
+
+		/* Look for the number of targets in a simple line */
+		if (type_nm != bol) {
+			/* If the line started with white space, the number
+			 * of targets is the same as the previous line. */
+			*bol = '\0';
+		} else {
+			type_nm += strcspn(type_nm, DCC_WHITESPACE);
+			if (*type_nm == '\0') {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "missing type in \"%s\"%s",
+					  bol, wf_fnm_lno(&fnm_buf, wf));
+				result = 0;
+				goto next_line;
+			}
+			*type_nm++ = '\0';
+			/* bol now starts with null-terminated
+			 * number of targets, "include", or "log" */
+			type_nm += strspn(type_nm, DCC_WHITESPACE);
+		}
+
+		ck = type_nm+strcspn(type_nm, DCC_WHITESPACE);
+
+		if (*ck != '\0') {
+			/* null terminate the type */
+			*ck++ = '\0';
+			ck += strspn(ck, DCC_WHITESPACE);
+		}
+
+		if (strcasecmp(type_nm, "hex")) {
+			hex = 0;
+		} else {
+			hex = 1;
+			type_nm = ck;
+			ck = type_nm+strcspn(type_nm, DCC_WHITESPACE);
+			if (*ck != '\0') {
+				*ck++ = '\0';
+				ck += strspn(ck, DCC_WHITESPACE);
+			}
+		}
+
+		/* parse the target count if it is present instead of blank */
+		if (*bol != '\0')
+			new_tgts = dcc_str2cnt(bol);
+		if (new_tgts == 0 || new_tgts == DCC_TGTS_INVALID) {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "missing or invalid # of targets \"%s\"%s",
+				  bol, wf_fnm_lno(&fnm_buf, wf));
+			new_tgts = DCC_TGTS_INVALID;
+			result = 0;
+			goto next_line;
+		}
+
+		if (*ck == '\0') {
+			dcc_pemsg(EX_DATAERR, emsg, "missing value%s",
+				  wf_fnm_lno(&fnm_buf, wf));
+			new_tgts = DCC_TGTS_INVALID;
+			result = 0;
+			goto next_line;
+		}
+
+		type = dcc_str2type_wf(type_nm, -1);
+
+		if (new_tgts == DCC_TGTS_OK_MX
+		    || new_tgts == DCC_TGTS_OK_MXDCC
+		    || new_tgts == DCC_TGTS_SUBMIT_CLIENT) {
+			if (type != DCC_CK_IP) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  new_tgts == DCC_TGTS_SUBMIT_CLIENT
+					  ? "client must be an IP address%s"
+					  : "MX server must be an IP address%s",
+					  wf_fnm_lno(&fnm_buf, wf));
+				new_tgts = DCC_TGTS_INVALID;
+				result = 0;
+				goto next_line;
+			}
+			if (wf->wf_flags & DCC_WF_PER_USER) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "%s illegal in per-user whitelist%s",
+					  dcc_tgts2str(tgts_buf,
+						       sizeof(tgts_buf),
+						       new_tgts, 0),
+					  wf_fnm_lno(&fnm_buf, wf));
+				new_tgts = DCC_TGTS_INVALID;
+				result = 0;
+				goto next_line;
+			}
+		}
+
+		/* Look for the type of the checksum, compute the checksum,
+		 * and write the checksum to the hash table file.
+		 * Write all of its aliases if it is a host name. */
+		if (hex) {
+			i = dcc_parse_hex_ck(emsg, wf,
+					     type_nm, type, ck, new_tgts,
+					     add_fnc);
+		} else {
+			i = dcc_parse_ck(emsg, wf,
+					 type_nm, type, ck, new_tgts,
+					 add_fnc, cidr_fnc);
+		}
+		/* give up on a fatal problem adding a checksum to the file */
+		if (i < 0)
+			break;
+		if (i == 0)
+			result = 0;
+		else if (new_tgts == DCC_TGTS_OK || new_tgts == DCC_TGTS_OK2)
+			wf_fnm_lno(&white_fnm_buf, wf);
+	}
+
+	/* fatal problem such as writing to the file */
+	if (cur_buf != &main_buf)
+		close(cur_buf->fd);
+	return -1;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/parse_word.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/parse_word.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,167 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.24 $Revision$
+ */
+
+#include "dcc_defs.h"
+
+
+/* Get a "word" ended by whitespace, while honoring backslashes
+ *	on exit the remainder of the line is trimmed of leading whitespace */
+const char *				/* 0 or remainder of line */
+dcc_parse_word(DCC_EMSG emsg,
+	       char *tgt,		/* copy word to here if not null */
+	       int tgt_len,		/* includes trailing '\0' */
+	       const char *line,	/* line of words */
+	       const char *fieldname,
+	       const char *fnm, int lno)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	const char *p;
+	char c;
+
+	if (!tgt_len && tgt)
+		dcc_logbad(EX_SOFTWARE, "bad tgt_len for dcc_get_word(%s)%s",
+			   fieldname, fnm_lno(&fnm_buf, fnm, lno));
+
+	if (!line) {
+		if (tgt)
+			*tgt = '\0';
+		if (fieldname)
+			dcc_pemsg(EX_USAGE, emsg, "%s missing%s",
+				  fieldname, fnm_lno(&fnm_buf, fnm, lno));
+		return 0;
+	}
+
+	line = line+strspn(line, DCC_WHITESPACE);   /* skip leading blanks */
+
+	p = line;
+	do {
+		c = *p;
+
+		if (c != '\0') {
+			++p;
+			if (c == '\\' && *p != '\0') {
+				/* recognize and convert escape sequences to
+				 * their real equivalents */
+				if ((c = *p++) == 'n') {
+					c = '\n';
+				} else if (c == 'r') {
+					c = '\r';
+				} else if (c == 't') {
+					c = '\t';
+				} else if (c == 'b') {
+					c = '\b';
+				} else if (c >= '0' && c <= '7') {
+					c -= '0';
+					if (*p >= '0' && *p <= '7') {
+					    c = (c<<3)+(*p++ - '0');
+					    if (*p >= '0' && *p <= '7')
+						c = (c<<3)+(*p++ - '0');
+					}
+				}
+			} else if (strchr(DCC_WHITESPACE, c)) {
+				/* Stop on the first whitespace or delimiter */
+				c = '\0';
+				/* Skip trailing whitespace */
+				p += strspn(p, DCC_WHITESPACE);
+			}
+		}
+
+		if (tgt) {
+			if (!tgt_len) {
+				if (fieldname)
+					dcc_pemsg(EX_USAGE, emsg,
+						  "%s \"%.*s...\" too long%s",
+						  fieldname,
+						  min(16, (int)(p-line)), line,
+						  fnm_lno(&fnm_buf, fnm, lno));
+				return 0;
+			}
+			*tgt++ = c;
+			--tgt_len;
+		}
+	} while (c != '\0');
+
+	return p;
+}
+
+
+
+/* get a password */
+const char *				/* 0 or remainder of line */
+parse_passwd(DCC_EMSG emsg,
+	     DCC_PASSWD passwd,		/* copy password here */
+	     const char *line,		/* line of words */
+	     const char *fieldname,
+	     const char *fnm, int lno)
+{
+	char buf[sizeof(DCC_PASSWD)+1];
+	const char *result;
+
+	memset(buf, 0, sizeof(buf));
+	result = dcc_parse_word(emsg, buf, sizeof(buf),
+				line, fieldname, fnm, lno);
+	if (result)
+		memcpy(passwd, buf, sizeof(DCC_PASSWD));
+	else
+		memset(passwd, 0, sizeof(DCC_PASSWD));
+	return result;
+}
+
+
+
+/* see if a string starts with a word possibly followed by a comma */
+u_char					/* 1=yes */
+dcc_ck_word_comma(const char **parg, const char *word)
+{
+	u_int word_len = strlen(word);
+	const char *arg = *parg;
+
+	if (strncasecmp(arg, word, word_len))
+		return 0;
+	arg += word_len;
+	if (*arg == '\0') {
+		*parg = arg;
+		return 1;
+	}
+	if (*arg == ',') {
+		*parg = arg+1;
+		return 1;
+	}
+	return 0;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/print_info.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/print_info.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,361 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.56 $Revision$
+ */
+
+#include "dcc_clnt.h"
+#include "dcc_xhdr.h"
+
+
+static void
+print_srvr(const DCC_SRVR_CLASS *class,
+	   const DCC_SRVR_ADDR *ap,
+	   u_char print_name,
+	   u_char have_rtt_adj)
+{
+	char srvr_nm[DCC_MAXDOMAINLEN];
+	char a1[DCC_SU2STR_SIZE+1+5];
+	DCC_SOCKU su;
+	int addr_len, name_len, i, j;
+
+	printf("# %1s", class->srvr_inx == ap - class->addrs ? "*" : "");
+	if (print_name) {
+		dcc_mk_su(&su, ap->ip.family, &ap->ip.u, ap->ip.port);
+		dcc_su2name(srvr_nm, sizeof(srvr_nm), &su);
+	} else {
+		srvr_nm[0] = '\0';
+	}
+	addr_len = dcc_ap2str_opt(a1, sizeof(a1),
+				  class, ap - class->addrs, '-');
+	name_len = strlen(srvr_nm);
+	i = 22 - (name_len-25);
+	if (i < 1)
+		i = 1;
+	else if (i > 22)
+		i = 22;
+	j = 25 - (addr_len-22);
+	if (j < 1)
+		j = 1;
+	else if (j > 25)
+		j = 25;
+	printf("%-*s %-*s", i, a1, j, srvr_nm);
+	if (ap->srvr_id != DCC_ID_INVALID) {
+		i = 16 - ((addr_len+name_len) - (22+25));
+		if (i < 1)
+			i = 1;
+		else if (i > 16)
+			i = 16;
+		printf(" %*s ID %d", i, ap->brand, ap->srvr_id);
+		if (ap->srvr_pkt_vers != DCC_PKT_VERSION)
+			printf("\n#     protocol version %d",
+			       ap->srvr_pkt_vers);
+	}
+	putchar('\n');
+
+	if (ap->rtt >= DCC_RTT_BAD) {
+		fputs("#      not answering\n", stdout);
+		return;
+	}
+	if (ap->total_xmits == 0) {
+		printf("#     %22s", "");
+	} else {
+		printf("#     %3.0f%% of %2d requests ok",
+		       (ap->total_resps*100.0)/ap->total_xmits,
+		       ap->total_xmits);
+	}
+	if (have_rtt_adj) {
+		if (ap->srvr_pkt_vers < DCC_PKT_VERSION
+#ifdef DCC_PKT_VERSION7
+		    /* version 7 and 8 are the same for the free code */
+		    && ap->srvr_pkt_vers+1 != DCC_PKT_VERSION
+#endif
+		    && ap->srvr_id != DCC_ID_INVALID) {
+			i = printf("%8.2f%+d+%d ms RTT",
+				   ap->rtt/1000.0,
+				   class->nms[ap->nam_inx].rtt_adj/1000,
+				   DCC_RTT_VERS_ADJ/1000);
+		} else {
+			i = printf("%8.2f%+d ms RTT",
+				   ap->rtt/1000.0,
+				   class->nms[ap->nam_inx].rtt_adj/1000);
+		}
+	} else {
+		i = printf("%8.2f ms RTT",
+			   ap->rtt/1000.0);
+	}
+	i = (i >= 22) ? 1 : (22-i);
+	printf("  %*s  %4d ms queue wait\n",
+	       i, "", ap->srvr_wait/1000);
+}
+
+
+
+/* dump the /var/dcc/map file */
+void
+dcc_print_info(const char *map_nm,	/* or null for temporary file */
+	       const DCC_CLNT_INFO *info,
+	       u_char quiet,
+	       u_char grey,
+	       u_char srcbad,
+	       u_char names,
+	       u_char show_passwd)
+{
+#define dcc_clnt_info ??? error using dcc_clnt_info
+	DCC_PATH path;
+	char date_buf[40];
+	int port;
+	NAM_INX nam_inx;
+	time_t now;
+	const DCC_SRVR_CLASS *class;
+	const DCC_SRVR_ADDR *ap,*ap_prev, *ap_next;
+	u_char printed_addr[DCC_MAX_SRVR_ADDRS];
+	char sustr[DCC_SU2STR_SIZE];
+	u_char have_rtt_adj;
+	u_char need_blank_line;
+	int i;
+
+	now = time(0);
+	class = grey ? &info->grey : &info->dcc;
+	if (map_nm && !quiet) {
+		printf("# %-s  %s%s\n",
+		       dcc_time2str(date_buf, sizeof(date_buf), "%x %X %Z",
+				    now),
+		       grey ? "greylist " : "",
+		       fnm2abs_err(path, map_nm));
+		fputs("# ", stdout);
+		if (class->resolve > now || dcc_clnt_debug)
+			printf("Re-resolve names after %s  ",
+			       dcc_time2str(date_buf, sizeof(date_buf), "%X",
+					    class->resolve));
+
+		if (class->measure > now || dcc_clnt_debug)
+			printf("Check RTTs after %s",
+			       dcc_time2str(date_buf, sizeof(date_buf), "%X",
+					    class->measure));
+
+		putchar('\n');
+
+		fputs("# ", stdout);
+		i = 0;
+		for (ap = class->addrs; ap <= LAST(class->addrs); ++ap) {
+			if (ap->rtt != DCC_RTT_BAD
+			    && ap->ip.family != AF_UNSPEC)
+				++i;
+		}
+		if (i > 1 || dcc_clnt_debug)
+			printf("%6.2f ms threshold, %4.2f ms average    ",
+			       class->thold_rtt/1000.0,
+			       class->avg_thold_rtt/1000.0);
+		printf("%d total, %d working servers\n",
+		       class->num_srvrs, i);
+		if (class->fail_exp != 0) {
+			int fail_time = class->fail_time - now;
+			if (fail_time > 0
+			    && fail_time <= DCC_MAX_FAIL_SECS) {
+				printf("# skipping asking %s server"
+				       " %d seconds more\n",
+				       grey ? "greylist" : "DCC", fail_time);
+			}
+		}
+
+		i = now/DCCPROC_COST - info->dccproc_last/DCCPROC_COST;
+		if (i > DCCPROC_MAX_CREDITS*2)
+			i = DCCPROC_MAX_CREDITS*2;
+		else if (i < 0)
+			i = 0;
+		i += info->dccproc_c;
+		if (i > DCCPROC_MAX_CREDITS)
+			i = DCCPROC_MAX_CREDITS;
+		else if (i < -DCCPROC_MAX_CREDITS)
+			i = -DCCPROC_MAX_CREDITS;
+		if (!grey && (i < DCCPROC_MAX_CREDITS
+			      || !DCC_IS_TIME(now, info->dccproc_dccifd_try,
+					      DCCPROC_TRY_DCCIFD))) {
+			printf("# %d dccproc balance since %s",
+			       i, dcc_time2str(date_buf, sizeof(date_buf), "%X",
+					       info->dccproc_last));
+			if (!DCC_IS_TIME(now, info->dccproc_dccifd_try,
+					 DCCPROC_TRY_DCCIFD))
+				printf("; do not try to start dccifd until %s",
+				       dcc_time2str(date_buf, sizeof(date_buf),
+						    "%X",
+						    info->dccproc_dccifd_try));
+			putchar('\n');
+		}
+	}
+	if (map_nm && !grey) {
+		fputs((info->flags & DCC_INFO_FG_IPV6)
+		      ? DCC_INFO_USE_IPV6 : DCC_INFO_USE_IPV4, stdout);
+		if (info->flags & DCC_INFO_FG_SOCKS)
+			fputs("   "DCC_INFO_USE_SOCKS, stdout);
+		if (info->src.family != AF_UNSPEC) {
+			printf("   "DCC_INFO_USE_SRC"%s%s",
+			       dcc_ip2str(sustr, sizeof(sustr),
+					  &info->src),
+			       srcbad ? " "DCC_INFO_USE_SRCBAD : "");
+		}
+		putchar('\n');
+	}
+
+	have_rtt_adj = 0;
+	for (nam_inx = 0; nam_inx < DIM(class->nms); ++nam_inx) {
+		if (class->nms[nam_inx].hostname[0] == '\0')
+			continue;
+		if (class->nms[nam_inx].rtt_adj != 0) {
+			have_rtt_adj = 1;
+			break;
+		}
+	}
+	if (!have_rtt_adj) {
+		for (ap = class->addrs; ap<=LAST(class->addrs); ++ap) {
+			if (ap->srvr_pkt_vers < DCC_PKT_VERSION
+#ifdef DCC_PKT_VERSION7
+			    /* version 7 and 8 are the same for the free code */
+			    && ap->srvr_pkt_vers+1 != DCC_PKT_VERSION
+#endif
+			    && ap->srvr_id != DCC_ID_INVALID) {
+				have_rtt_adj = 1;
+				break;
+			}
+		}
+	}
+
+	memset(printed_addr, 0, sizeof(printed_addr));
+
+	/* convert each non-null hostname */
+	need_blank_line = 1;
+	for (nam_inx = 0; nam_inx < DIM(class->nms); ++nam_inx) {
+		if (class->nms[nam_inx].hostname[0] == '\0')
+			continue;
+
+		/* First display the main line for a host */
+		if (class->nms[nam_inx].defined == 0)
+			need_blank_line = 1;
+		if (!need_blank_line && nam_inx != 0) {
+			for (ap = class->addrs; ap<=LAST(class->addrs); ++ap) {
+				if (ap->nam_inx == nam_inx) {
+					need_blank_line = 1;
+					break;
+				}
+			}
+		}
+		if (need_blank_line) {
+			need_blank_line = 0;
+			if (!quiet)
+				putchar('\n');
+		}
+		i = printf("%s", class->nms[nam_inx].hostname);
+		i = (i >= 26) ? 1 : (26-i);
+		port = class->nms[nam_inx].port;
+		if (port == DCC_GREY2PORT(grey))
+			printf(",%-*s", i, "-   ");
+		else
+			printf(",%-*d", i, ntohs(port));
+
+		if (grey)
+			fputs(" Greylist", stdout);
+
+		if (have_rtt_adj) {
+			i = printf(" RTT%+d ms",
+				   class->nms[nam_inx].rtt_adj/1000);
+			i = (i >= 12) ? 1 : (12-i);
+			printf("%*s", i, "");
+		}
+
+		/* Suppress the password if it does not exist or is secret */
+		if (class->nms[nam_inx].clnt_id == DCC_ID_ANON) {
+			fputs(" "DCC_XHDR_ID_ANON"\n", stdout);
+		} else {
+			printf(" %5d "DCC_PASSWD_PAT"\n",
+			       class->nms[nam_inx].clnt_id,
+			       show_passwd ? class->nms[nam_inx].passwd : "");
+		}
+
+		if (class->nms[nam_inx].defined == 0) {
+			need_blank_line = 1;
+			if (!quiet)
+				fputs("#   UNDEFINED\n", stdout);
+		}
+
+		/* display operating information for each IP address
+		 * kludge sort the IP addresses */
+		for (ap_prev = 0, i = 0;
+		     i < DCC_MAX_SRVR_ADDRS;
+		     ap_prev = ap_next, ++i) {
+			ap_next = 0;
+			for (ap = class->addrs; ap<=LAST(class->addrs); ++ap) {
+				if (ap->nam_inx != nam_inx
+				    || ap->ip.family == AF_UNSPEC)
+					continue;
+				/* find smallest IP address not yet printed */
+				if (printed_addr[ap - class->addrs])
+					continue;
+				if (ap_next
+				    && ap->ip.family >= ap_next->ip.family
+				    && 0 <= memcmp(&ap->ip.u, &ap_next->ip.u,
+						   sizeof(ap->ip.u))
+				    && ap->ip.port >= ap_next->ip.port)
+					continue;
+				if (!ap_prev
+				    || ap->ip.family >= ap_prev->ip.family
+				    || 0 <= memcmp(&ap->ip.u, &ap_prev->ip.u,
+						   sizeof(ap->ip.u))
+				    || ap->ip.port >= ap_prev->ip.port)
+					ap_next = ap;
+			}
+			if (!ap_next)
+				break;
+			if (!quiet)
+				print_srvr(class, ap_next, names, have_rtt_adj);
+			printed_addr[ap_next - class->addrs] = 1;
+			need_blank_line = 1;
+
+		}
+	}
+
+	for (ap = class->addrs, i = 0; ap <= LAST(class->addrs); ++ap, ++i) {
+		if (ap->ip.family == 0)
+			continue;
+		if (printed_addr[i])
+			continue;
+		printf("\n# stray address entry #%d, nam_inx %d:\n",
+		       i, ap->nam_inx);
+		print_srvr(class, ap, names, have_rtt_adj);
+	}
+
+#undef dcc_clnt_info
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/restart.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/restart.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,231 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * continually restart a daemon
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.16 $Revision$
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <pwd.h>
+#include "dcc_clnt.h"
+
+
+static pid_t start_pid = -1;
+
+static void
+restart_sigterm(int sig)
+{
+	if (start_pid > 0
+	    && 0 <= kill(start_pid, sig))
+		return;
+	exit(-1);
+}
+
+
+
+#define MIN_RESTART_DELAY   5
+#define MAX_RESTART_DELAY   (5*60)
+
+/* stay alive despite core dumps
+ *	Restart immediately, but not more often than every MINRESTART_DELAY
+ *	seconds.  If the restarts are happening at the maximum rate,
+ *	halve the rate. */
+void
+dcc_daemon_restart(const char *rundir, void(reconn)(void))
+{
+#if defined(WIFEXITED) && defined(WTERMSIG) && defined(WIFSIGNALED)
+	pid_t pid;
+	time_t next_restart, restart_delay;
+	int status;
+	const char *etype;
+	DCC_PATH pidpath;
+
+	signal(SIGHUP, restart_sigterm);
+	signal(SIGTERM, restart_sigterm);
+	signal(SIGINT, restart_sigterm);
+
+	restart_delay = MIN_RESTART_DELAY;
+	next_restart = 0;
+	for (;;) {
+		start_pid = fork();
+		if (!start_pid) {
+#ifdef HAVE_SETPGID
+			if (0 > setpgid(0, 0))
+				dcc_error_msg("setpgid(): %s", ERROR_STR());
+#endif
+			/* reconnect sockets or whatever except first time */
+			if (next_restart != 0
+			    && reconn)
+				reconn();
+			return;
+		}
+
+		if (start_pid < 0)
+			dcc_logbad(EX_OSERR, "(re)start fork(): %s",
+				   ERROR_STR());
+
+		next_restart = time(0)+restart_delay;
+		pid = waitpid(start_pid, &status, 0);
+		if (pid < 0) {
+			if (errno != EINTR)
+				dcc_logbad(EX_OSERR, "restart waitpid(): %s",
+					   ERROR_STR());
+			exit(0);
+		}
+
+		start_pid = -1;
+		if (WIFEXITED(status)) {
+			status = WEXITSTATUS(status);
+			etype = "exit ";
+			if (status != EX_DCC_RESTART) {
+				if (dcc_clnt_debug)
+					dcc_error_msg("do not restart after"
+						      " exit(%d)",
+						      status);
+				exit(status);
+			}
+
+		} else if (WIFSIGNALED(status)) {
+			status = WTERMSIG(status);
+			etype = "signal #";
+			if (status != SIGILL
+			    && status != SIGSEGV
+#ifdef SIGBUS
+			    && status != SIGBUS
+#endif
+#ifdef SIGXCPU
+			    && status != SIGXCPU
+#endif
+#ifdef SIGFPE
+			    && status != SIGFPE
+#endif
+#ifdef SIGSYS
+			    && status != SIGSYS
+#endif
+#ifdef SIGTRAP
+			    && status != SIGTRAP
+#endif
+			    && status != SIGQUIT
+			    && status != SIGABRT) {
+				if (dcc_clnt_debug)
+					dcc_error_msg("do not restart after"
+						      " signal %d",
+						      status);
+				exit(0);
+			}
+
+		} else {
+			dcc_logbad(EX_OSERR, "unknown exit status %#x", status);
+		}
+
+		next_restart -= time(0);
+		if (next_restart > 0 && next_restart <= MAX_RESTART_DELAY) {
+			restart_delay *= 2;
+			if (restart_delay > MAX_RESTART_DELAY)
+				restart_delay = MAX_RESTART_DELAY;
+
+			/* while we wait, become the designated target */
+			dcc_error_msg("delay %d seconds to restart after %s%d",
+				      (int)next_restart, etype, status);
+			dcc_pidfile(pidpath, rundir);
+			sleep(next_restart);
+			unlink(pidpath);
+
+		} else {
+			restart_delay -= next_restart-1;
+			if (restart_delay < MIN_RESTART_DELAY)
+				restart_delay = MIN_RESTART_DELAY;
+		}
+		dcc_error_msg("restart after %s%d", etype, status);
+	}
+#endif /* not POSIX */
+}
+
+
+
+/* change to the UID and GID of a daemon */
+void
+dcc_daemon_su(const char *user)
+{
+	struct passwd *pw;
+
+	pw = getpwnam(user);
+	if (!pw) {
+		dcc_error_msg("getpwnam(%s): %s", user, ERROR_STR());
+		return;
+	}
+
+	if (0 > setgid(pw->pw_gid))
+		dcc_error_msg("setgid(%d %s): %s",
+			      (int)pw->pw_gid, user, ERROR_STR());
+	if (0 > setuid(pw->pw_uid))
+		dcc_error_msg("setuid(%d %s): %s",
+			      (int)pw->pw_uid, user, ERROR_STR());
+}
+
+
+
+void
+dcc_pidfile(DCC_PATH pidpath, const char *rundir)
+{
+	FILE *f;
+
+	snprintf(pidpath, sizeof(DCC_PATH), "%s/%s.pid",
+		 rundir, dcc_progname);
+	unlink(pidpath);
+	f = fopen(pidpath, "w");
+	if (!f) {
+		dcc_error_msg("fopen(%s): %s", pidpath, ERROR_STR());
+	} else {
+#ifdef linux
+		/* Linux threads are broken.  Signals given the
+		 * original process are delivered to only the
+		 * thread that happens to have that PID.  The
+		 * sendmail libmilter thread that needs to hear
+		 * SIGINT and other signals is known only to the milter.
+		 * Unless you put its PID into the file, it will not hear
+		 * the signals.  That breaks scripts that need to stop dccm.
+		 * However, signaling the process group works. */
+		fprintf(f, "-%d\n", (u_int)getpgrp());
+#else
+		fprintf(f, "%d\n", (u_int)getpid());
+#endif
+		fclose(f);
+	}
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/sign.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/sign.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,80 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * message authentication
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.13 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include "dcc_md5.h"
+#include <string.h>
+
+
+/* sign a DCC packet */
+void
+dcc_sign(const char *passwd,
+	 int passwd_len,
+	 void *pkt,
+	 u_int pkt_len)			/* including signature */
+{
+	MD5_CTX ctx;
+
+	MD5Init(&ctx);
+	MD5Update(&ctx, passwd, passwd_len);
+	MD5Update(&ctx, pkt, pkt_len-sizeof(DCC_SIGNATURE));
+	MD5Final(&((u_char*)pkt)[pkt_len-sizeof(DCC_SIGNATURE)], &ctx);
+}
+
+
+
+/* check the signature of a DCC packet */
+u_char					/* 0=forgery, 1=good signature */
+dcc_ck_signature(const char *passwd,
+		 int passwd_len,
+		 const void *pkt,
+		 u_int pkt_len)		/* including signature */
+{
+	MD5_CTX ctx;
+	DCC_SIGNATURE signature;
+
+	MD5Init(&ctx);
+	MD5Update(&ctx, passwd, passwd_len);
+	MD5Update(&ctx, pkt, pkt_len-sizeof(DCC_SIGNATURE));
+	MD5Final(signature, &ctx);
+	return !memcmp(signature, &((char*)pkt)[pkt_len-sizeof(DCC_SIGNATURE)],
+		       sizeof(signature));
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/str2cnt.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/str2cnt.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,69 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.15 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include "dcc_xhdr.h"
+
+
+DCC_TGTS
+dcc_str2cnt(const char *str)
+{
+	u_long l;
+	char *p;
+
+	l = strtoul(str, &p, 0);
+	if (*p == '\0') {
+		if (l > DCC_TGTS_TOO_MANY)
+			l = DCC_TGTS_TOO_MANY;
+		return l;
+	}
+	if (!strcasecmp(str, DCC_XHDR_TOO_MANY))
+		return DCC_TGTS_TOO_MANY;
+	if (!strcasecmp(str, DCC_XHDR_OK))
+		return DCC_TGTS_OK;
+	if (!strcasecmp(str, DCC_XHDR_OK2))
+		return DCC_TGTS_OK2;
+	if (!strcasecmp(str, DCC_XHDR_OK_MX))
+		return DCC_TGTS_OK_MX;
+	if (!strcasecmp(str, DCC_XHDR_OK_MXDCC))
+		return DCC_TGTS_OK_MXDCC;
+	if (!strcasecmp(str, DCC_XHDR_SUBMIT_CLIENT))
+		return DCC_TGTS_SUBMIT_CLIENT;
+	return DCC_TGTS_INVALID;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/str2type.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/str2type.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,184 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.27 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include "dcc_xhdr.h"
+#include <ctype.h>
+
+
+static struct tbl {
+    char	nm[DCC_XHDR_MAX_TYPE_LEN];
+    DCC_CK_TYPES type;
+} tbl[] = {
+    {DCC_XHDR_TYPE_IP,		DCC_CK_IP},
+    {DCC_XHDR_TYPE_ENV_FROM,	DCC_CK_ENV_FROM},
+    {DCC_XHDR_TYPE_FROM,	DCC_CK_FROM},
+    {DCC_XHDR_TYPE_SUB,		DCC_CK_SUB},
+    {DCC_XHDR_TYPE_MESSAGE_ID,	DCC_CK_MESSAGE_ID},
+    {DCC_XHDR_TYPE_RECEIVED,	DCC_CK_RECEIVED},
+    {DCC_XHDR_TYPE_BODY,	DCC_CK_BODY},
+    {DCC_XHDR_TYPE_FUZ1,	DCC_CK_FUZ1},
+    {DCC_XHDR_TYPE_FUZ2,	DCC_CK_FUZ2},
+    {DCC_XHDR_TYPE_GREY_MSG,	DCC_CK_G_MSG_R_TOTAL},
+    {DCC_XHDR_TYPE_GREY_TRIPLE,	DCC_CK_G_TRIPLE_R_BULK},
+    {DCC_XHDR_TYPE_REP_TOTAL,	DCC_CK_G_MSG_R_TOTAL},
+    {DCC_XHDR_TYPE_REP_BULK,	DCC_CK_G_TRIPLE_R_BULK},
+    {DCC_XHDR_TYPE_ENV_TO,	DCC_CK_ENV_TO},	/* same as DCC_CK_FLOD_PATH */
+    {DCC_XHDR_TYPE_FLOD_PATH,	DCC_CK_FLOD_PATH},  /* same as DCC_CK_ENV_TO */
+    {DCC_XHDR_TYPE_SRVR_ID,	DCC_CK_SRVR_ID},
+
+    {"ALL",			SET_ALL_THOLDS},
+    {"CMN",			SET_CMN_THOLDS},
+};
+
+
+
+static DCC_CK_TYPES
+dcc_str2type_base(const char *str,
+		  int len0)		/* length or -1 */
+{
+	struct tbl *tp;
+	const char *tgtp, *nmp;
+	char tgtc, nmc, d;
+	int len, i;
+
+	/* ignore leading blanks */
+	i = strspn(str, DCC_WHITESPACE);
+	str += i;
+	if (len0 >= 0) {
+		len0 -= i;
+		if (len0 <= 0)
+			return DCC_CK_INVALID;
+	}
+
+	for (tp = tbl; tp <= LAST(tbl); ++tp) {
+		nmp = tp->nm;
+		tgtp = str;
+		len = len0;
+		do {
+			if (len >= 0 && --len < 0)
+				tgtc = '\0';
+			else
+				tgtc = *tgtp++;
+			nmc = *nmp++;
+			if (nmc == '\0') {
+				/* ignore trailing blanks and colons */
+				while (tgtc == ':' || tgtc == ' '
+				       || tgtc == '\t' || tgtc == '\n') {
+					if (len >= 0 && --len < 0)
+					    tgtc = '\0';
+					else
+					    tgtc = *tgtp++;
+				}
+				if (tgtc == '\0')
+					return tp->type;
+				break;
+			}
+			d = (nmc ^ tgtc);
+		} while (d == 0 || d == ('A' ^ 'a')
+			 || ((nmc == '-' || nmc == '.' || nmc == '_')
+			     && (tgtc == '-' || tgtc == '.' || tgtc == '_')));
+	}
+
+	return DCC_CK_INVALID;
+}
+
+
+
+/* for whiteclnt files and dccsight */
+DCC_CK_TYPES
+dcc_str2type_wf(const char *str,
+		 int len0)		/* length or -1 */
+{
+	DCC_CK_TYPES type;
+
+	type = dcc_str2type_base(str, len0);
+	if (!DCC_CK_THOLD_OK(type)
+	    && type != DCC_CK_ENV_TO)
+		return DCC_CK_INVALID;
+	return type;
+}
+
+
+
+/* types that can be deleted in database */
+DCC_CK_TYPES
+dcc_str2type_del(const char *str,
+	     int len0)			/* length or -1 */
+{
+	DCC_CK_TYPES type;
+
+	type = dcc_str2type_base(str, len0);
+	if (!DCC_CK_THOLD_OK(type)
+	    && type != DCC_CK_SRVR_ID)
+		return DCC_CK_INVALID;
+	return type;
+}
+
+
+
+/* for `dbclean -t type,...`  `dccd -K type`  `dccproc -g type` */
+DCC_CK_TYPES
+dcc_str2type_db(const char *str,
+		 int len0)		/* length or -1 */
+{
+	DCC_CK_TYPES type;
+
+	type = dcc_str2type_base(str, len0);
+	if (!DCC_CK_THOLD_OK(type))
+		return DCC_CK_INVALID;
+	return type;
+}
+
+
+
+/* for `dbclean -t type,...` and dccm, dccifd, and dccproc thresholds */
+DCC_CK_TYPES
+dcc_str2type_thold(const char *str,
+		   int len0)		/* length or -1 */
+{
+	DCC_CK_TYPES type;
+
+	type = dcc_str2type_base(str, len0);
+	if (!DCC_CK_THOLD_OK(type)
+	    && type != SET_ALL_THOLDS
+	    && type != SET_CMN_THOLDS)
+		return DCC_CK_INVALID;
+	return type;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/strlcat.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/strlcat.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,24 @@
+/* compatibility hack */
+
+#include "dcc_defs.h"
+
+int
+dcc_strlcat(char *dst, const char *src, int lim)
+{
+	int dlen, slen, delta;
+
+	dlen = strlen(dst);
+	delta = lim - dlen;
+	if (delta <= 0)
+		return lim;
+
+	slen = strlen(src);
+	if (slen >= delta)
+		slen = delta-1;
+	if (slen <= 0)
+		return dlen;
+	memcpy(dst+dlen, src, slen);
+	dlen += slen;
+	dst[dlen] = '\0';
+	return dlen;
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/su2str.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/su2str.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,245 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.42 $Revision$
+ */
+
+#include "dcc_defs.h"
+#ifndef DCC_WIN32
+#include <arpa/inet.h>
+#endif
+
+#if !defined(HAVE_INET_NTOP) || defined(NO_IPV6)
+#define DCC_INET_NTOP dcc_inet_ntop
+extern const char *DCC_INET_NTOP(int, const void *, char *, size_t);
+#else
+#define DCC_INET_NTOP inet_ntop
+#endif
+
+
+
+/* strip IPv6 prefix from ::ffff:10.2.3.4 */
+const char *
+dcc_trim_ffff(const char *str)
+{
+	return strncmp("::ffff:", str, 7) ? str : (str+7);
+}
+
+
+
+const char *
+dcc_ipv4tostr(char *buf, int buf_len, const struct in_addr *addr4)
+{
+	if (!DCC_INET_NTOP(AF_INET, addr4, buf, buf_len))
+		STRLCPY(buf, "???", buf_len);
+	return buf;
+}
+
+
+
+const char *
+dcc_ipv6tostr(char *buf, int buf_len, const struct in6_addr *addr6)
+{
+	if (!DCC_INET_NTOP(AF_INET6, addr6, buf, buf_len))
+		STRLCPY(buf, "???", buf_len);
+	return buf;
+}
+
+
+
+const char *
+dcc_ipv6tostr2(char *buf, int buf_len, const struct in6_addr *addr6)
+{
+	struct in_addr addr4;
+
+	if (dcc_ipv6toipv4(&addr4, addr6))
+		return dcc_ipv4tostr(buf, buf_len, &addr4);
+
+	if (!DCC_INET_NTOP(AF_INET6, addr6, buf, buf_len))
+		STRLCPY(buf, "???", buf_len);
+	return buf;
+}
+
+
+
+const char *
+dcc_ip2str(char *buf, int buf_len, const DCC_IP *ip)
+{
+	if (ip->family == AF_INET)
+		return dcc_ipv4tostr(buf, buf_len, &ip->u.v4);
+	else
+		return dcc_ipv6tostr(buf, buf_len, &ip->u.v6);
+}
+
+
+
+/* convert to a string including the port number.
+ *	try to make a short IPv4 string */
+const char *
+dcc_su2str(char *buf, int buf_len, const DCC_SOCKU *su)
+{
+	int i;
+
+	if (su->sa.sa_family == AF_INET) {
+		dcc_ipv4tostr(buf, buf_len, &su->ipv4.sin_addr);
+	} else {
+		dcc_ipv6tostr2(buf, buf_len, &su->ipv6.sin6_addr);
+	}
+	i = strlen(buf);
+	snprintf(buf+i, buf_len-i, ",%d", ntohs(*DCC_SU_PORTP(su)));
+	return buf;
+}
+
+
+
+/* convert to a string without the port number.
+ *	try to make a short IPv4 string */
+const char *
+dcc_su2str2(char *buf, int buf_len, const DCC_SOCKU *su)
+{
+	if (su->sa.sa_family == AF_INET)
+		return dcc_ipv4tostr(buf, buf_len, &su->ipv4.sin_addr);
+
+	return dcc_ipv6tostr2(buf, buf_len, &su->ipv6.sin6_addr);
+}
+
+
+
+/* convert to a string without a boring port number
+ *	try to make a short IPv4 string */
+const char *
+dcc_su2str3(char *buf, int buf_len, const DCC_SOCKU *su, u_short port)
+{
+	if (*DCC_SU_PORTP(su) != port) {
+		return dcc_su2str(buf, buf_len, su);
+	} else {
+		return dcc_su2str2(buf, buf_len, su);
+	}
+}
+
+
+
+/* Convert IP address to a string, but not into a single buffer
+ *	This is not thread safe but good enough for error messages */
+const char *
+dcc_su2str_err(const DCC_SOCKU *su)
+{
+	static int bufno;
+	static struct {
+	    char    str[DCC_SU2STR_SIZE];
+	} bufs[4];
+	char *s;
+
+	s = bufs[bufno].str;
+	bufno = (bufno+1) % DIM(bufs);
+	return dcc_su2str(s, sizeof(bufs[0].str), su);
+}
+
+
+
+const char *
+dcc_host_portname(char *buf, int buf_len,
+		  const char *hostname, const char *portname)
+{
+	if (!portname || *portname == '\0' || !strcmp(portname, "-")) {
+		STRLCPY(buf, hostname, buf_len);
+	} else {
+		snprintf(buf, buf_len, "%s,%s", hostname, portname);
+	}
+	return buf;
+}
+
+
+
+/* Convert IP address to a name */
+const char *
+dcc_su2name(char *name, int name_len, const DCC_SOCKU *su)
+{
+#undef EXPANDED
+#if defined(USE_GETIPNODEBYNAME) && !defined(EXPANDED) && !defined(NO_IPV6)
+#define EXPANDED
+	struct hostent *hp;
+	int error;
+
+	dcc_host_lock();
+	if (su->sa.sa_family == AF_INET)
+		hp = getipnodebyaddr(&su->ipv4.sin_addr,
+				     sizeof(su->ipv4.sin_addr),
+				     su->sa.sa_family, &error);
+	else
+		hp = getipnodebyaddr(&su->ipv6.sin6_addr,
+				     sizeof(su->ipv6.sin6_addr),
+				     su->sa.sa_family, &error);
+	if (!hp) {
+		if (name_len > 0)
+			*name = '\0';
+	} else {
+		if (name_len > 0)
+			STRLCPY(name, hp->h_name, name_len);
+		freehostent(hp);
+	}
+	dcc_host_unlock();
+#endif
+#if defined(USE_GETADDRINFO) && !defined(EXPANDED) && !defined(NO_IPV6)
+#define EXPANDED
+	int error;
+
+	dcc_host_lock();
+	error = getnameinfo(&su->sa, DCC_SU_LEN(su),
+			    name, name_len, 0, 0, NI_NAMEREQD);
+	dcc_host_unlock();
+	if (error && name_len > 0)
+		*name = '\0';
+#endif
+#ifndef EXPANDED
+	struct hostent *hp;
+	DCC_SOCKU su4;
+
+	dcc_host_lock();
+	if (dcc_ipv6sutoipv4(&su4, su)) {
+		hp = gethostbyaddr((char *)&su4.ipv4.sin_addr,
+				   sizeof(su4.ipv4.sin_addr),
+				   AF_INET);
+	} else {
+		hp = 0;
+	}
+	if (name_len > 0)
+		STRLCPY(name, hp ? hp->h_name : "", name_len);
+	dcc_host_unlock();
+#endif
+	return name;
+#undef EXPANDED
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/tgts2str.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/tgts2str.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,110 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.23 $Revision$
+ */
+
+#include "dcc_ck.h"
+#include "dcc_xhdr.h"
+
+
+char *
+dcc_tgts2str(char *buf, u_int buf_len, DCC_TGTS tgts, u_char grey)
+{
+	switch (tgts) {
+	case DCC_TGTS_TOO_MANY:
+		if (grey)
+			STRLCPY(buf, DCC_XHDR_GREY_PASS, buf_len);
+		else
+			STRLCPY(buf, DCC_XHDR_TOO_MANY, buf_len);
+		break;
+	case DCC_TGTS_OK:
+		STRLCPY(buf, DCC_XHDR_OK, buf_len);
+		break;
+	case DCC_TGTS_OK2:
+		if (grey)		/* DCC_TGTS_GREY_WHITE */
+			STRLCPY(buf, DCC_XHDR_OK, buf_len);
+		else
+			STRLCPY(buf, DCC_XHDR_OK2, buf_len);
+		break;
+	case DCC_TGTS_DEL:
+		STRLCPY(buf, DCC_XHDR_DEL, buf_len);
+		break;
+	case DCC_TGTS_REP_ADJ:
+		STRLCPY(buf, DCC_XHDR_TGTS_REP_ADJ, buf_len);
+		break;
+	case DCC_TGTS_OK_MX:
+		STRLCPY(buf, DCC_XHDR_OK_MX, buf_len);
+		break;
+	case DCC_TGTS_OK_MXDCC:
+		STRLCPY(buf, DCC_XHDR_OK_MXDCC, buf_len);
+		break;
+	case DCC_TGTS_SUBMIT_CLIENT:
+		STRLCPY(buf, DCC_XHDR_SUBMIT_CLIENT, buf_len);
+		break;
+	case DCC_TGTS_INVALID:
+		STRLCPY(buf, DCC_XHDR_INVALID, buf_len);
+		break;
+	default:
+		if (tgts & DCC_TGTS_SPAM)
+			snprintf(buf, buf_len, "%d spam",
+				 tgts & ~DCC_TGTS_SPAM);
+		else
+			snprintf(buf, buf_len, "%d",
+				 tgts);
+		break;
+	}
+
+	return buf;
+}
+
+
+
+char *
+dcc_thold2str(char *buf, u_int buf_len, DCC_CK_TYPES type, DCC_TGTS tgts)
+{
+	if (tgts == DCC_THOLD_NEVER) {
+		STRLCPY(buf, DCC_XHDR_THOLD_NEVER, buf_len);
+		return buf;
+	}
+
+	if (type == DCC_CK_REP_BULK) {
+		snprintf(buf, buf_len, "%u%%", tgts);
+		return buf;
+	}
+
+	return dcc_tgts2str(buf, buf_len, tgts, 0);
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/type2str.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/type2str.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,110 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.23 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include "dcc_xhdr.h"
+
+char *
+dcc_type2str(char *buf, u_int buf_len,	/* put it here */
+	     DCC_CK_TYPES type,		/* this type */
+	     const char *sub_str,	/* sub-type for DCC_CK_SUB */
+	     u_char is_db,		/* 0=whitelist 1=database */
+	     u_char is_grey_db)		/* 1=greylist DB */
+{
+#define PCK(t) case DCC_CK_##t:					\
+	STRLCPY(buf,DCC_XHDR_TYPE_##t,buf_len);			\
+	return buf;
+#define PCK2(c,sw,t1,t2) case DCC_CK_##c:			\
+	if (!sw)						\
+		STRLCPY(buf,DCC_XHDR_TYPE_##t1,buf_len);	\
+	else							\
+		STRLCPY(buf,DCC_XHDR_TYPE_##t2,buf_len);	\
+	return buf;
+
+	switch (type) {
+	PCK(IP)
+	PCK(ENV_FROM)
+	PCK(FROM)
+	PCK(MESSAGE_ID)
+	PCK(RECEIVED)
+	PCK(BODY)
+	PCK(FUZ1)
+	PCK(FUZ2)
+	PCK2(G_MSG_R_TOTAL, is_grey_db, REP_TOTAL, GREY_MSG)
+	PCK2(G_TRIPLE_R_BULK, is_grey_db, REP_BULK, GREY_TRIPLE)
+	PCK(SRVR_ID)
+	PCK2(FLOD_PATH, is_db, ENV_TO, FLOD_PATH)
+	case DCC_CK_SUB:
+		if (sub_str)
+			snprintf(buf, buf_len, DCC_XHDR_TYPE_SUB" %s",
+				 sub_str);
+		else
+			snprintf(buf, buf_len, DCC_XHDR_TYPE_SUB);
+		return buf;
+	case DCC_CK_INVALID:
+		break;
+	}
+
+	snprintf(buf, buf_len, "unknown %d", type);
+	return buf;
+#undef PCK
+#undef PCK2
+}
+
+
+
+/* use sparingly for error messages since it is not thread safe */
+const char *
+dcc_type2str_err(DCC_CK_TYPES type,	/* type to convert to a string */
+		 const char *sub_str,	/* sub-type for DCC_CK_SUB */
+		 u_char is_db,		/* 0=whitelist 1=database */
+		 u_char is_grey_db)	/* 1=greylist DB */
+{
+	static int bufno;
+	static struct {
+	    char    str[DCC_XHDR_MAX_TYPE_LEN+1];
+	} bufs[4];
+	char *s;
+
+	s = bufs[bufno].str;
+	bufno = (bufno+1) % DIM(bufs);
+
+	return dcc_type2str(s, sizeof(bufs[0].str),
+			    type, sub_str, is_db, is_grey_db);
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/vsyslog.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/vsyslog.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,17 @@
+/* compatibility hack for old systems that don't have vsyslog() */
+
+#include "dcc_config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+/* this is not thread safe */
+void
+dcc_vsyslog(int pri, const char *fmt, va_list args)
+{
+    char buf[512];
+
+    vsnprintf(buf, sizeof(buf), fmt, args);
+    syslog(pri, "%s", buf);
+}
diff -r 000000000000 -r c7f6b056b673 dcclib/win32.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/win32.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,421 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * routines to make WIN32 look sort of reasonable
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.23 $Revision$
+ */
+
+
+#include "dcc_defs.h"
+
+#ifdef DCC_WIN32
+
+static DCC_PATH path_tmp;
+const char *_PATH_TMP = path_tmp;
+
+static u_char is_nt;
+
+
+void
+win32_init(void)
+{
+	OSVERSIONINFO ver;
+	WSADATA WSAData;
+
+	ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+	GetVersionEx(&ver);
+	is_nt = (ver.dwPlatformId == VER_PLATFORM_WIN32_NT);
+
+	if (WSAStartup(MAKEWORD(2, 0), &WSAData))
+		dcc_logbad(EX_SOFTWARE, "WSAStartup(): %s", ERROR_STR());
+
+	atexit((void(*)(void))WSACleanup);
+
+	GetTempPath(sizeof(path_tmp), path_tmp);
+
+#ifdef __BORLANDC__
+	_fmode = O_BINARY;
+#endif
+}
+
+
+
+int
+gettimeofday(struct timeval *tv, struct timezone *tzp)
+{
+	static SYSTEMTIME epoch_st = {1970, 1, 0, 1, 0, 0, 0, 0};
+	static LONGLONG epoch;		/* the first second of the UNIX epoch */
+	LONGLONG now;
+
+	if (epoch == 0)
+		SystemTimeToFileTime(&epoch_st, (FILETIME *)&epoch);
+	GetSystemTimeAsFileTime((FILETIME *)&now);
+	now -= epoch;
+	now /= 10;
+	tv->tv_sec = now/(1000*1000);
+	tv->tv_usec = now%(1000*1000);
+	return 0;
+}
+
+
+
+void
+win32_unmap(HANDLE *hp, void *p, const char *nm)
+{
+	if (!UnmapViewOfFile(p))
+		dcc_error_msg("UnmapViewOfFile(%s): %s", nm, ERROR_STR());
+	if (!CloseHandle(*hp))
+		dcc_error_msg("CloseHandle(%s): %s", nm, ERROR_STR());
+	*hp = INVALID_HANDLE_VALUE;
+}
+
+
+
+void *
+win32_map(DCC_EMSG emsg,
+	  HANDLE *map_handle,		/* put handle for the map here */
+	  const char *nm,		/* for this resolved path name */
+	  int fd,			/* with this C style file descriptor */
+	  int size)			/* with this size (to extend file) */
+{
+	static char junk;		/* foil optimizer */
+	DCC_PATH map_nm;
+	HANDLE h;
+	void *p;
+	int i;
+
+	/* make a name for the mapping */
+	if (!fnm2abs(map_nm, nm, 0))
+		STRLCPY(map_nm, nm, sizeof(DCC_PATH));
+	for (i = 0; i < sizeof(DCC_PATH) && map_nm[i] != '\0'; ++i) {
+		if (map_nm[i] == '/' || map_nm[i] == '\\')
+			map_nm[i] = '-';
+	}
+
+	h = CreateFileMapping((HANDLE)_get_osfhandle(fd),
+			      0, PAGE_READWRITE, 0, size, map_nm);
+	if (!h) {
+		dcc_pemsg(EX_IOERR, emsg, "CreateFileMapping(%s): %s",
+			  nm, ERROR_STR());
+		*map_handle = INVALID_HANDLE_VALUE;
+		return 0;
+	}
+	p = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0,0, size);
+	if (!p) {
+		dcc_pemsg(EX_IOERR, emsg, "MapViewOfFile(%s): %s",
+			  nm, ERROR_STR());
+		CloseHandle(h);
+		*map_handle = INVALID_HANDLE_VALUE;
+		return 0;
+	}
+
+	/* If you immediately lock the file, the mapping is garbage on Win98.
+	 * It seems to help to poke at the mapping.
+	 * One theory is that a page fault on a locked file fails */
+	for (i = 0; i < size; i += 512)
+		junk += ((char *)p)[i];
+
+	*map_handle = h;
+	return p;
+}
+
+
+
+/* get an exclusive lock on a file */
+u_char
+win32_lock(HANDLE h, DWORD flags)
+{
+	OVERLAPPED olap;
+	int e;
+
+	if (is_nt) {
+		memset(&olap, 0, sizeof(olap));
+		return LockFileEx(h, flags, 0, 1,0, &olap);
+	}
+
+	/* this is ugly, but so is Win95 */
+	for (;;) {
+		if (LockFile(h, 0,0, 1,0))
+			return 1;
+		e = GetLastError();
+		if (e != ERROR_LOCKED
+		    && e != ERROR_LOCK_VIOLATION
+		    && e != ERROR_SHARING_VIOLATION)
+			return 0;
+		Sleep(100);
+	}
+}
+
+
+
+u_char
+win32_unlock(HANDLE h)
+{
+	return UnlockFile(h, 0,0, 1,0);
+}
+
+
+
+/* not all WIN32 systems have snprintf
+ *	At least some versions of FormatMessage() do not understand %f
+ *	There should be no unsafe sprintf's, so worry only a little */
+int
+dcc_vsnprintf(char *tgt, int tgt_len, const char *pat, va_list args)
+{
+	char buf[32*1024];
+	int len;
+
+	len = vsprintf(buf, pat, args);
+	STRLCPY(tgt, buf, tgt_len);
+	return len;
+}
+
+
+
+int
+dcc_snprintf(char *buf, int buf_len, const char *pat, ...)
+{
+	int len;
+	va_list args;
+
+	va_start(args, pat);
+	len = dcc_vsnprintf(buf, buf_len, pat, args);
+	va_end(args);
+	return len;
+}
+
+
+
+/* in NT, this should probably have something to do with the event log */
+
+char syslog_prefix[64];
+
+#pragma argsused
+void
+openlog(const char *ident, int logopt, int facility)
+{
+	BUFCPY(syslog_prefix, ident);
+}
+
+
+
+#pragma argsused
+void
+vsyslog(int priority, const char *msg, va_list args)
+{
+	struct tm tm;
+	char *bp, buf[sizeof(syslog_prefix)+256];
+
+	if (dcc_no_syslog)
+		return;
+
+	strcpy(buf, syslog_prefix);
+	bp = buf+strlen(buf);
+	dcc_localtime(time(0), &tm);
+	bp += strftime(bp, &buf[sizeof(buf)-3]-bp, " %D %H:%M:%S ", &tm);
+	if (bp >= &buf[sizeof(buf)-3])
+		bp = &buf[sizeof(buf)-3];
+
+	bp += vsnprintf(bp, &buf[sizeof(buf)-3]-bp, msg, args);
+	if (bp >= &buf[sizeof(buf)-3])
+		bp = &buf[sizeof(buf)-3];
+	strcpy(bp, "\r\n");
+
+	puts(buf);
+}
+
+
+
+void
+syslog(int priority, const char *msg, ...)
+{
+	va_list args;
+
+	va_start(args, msg);
+	vsyslog(priority, msg, args);
+	va_end(args);
+}
+
+
+
+void
+closelog(void)
+{
+	fflush(stdout);
+	fflush(stderr);
+}
+
+
+/* Strip any CR or LF and convert the strange, non-ASCII
+ *	garbage from Microsoft messages
+ * Trim the trailing blanks and '.' from Borland messages */
+static void
+ws_strip(const char *begin, char *tgt, char *end)
+{
+	const char *src;
+	char c;
+
+	src = begin;
+	do {
+		if (tgt >= end) {
+			*tgt++ = '\0';
+			break;
+		}
+		c = *src++;
+		if (c == '\r')		/* skip carriage return */
+			continue;
+		if (c == '\n')
+			c = ' ';
+		if ((c < ' ' && c != '\0') || c > 0x7e)
+			c = '?';
+		*tgt++ = c;
+	} while (c != '\0');
+
+	/* trim trailing whitespace */
+	--tgt;
+	while (--tgt >= begin
+	       && ((c = *tgt) == ' ' || c == '\t' || c == '.')) {
+		*tgt = '\0';
+	}
+}
+
+
+
+/* complete the lame strerror() from Borland for WIN95 */
+const char *
+ws_strerror(int eno)
+{
+	static struct {
+	    char    s[256];
+	} ebufs[8];
+	static int ebuf_num;
+	int bn;
+	const char *src;
+	char *begin;
+
+	/* Borland fopen() and who knows what else does not set
+	 * the WIN32 GetLastError() value */
+#ifdef __BORLANDC__
+	/* sometimes the Borland wrapper for errno works better */
+	if (eno == 0)
+		eno = *__errno();
+#endif
+	if (eno == 0)
+		return "unknown error";
+
+	/* Use an array of static buffers to kludge around problems with
+	 * threads */
+	bn = ebuf_num;
+	ebuf_num = (bn+1) % DIM(ebufs);
+	begin = ebufs[bn].s;
+
+	/* Use the Microsoft message if it is not silly. */
+	if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+			  0, eno, 0, begin, sizeof(ebufs[bn].s), 0)) {
+
+		/* strip any CR or LF and convert the strange, non-ASCII
+		 * garbage from Microsoft messages */
+		ws_strip(begin, begin, begin+sizeof(ebufs[bn].s)-1);
+		if (strlen(begin) < 128)
+			return begin;
+	}
+
+	/* If Microsoft fails, try the Borland messages,
+	 * and use anything other than "unknown error" */
+	src = strerror(eno);
+	if (strcmp(src, "Unknown error\n")) {
+		ws_strip(src, begin, begin+sizeof(ebufs[bn].s)-1);
+		return begin;
+	}
+
+	/* MicroSoft has only some of the BSD standard error messages */
+	switch (eno) {
+	case WSAEACCES:		return "SO_BROADCAST not enabled";
+	case WSAEADDRINUSE:	return "address already in use";
+	case WSAEADDRNOTAVAIL:	return "address not available";
+	case WSAEAFNOSUPPORT:	return "Address family not supported";
+	case WSAEALREADY:	return "nonblocking connect in progress";
+	case WSAEBADF:		return "Bad file descriptor";
+	case WSAECONNABORTED:	return "Software caused connection abort";
+	case WSAECONNREFUSED:	return "Connection refused";
+	case WSAECONNRESET:	return "Connection reset by peer";
+	case WSAEDESTADDRREQ:	return "Destination address required";
+	case WSAEDQUOT:		return "Disc quota exceeded";
+	case WSAEFAULT:		return "WS bad address";
+	case WSAEHOSTDOWN:	return "Host is down";
+	case WSAEHOSTUNREACH:	return "No route to host";
+	case WSAEINPROGRESS:	return "winsock 1.1 call in progress";
+	case WSAEINTR:		return "cancelled by WSACancelBlockingCall";
+	case WSAEINVAL:		return "WS invalid argument";
+	case WSAEISCONN:	return "Socket is already connected";
+	case WSAELOOP:		return "Too many levels of symbolic links";
+	case WSAEMFILE:		return "Too many open files";
+	case WSAEMSGSIZE:	return "Message too long";
+	case WSAENAMETOOLONG:	return "File name too long";
+	case WSAENETDOWN:	return "network is down";
+	case WSAENETRESET:	return "Network dropped connection on reset";
+	case WSAENETUNREACH:	return "network is unreachable";
+	case WSAENOBUFS:	return "No buffer space available";
+	case WSAENOPROTOOPT:	return "Protocol not available";
+	case WSAENOTCONN:	return "Socket is not connected";
+	case WSAENOTEMPTY:	return "Directory not empty";
+	case WSAENOTSOCK:	return "socket operation on non-socket";
+	case WSAEOPNOTSUPP:	return "Operation not supported";
+	case WSAEPFNOSUPPORT:	return "Protocol family not supported";
+	case WSAEPROCLIM:	return "Too many processes";
+	case WSAEPROTONOSUPPORT:return "Protocol not supported";
+	case WSAEPROTOTYPE:	return "Protocol wrong type for socket";
+	case WSAEREMOTE:	return "Too many levels of remote in path";
+	case WSAESHUTDOWN:	return "Can't send after socket shutdown";
+	case WSAESOCKTNOSUPPORT:return "Socket type not supported";
+	case WSAESTALE:		return "Stale NFS file handle";
+	case WSAETIMEDOUT:	return "Operation timed out";
+	case WSAETOOMANYREFS:	return "Too many references: can't splice";
+	case WSAEUSERS:		return "Too many users";
+	case WSAEWOULDBLOCK:	return "Operation would block";
+	case WSANOTINITIALISED:	return "WSAStartup not done";
+	case WSAHOST_NOT_FOUND:	return "Unknown host";
+	case WSATRY_AGAIN:	return "Host name lookup failure";
+	case WSANO_RECOVERY:	return "Unknown server error";
+	case WSANO_DATA:	return "No address associated with name";
+	}
+
+	/* fall back on Borland's "unkonwn error", but admit the error # */
+	snprintf(begin, sizeof(ebufs[bn].s), "Unknown error %d", eno);
+	return begin;
+}
+#endif /* !DCC_WIN32 */
diff -r 000000000000 -r c7f6b056b673 dcclib/win32.mak
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/win32.mak	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,78 @@
+# Makefile for dcclib for WIN32.
+
+#   This assumes Borland's free command line tools FreeCommandLineTools.exe 
+#   available in 2004 at
+#	http://www.borland.com/products/downloads/download_cbuilder.html
+#   and elsewhere
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.16 $Revision$
+
+!include "../win32.makinc1"
+
+TARGET	=dcclib.lib
+
+# omit daemon.c dccif.c helper.c parse_log_opt.c restart.c vsyslog.c
+#	xfltr_cmn.c xfltr_stubs.c
+# add getopt.c win32.c
+SRCS	=aop.c ask.c ck.c ck2str.c ckbody.c ckfuz1.c ckfuz2.c ckmime.c	\
+	ckparse.c ckwhite.c clnt_init.c clnt_send.c clnt_unthreaded.c	\
+	ipv6_conv.c dnsbl.c error_msg.c					\
+	fnm2path.c get_id.c get_port.c get_secs.c getifaddrs.c		\
+	heap_debug.c hstrerror.c id2str.c inet_ntop.c			\
+	load_ids.c lock_open.c logbad.c md5.c mkstemp.c op2str.c	\
+	parse_srvr_nm.c parse_whitefile.c parse_word.c			\
+	print_info.c sign.c str2type.c str2cnt.c  strlcat.c		\
+	su2str.c tgts2str.c type2str.c getopt.c win32.c xhdr.c
+
+OBJS	=$(SRCS:.c=.obj)
+
+$(TARGET): $(OBJS)
+	-del $@
+!ifndef __NMAKE__
+# Borland
+	TLIB /c $@ @&&|
++$(**: = &^
++)
+|
+!else
+# Microsoft
+	LIB /OUT:$@ @<<
+$**
+<<
+!endif
+
+!include "../win32.makinc2"
diff -r 000000000000 -r c7f6b056b673 dcclib/xhdr.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/xhdr.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,237 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * build and parse headers
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.37 $Revision$
+ */
+
+#include "dcc_clnt.h"
+#include "dcc_xhdr.h"
+
+
+/* add text to the growing X-DCC header line */
+void
+xhdr_add_str(DCC_HEADER_BUF *hdr, const char *p, ...)
+{
+	char *hp;
+	u_int lim, n;
+	va_list args;
+
+	lim = sizeof(hdr->buf) - hdr->used;
+	if (lim <= 0)
+		return;
+	hp = &hdr->buf[hdr->used];
+	if (*(hp-1) == '\n') {
+		*(hp-1) = ' ';
+		++hdr->col;
+	}
+	va_start(args, p);
+	n = vsnprintf(hp, lim, p, args);
+	va_end(args);
+	if (n >= lim-3) {
+		dcc_error_msg("header buffer too small");
+		hdr->buf[lim-1] = '\n';
+		hdr->used = sizeof(hdr->buf);
+		return;
+	}
+	hdr->col += n;
+	hp[n++] = '\n';
+	hp[n] = '\0';
+	hdr->used += n;
+
+	/* follow RFC 2822 and limit lines to 78 */
+	if (hdr->col > DCC_MAX_HDR_LINE	/* if pushed past line end, */
+	    && *(hp-1) == ' '		/* & not the first cksum report, */
+	    && hdr->used < sizeof(hdr->buf)-2) {    /* & have space */
+		memmove(hp+1, hp, n+1);	/* then break the line */
+		*(hp-1) = '\n';
+		*hp = '\t';
+		hdr->col = n+8;
+		++hdr->used;
+	}
+}
+
+
+
+/* generate X-DCC field name */
+int					/* bytes put into buffer */
+get_xhdr_fname(char *xhdr, int xhdr_len, const DCC_CLNT_INFO *info)
+{
+	SRVR_INX srvr_inx;
+	const char *brand;
+	int i;
+
+	if (!info
+	    || !GOOD_SRVR(&info->dcc, srvr_inx = info->dcc.srvr_inx)) {
+		brand = "";
+		i = 0;
+	} else {
+		brand = info->dcc.addrs[srvr_inx].brand;
+		i = xhdr_len-sizeof(DCC_XHDR_START);
+		if (i < 0)
+			i = 0;
+		if (i > ISZ(DCC_BRAND))
+			i = ISZ(DCC_BRAND);
+	}
+
+	i = snprintf(xhdr, xhdr_len, DCC_XHDR_PAT, i, brand);
+	if (i >= xhdr_len)
+		i = xhdr_len-1;
+	return i;
+}
+
+
+
+/* get ready to generate an X-DCC header including generating the
+ * field name */
+void
+xhdr_init(DCC_HEADER_BUF *hdr, DCC_SRVR_ID srvr_id)
+{
+	if (srvr_id < DCC_SRVR_ID_MIN || srvr_id > DCC_SRVR_ID_MAX) {
+		hdr->used = get_xhdr_fname(hdr->buf, sizeof(hdr->buf)-8,
+					   0);
+	} else {
+		hdr->used = get_xhdr_fname(hdr->buf, sizeof(hdr->buf)-8,
+					   dcc_clnt_info);
+	}
+	hdr->col = hdr->used;
+	hdr->start_len = hdr->used;
+
+	xhdr_add_str(hdr, ": %s %d;", dcc_clnt_hostname, srvr_id);
+}
+
+
+
+/* add a checksum and its counts to a growing X-DCC-Warning header line */
+void
+xhdr_add_ck(DCC_HEADER_BUF *hdr,
+	    DCC_CK_TYPES type,		/* which kind of checksum */
+	    DCC_TGTS tgts)
+{
+	char tbuf[30], ckcnt[10];
+
+	xhdr_add_str(hdr, "%s=%s",
+		     dcc_type2str(tbuf, sizeof(tbuf), type, 0, 0, 0),
+		     dcc_tgts2str(ckcnt, sizeof(ckcnt), tgts, 0));
+}
+
+
+
+/* write header with lines ending with either "\n" or "\r\n"
+ *	the buffer must already contain '\n' as needed */
+void
+xhdr_write(LOG_WRITE_FNC fnc, void *wctxt,
+	   const char *hdr, int hdr_len,
+	   u_char crlf)			/* 1=use "\r\n" instead of "\n" */
+{
+	char c;
+	int i;
+
+	if (hdr_len == 0)
+		return;
+
+	i = 0;
+	for (;;) {
+		c = hdr[i];
+		if (c == '\n' && crlf) {
+			if (i != 0)
+				fnc(wctxt, hdr, i);
+			fnc(wctxt, "\r\n", 2);
+			++i;
+			hdr += i;
+			hdr_len -= i;
+			if (hdr_len <= 0)
+				return;
+			i = 0;
+			continue;
+		}
+		if (++i >= hdr_len) {
+			fnc(wctxt, hdr, i);
+			return;
+		}
+	}
+}
+
+
+
+/* create a special X-DCC header for a whitelist mail message
+ *	it lacks a DCC server-ID because there is none and to let
+ *	DCC clients distinguish it from real X-DCC headers */
+void
+xhdr_whitelist(DCC_HEADER_BUF *hdr)
+{
+	hdr->col = 0;
+	hdr->start_len = 0;
+	hdr->used = 0;
+	xhdr_add_str(hdr, DCC_XHDR_START DCC_XHDR_END": %s; "DCC_XHDR_WHITELIST,
+		     dcc_clnt_hostname);
+}
+
+
+
+/* see if an X-DCC header looks like one of our own and so should
+ * be deleted */
+u_char					/* 1=is an X-DCC-...-metrics header */
+is_xhdr(const char *buf,		/* complete header */
+	int buf_len)
+{
+	const char *e;
+
+	if (buf[0] != 'X' && buf[0] != 'x')
+		return 0;
+	if (buf_len <= LITZ(DCC_XHDR_START DCC_XHDR_END":")
+	    || CLITCMP(buf, DCC_XHDR_START))
+		return 0;
+
+	buf += LITZ(DCC_XHDR_START);
+	buf_len -= LITZ(DCC_XHDR_START);
+
+	/* look for the end of header field name */
+	e = memchr(buf, ':', buf_len);
+	if (e) {
+		buf_len = e - buf;
+		if (buf_len < LITZ(DCC_XHDR_END))
+			return 0;
+	}
+
+	buf_len -= LITZ(DCC_XHDR_END);
+	buf += buf_len;
+	if (!CLITCMP(buf, DCC_XHDR_END))
+		return 1;
+
+	return 0;
+}
diff -r 000000000000 -r c7f6b056b673 dccm.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccm.0	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,636 @@
+dccm(8)               Distributed Checksum Clearinghouse               dccm(8)
+
+NNAAMMEE
+     ddccccmm -- Distributed Checksum Clearinghouse Milter Interface
+
+SSYYNNOOPPSSIISS
+     ddccccmm [--VVddbbxxAANNQQ] [--GG _o_n | _o_f_f | _n_o_I_P | _I_P_m_a_s_k_/_x_x] [--hh _h_o_m_e_d_i_r] [--II _u_s_e_r]
+          [--pp _p_r_o_t_o_c_o_l_:_f_i_l_e_n_a_m_e | _p_r_o_t_o_c_o_l_:_p_o_r_t_@_h_o_s_t] [--mm _m_a_p]
+          [--ww _w_h_i_t_e_c_l_n_t] [--UU _u_s_e_r_d_i_r_s] [--aa _I_G_N_O_R_E | _R_E_J_E_C_T | _D_I_S_C_A_R_D]
+          [--tt _t_y_p_e_,[_l_o_g_-_t_h_o_l_d_,]_r_e_j_-_t_h_o_l_d] [--gg [_n_o_t_-]_t_y_p_e] [--SS _h_e_a_d_e_r]
+          [--ll _l_o_g_d_i_r] [--RR _r_u_n_d_i_r] [--rr _r_e_j_e_c_t_i_o_n_-_m_s_g] [--jj _m_a_x_j_o_b_s]
+          [--BB _d_n_s_b_l_-_o_p_t_i_o_n] [--LL _l_t_y_p_e_,_f_a_c_i_l_i_t_y_._l_e_v_e_l]
+
+DDEESSCCRRIIPPTTIIOONN
+     ddccccmm is a daemon built with the sendmail milter interface intended to
+     connect sendmail(8) to DCC servers.  When built with the milter filter
+     machinery and configured to talk to ddccccmm in the _s_e_n_d_m_a_i_l_._c_f file, send-
+     mail passes all email to ddccccmm which in turn reports related checksums to
+     the nearest DCC server.  ddccccmm then adds an _X_-_D_C_C SMTP header line to the
+     message.  Sendmail is told to reject the message if it is unsolicited
+     bulk mail.
+
+     DDccccmm sends reports of checksums related to mail received by DCC clients
+     and queries about the total number of reports of particular checksums.  A
+     DCC server receives _n_o mail, address, headers, or other information, but
+     only cryptographically secure checksums of such information.  A DCC
+     server cannot determine the text or other information that corresponds to
+     the checksums it receives.  Its only acts as a clearinghouse of counts
+     for checksums computed by clients.  For complete privacy as far as the
+     DCC is concerned, the checksums of purely internal mail or other mail
+     that is known to not be unsolicited bulk can be listed in a whitelist to
+     not be reported to the DCC server.
+
+     Since the checksums of messages that are whitelisted locally by the --ww
+     _w_h_i_t_e_c_l_n_t file are not reported to the DCC server, ddccccmm knows nothing
+     about the total recipient counts for their checksums and so cannot add
+     _X_-_D_C_C header lines to such messages.  Sendmail does not tell ddccccmm about
+     messages that are not received by sendmail via SMTP, including messages
+     submitted locally and received via UUCP, and so they also do not receive
+     _X_-_D_C_C header lines.
+
+     Enable the daemon and put its parameters in the _d_c_c___c_o_n_f file and start
+     the daemon with the _/_v_a_r_/_d_c_c_/_l_i_b_e_x_e_c_/_s_t_a_r_t_-_d_c_c_m or _v_a_r_/_d_c_c_/_l_i_b_e_x_e_c_/_r_c_D_C_C
+     script.
+
+     The list of servers that ddccccmm contacts is in the memory mapped file _m_a_p
+     shared by local DCC clients.  The file is  maintained with cdcc(8).
+
+   OOPPTTIIOONNSS
+     The following options are available:
+
+     --VV   displays the version of ddccccmm.
+
+     --dd   enables debugging output from the DCC client software.  Additional
+          --dd options increase the number of messages.  A single --dd
+           aborted SMTP transactions including those from some "dictionary
+          attacks."
+
+     --bb   causes the daemon to not detach itself from the controlling tty and
+          put itself into the background.
+
+     --xx   causes the daemon to try "extra hard" to contact a DCC server.
+          Since it is usually more important to deliver mail than to report
+          its checksums, ddccccmm normally does not delay too long while trying to
+          contact a DCC server.  It will not try again for several seconds
+          after a failure.  With --xx, it will always try to contact the DCC
+          server and it will tell the MTA to answer the DATA command with a
+          4yz temporary failure.
+
+     --AA   adds to existing X-DCC headers in the message instead of replacing
+          existing headers of the brand of the current server.
+
+     --NN   neither adds, deletes, nor replaces existing X-DCC headers in the
+          message.  Each message is logged, rejected, and otherwise handled
+          the same.
+
+     --QQ   only queries the DCC server about the checksums of messages instead
+          of reporting and querying.  This is useful when ddccccmm is used to fil-
+          ter mail that has already been reported to a DCC server by another
+          DCC client.  No single mail message should be reported to a DCC
+          server more than once per recipient, because each report will
+          increase the apparent "bulkness" of the message.
+
+          It is better to use _M_X_D_C_C lines in the global _w_h_i_t_e_c_l_n_t file for
+          your MX mail servers that use DCC than --QQ.
+
+     --GG _o_n | _o_f_f | _n_o_I_P | _I_P_m_a_s_k_/_x_x
+          controls _g_r_e_y_l_i_s_t_i_n_g.  At least one working greylist server must be
+          listed in the _m_a_p file in the DCC home directory.  If more than one
+          is named, they must "flood" or change checksums and they must use
+          the same --GG parameters.  See dccd(8).  Usually all dccm or dccifd
+          DCC client processes use the same --GG parameters.
+
+          _I_P_m_a_s_k_/_x_x and _n_o_I_P remove part or all of the IP address from the
+          greylist triple.  The CIDR block size, _x_x, must be between 1 and
+          128.  96 is added to block sizes smaller than 33 to make them appro-
+          priate for the IPv6 addresses used by the DCC.  _I_P_m_a_s_k_/_9_6 differs
+          from _n_o_I_P for IPv4 addresses, because the former retains the IPv4 to
+          IPv6 mapping prefix.
+
+     --hh _h_o_m_e_d_i_r
+          overrides the default DCC home directory, _/_v_a_r_/_d_c_c.
+
+     --II _u_s_e_r
+          specifies the UID and GID of the process.
+
+     --pp _p_r_o_t_o_c_o_l_:_f_i_l_e_n_a_m_e | _p_r_o_t_o_c_o_l_:_p_o_r_t_@_h_o_s_t
+          specifies the protocol and address by which sendmail will contact
+          ddccccmm.  The default is a UNIX domain socket in the "run" directory,
+          _/_v_a_r_/_r_u_n_/_d_c_c_/_d_c_c_m.  (See also --RR)) This protocol and address must
+          match the value in _s_e_n_d_m_a_i_l_._c_f.  This mechanism can be used to con-
+          nect ddccccmm on one computer to sendmail on another computer when a
+          port and host name or IP address are used.
+
+     --mm _m_a_p
+          specifies a name or path of the memory mapped parameter file instead
+          of the default _m_a_p file in the DCC home directory.  It should be
+          created with the cdcc(8) command.
+
+     --ww _w_h_i_t_e_c_l_n_t
+          specifies an optional file containing filtering parameters as well
+          as SMTP client IP addresses, SMTP envelope values, and header values
+          of mail that is spam or is not spam and does not need a _X_-_D_C_C
+          header, and whose checksums should not be reported to the DCC
+          server.
+
+          If the pathname _w_h_i_t_e_c_l_n_t is not absolute, it is relative to the DCC
+          home directory.
+
+          The format of the ddccccmm whiteclnt file is the same as the _w_h_i_t_e_l_i_s_t
+          files used by dbclean(8) and the _w_h_i_t_e_c_l_n_t file used by dccproc(8).
+          See dcc(8) for a description of DCC white and blacklists.  Because
+          the contents of the _w_h_i_t_e_c_l_n_t file are used frequently, a companion
+          file is automatically created and maintained.  It has the same path-
+          name but with an added suffix of _._d_c_c_w and contains a memory mapped
+          hash table of the main file.
+
+          A whitelist entry ("OK") or two or more semi-whitelistings ("OK2")
+          for one of the message's checksums prevents all of the message's
+          checksums from being reported to the DCC server and the addition of
+          a _X_-_D_C_C header line by ddccccmm A whitelist entry for a checksum also
+          prevents rejecting or discarding the message based on DCC recipient
+          counts as specified by --aa and --tt.  Otherwise, one or more checksums
+          with blacklisting entries ("MANY") cause all of the message's check-
+          sums to be reported to the server with an addressee count of "MANY".
+
+          If the message has a single recipient, an _e_n_v___T_o _w_h_i_t_e_c_l_n_t entry of
+          "OK" for the checksum of its recipient address acts like any other
+          _w_h_i_t_e_c_l_n_t entry of "OK."  When the SMTP message has more than one
+          recipient, the effects can be complicated.  When a message has sev-
+          eral recipients with some but not all listed in the _w_h_i_t_e_c_l_n_t file,
+          ddccccmm tries comply with the wishes of the users who want filtering as
+          well as those who don't by silently not delivering the message to
+          those who want filtering (i.e. are not whitelisted) and delivering
+          the message to don't want filtering.
+
+     --UU _u_s_e_r_d_i_r_s
+          enables per-user _w_h_i_t_e_c_l_n_t files and log directories.  Each target
+          of a message can have a directory of log files named
+          _u_s_e_d_i_r_s_/_$_{_d_c_c___u_s_e_r_d_i_r_}_/_l_o_g where _$_{_d_c_c___u_s_e_r_d_i_r_} is the _s_e_n_d_m_a_i_l_._c_f
+          macro described below.  If _$_{_d_c_c___u_s_e_r_d_i_r_} is not set,
+          _u_s_e_r_d_i_r_s_/_$_{_r_c_p_t___m_a_i_l_e_r_}_/_$_{_r_c_p_t___a_d_d_r_}_/_l_o_g is used.  The most likely
+          value of _m_a_i_l_e_r is _l_o_c_a_l.  Appropriate values for both
+          _$_{_r_c_p_t___m_a_i_l_e_r_} and _$_{_r_c_p_t___a_d_d_r_} can be seen by examining _e_n_v___T_o
+          lines in --ll _l_o_g_d_i_r files.  If it is not absolute, _u_s_e_r_d_i_r_s is rela-
+          tive to the DCC home directory.  The directory containing the log
+          files must be named _l_o_g and it must be writable by the ddccccmm process.
+          Each log directory must exist or logging for the corresponding is
+          silently disabled.  The files created in the log directory are owned
+          by the UID of the ddccccmm process, but they have _g_r_o_u_p and _o_t_h_e_r read
+          and write permissions copied from the corresponding _l_o_g directory.
+          To ensure the privacy of mail, it may be good to make the directo-
+          ries readable only by _o_w_n_e_r and _g_r_o_u_p, and to use a cron script that
+          changes the owner of each file to match the grandparent _a_d_d_r direc-
+          tory.
+
+          There can also be a per -user whitelist file named
+          _u_s_e_r_d_i_r_s_/_$_{_d_c_c___u_s_e_r_d_i_r_}_/_w_h_i_t_e_c_l_n_t or if _$_{_d_c_c___u_s_e_r_d_i_r_} is not set,
+          _u_s_e_r_d_i_r_s_/_$_{_r_c_p_t___m_a_i_l_e_r_}_/_$_{_r_c_p_t___a_d_d_r_} per-user whitelist files.  Any
+          checksum that is not white- or blacklisted by an individual
+          addressee's _w_h_i_t_e_c_l_n_t file  is checked in the main --ww --wwhhiitteeccllnntt
+          file.  A missing per-addressee _w_h_i_t_e_c_l_n_t file is the same as an
+          empty file.  Relative paths for files included in per-addressee
+          files are resolved in the DCC home directory.  The _w_h_i_t_e_c_l_n_t files
+          and the _a_d_d_r directories containing them must be writable by the
+          ddccccmm process.
+
+          _O_p_t_i_o_n lines in per-user whiteclnt files can be used to modify many
+          aspects of ddccccmm filtering, as described in the main dcc man page.
+          For example, an _o_p_t_i_o_n _d_c_c_-_o_f_f line turns off DCC filtering for
+          individual mailboxes.
+
+     --aa _I_G_N_O_R_E | _R_E_J_E_C_T | _D_I_S_C_A_R_D
+          specifies the action taken when DCC server counts or --tt thresholds
+          say that a message is unsolicited and bulk.  _I_G_N_O_R_E causes the mes-
+          sage to be unaffected except for adding the _X_-_D_C_C header line to the
+          message.  This turns off DCC filtering.
+
+          Spam can also be _R_E_J_E_C_Ted or accepted and silently _D_I_S_C_A_R_Ded without
+          being delivered to local mailboxes.  The default is _R_E_J_E_C_T.
+
+          Mail forwarded via IP addresses marked _M_X or _M_X_D_C_C in the main
+          _w_h_i_t_e_c_l_n_t file is treated as if --aa _D_I_S_C_A_R_D were specified.  This
+          prevents "bouncing" spam.
+
+          Determinations that mail is or is not spam from sendmail via
+          _$_{_d_c_c___i_s_s_p_a_m_} or _$_{_d_c_c___n_o_t_s_p_a_m_} macros override --aa.  The effects of
+          the --ww _w_h_i_t_e_c_l_n_t are not affected by --aa.
+
+     --tt _t_y_p_e_,[_l_o_g_-_t_h_o_l_d_,]_r_e_j_-_t_h_o_l_d
+          sets logging and "spam" thresholds for checksum _t_y_p_e.  The checksum
+          types are _I_P, _e_n_v___F_r_o_m, _F_r_o_m, _M_e_s_s_a_g_e_-_I_D, _s_u_b_s_t_i_t_u_t_e, _R_e_c_e_i_v_e_d,
+          _B_o_d_y, _F_u_z_1, _F_u_z_2, _r_e_p_-_t_o_t_a_l, and _r_e_p.  The first six, _I_P through
+          _s_u_b_s_t_i_t_u_t_e, have no effect except when a local DCC server configured
+          with --KK is used.  The _s_u_b_s_t_i_t_u_t_e thresholds apply to the first sub-
+          stitute heading encountered in the mail message.  The string _A_L_L
+          sets thresholds for all types, but is unlikely to be useful except
+          for setting logging thresholds.  The string _C_M_N specifies the com-
+          monly used checksums _B_o_d_y, _F_u_z_1, and _F_u_z_2.  _R_e_j_-_t_h_o_l_d and _l_o_g_-_t_h_o_l_d
+          must be numbers, the string _N_E_V_E_R, or the string _M_A_N_Y indicating
+          millions of targets.  Counts from the DCC server as large as the
+          threshold for any single type are taken as sufficient evidence that
+          the message should be logged or rejected.
+
+          _L_o_g_-_t_h_o_l_d is the threshold at which messages are logged.  It can be
+          handy to log messages at a lower threshold to find solicited bulk
+          mail sources such as mailing lists.  If no logging threshold is set,
+          only rejected mail and messages with complicated combinations of
+          white and blacklisting are logged.  Messages that reach at least one
+          of their rejection thresholds are logged regardless of logging
+          thresholds.
+
+          _R_e_j_-_t_h_o_l_d is the threshold at which messages are considered "bulk,"
+          and so should be rejected or discarded if not whitelisted.
+
+          DCC Reputation thresholds in the commercial version of the DCC are
+          controlled by thresholds on checksum types _r_e_p and _r_e_p_-_t_o_t_a_l.  Mes-
+          sages from an IP address that the DCC database says has sent more
+          than --tt _r_e_p_-_t_o_t_a_l_,_l_o_g_-_t_h_o_l_d messages are logged.  A DCC Reputation
+          is computed for messages received from IP addresses that have sent
+          more than --tt _r_e_p_-_t_o_t_a_l_,_l_o_g_-_t_h_o_l_d messages.  The DCC Reputation of an
+          IP address is the percentage of its messages that have been detected
+          as bulk or having at least 10 recipients.  The defaults are equiva-
+          lent to --tt _r_e_p_,_n_e_v_e_r and --tt _r_e_p_-_t_o_t_a_l_,_n_e_v_e_r_,_2_0.
+
+          Bad DCC Reputations do not reject mail unless enabled by an _o_p_t_i_o_n
+          _D_C_C_-_r_e_p_-_o_n line in a _w_h_i_t_e_c_l_n_t file.
+
+          The checksums of locally whitelisted messages are not checked with
+          the DCC server and so only the number of targets of the current copy
+          of a whitelisted message are compared against the thresholds.
+
+          The default is _A_L_L_,_N_E_V_E_R, so that nothing is discarded, rejected, or
+          logged.  A common choice is _C_M_N_,_2_5_,_5_0 to reject or discard mail with
+          common bodies except as overridden by the whitelist of the DCC
+          server, the sendmail _$_{_d_c_c___i_s_s_p_a_m_} and _$_{_d_c_c___n_o_t_s_p_a_m_} macros, and
+          --gg, and --ww.
+
+     --gg [_n_o_t_-]_t_y_p_e
+          indicates that whitelisted, _O_K or _O_K_2, counts from the DCC server
+          for a type of checksum are to be believed.  They should be ignored
+          if prefixed with _n_o_t_-.  _T_y_p_e is one of the same set of strings as
+          for --tt.  Only _I_P, _e_n_v___F_r_o_m, and _F_r_o_m are likely choices.  By default
+          all three are honored, and hence the need for _n_o_t_-.
+
+     --SS _h_d_r
+          adds to the list of substitute or locally chosen headers that are
+          checked with the --ww _w_h_i_t_e_c_l_n_t file and sent to the DCC server.  The
+          checksum of the last header of type _h_d_r found in the message is
+          checked.  _H_d_r can be _H_E_L_O to specify the SMTP envelope HELO value.
+          _H_d_r can also be _m_a_i_l___h_o_s_t to specify the sendmail "resolved" host
+          name from the Mail_from value in the SMTP envelope.  As many as six
+          different substitute headers can be specified, but only the checksum
+          of the first of the six will be sent to the DCC server.
+
+     --ll _l_o_g_d_i_r
+          specifies a directory in which files containing copies of messages
+          processed by ddccccmm are kept.  They can be copied to per-user directo-
+          ries specified with --UU.  Information about other recipients of a
+          message is deleted from the per-user copies.
+
+          See the FILES section below concerning the contents of the files.
+          See also the _o_p_t_i_o_n _l_o_g_-_s_u_b_d_i_r_e_c_t_o_r_y_-_{_d_a_y_,_h_o_u_r_,_m_i_n_u_t_e_} lines in
+          _w_h_i_t_e_c_l_n_t files described in dcc(8).
+
+          The directory is relative to the DCC home directory if it is not
+          absolute
+
+     --RR _r_u_n_d_i_r
+          specifies the "run" directory where the UNIX domain socket and file
+          containing the daemon's process ID are stored.  The default value is
+          /var/run/dcc .
+
+     --rr _r_e_j_e_c_t_i_o_n_-_m_s_g
+          specifies the rejection message in --oo proxy mode for unsolicited
+          bulk mail or for mail temporarily blocked by _g_r_e_y_l_i_s_t_i_n_g when --GG is
+          specified.  The first --rr _r_e_j_e_c_t_i_o_n_-_m_s_g replaces the default bulk
+          mail rejection message, "5.7.1 550 mail %ID from %CIP rejected by
+          DCC".  The second replaces "4.2.1 452 mail %ID from %CIP temporary
+          greylist embargoed".  The third --rr _r_e_j_e_c_t_i_o_n_-_m_s_g replaces the
+          default SMTP rejection message "5.7.1 550 %ID bad reputation; see
+          http://commercial-dcc.rhyolite.com/cgi-bin/reps.cgi?tgt=%CIP" for
+          mail with bad DCC Reputations.  If _r_e_j_e_c_t_i_o_n_-_m_s_g is the zero-length
+          string, the --rr setting is counted but the corresponding message is
+          not changed.
+
+          _R_e_j_e_c_t_i_o_n_-_m_s_g can contain specific information about the mail mes-
+          sage.  The following strings starting with % are replaced with the
+          corresponding values:
+              %ID       message ID such as the unique part of log file name or
+                        sendmail queue ID
+              %CIP      SMTP client IP address
+              %BTYPE    type of DNS blacklist hit, such as "SMTP client",
+                        "mail_host", or "URL NS"
+              %BTGT     IP address or name declared bad by DNS blacklist
+              %BPROBE   domain name found in DNS blacklist such as
+                        4.3.2.10.example.com
+              %BRESULT  value of the %BPROBE domain name found in DNS black-
+                        list
+
+          A common alternate for the bulk mail rejection message is "4.7.1 451
+          Access denied by DCC" to tell the sending mail system to continue
+          trying.  Use a 4yz response with caution, because it is likely to
+          delay for days a delivery failure message for false positives.  If
+          the rejection message does not start with an RFC 1893 status code
+          and RFC 2821 reply code, 5.7.1 and 550 or 4.2.1 and 452 are used.
+
+          See also --BB _s_e_t_:_r_e_j_-_m_s_g_=_r_e_j_e_c_t_i_o_n_-_m_s_g to set the status message for
+          mail rejected by DNS blacklists.
+
+     --jj _m_a_x_j_o_b_s
+          limits the number of simultaneous requests that will be processed.
+          The default value is the maximum number that seems to be possible
+          given system limits on open files, select() bit masks, and so forth.
+          Start ddccccmm with --dd and see the starting message in the system log to
+          see the limit.
+
+     --BB _d_n_s_b_l_-_o_p_t_i_o_n
+          enables DNS blacklist checks of the SMTP client IP address, SMTP
+          envelope Mail_From sender domain name, and of host names in URLs in
+          the message body.  Body URL blacklisting has too many false posi-
+          tives to use on abuse mailboxes.  It is less effective than
+          greylisting with dccm(8) or dccifd(8) but can be useful in situa-
+          tions where greylisting cannot be used.
+
+          _D_n_s_b_l_-_o_p_t_i_o_n is either one of the --BB _s_e_t_:_o_p_t_i_o_n forms or
+              --BB _d_o_m_a_i_n[_,_I_P_a_d_d_r[_/_x_x[_,_b_l_t_y_p_e]]]
+          _D_o_m_a_i_n is a DNS blacklist domain such as example.com that will be
+          searched.  _I_P_a_d_d_r[_/_x_x_x] is the string "any" an IP address in the DNS
+          blacklist that indicates that the mail message should be rejected,
+          or a CIDR block covering results from the DNS blacklist.
+          "127.0.0.2" is assumed if _I_P_a_d_d_r is absent.  IPv6 addresses can be
+          specified with the usual colon (:) notation.  Names can be used
+          instead of numeric addresses.  The type of DNS blacklist is speci-
+          fied by _b_l_t_y_p_e as _n_a_m_e, _I_P_v_4, or _I_P_v_6.  Given an envelope sender
+          domain name or a domain name in a URL of spam.domain.org and a
+          blacklist of type _n_a_m_e, spam.domain.org.example.com will be tried.
+          Blacklist types of _I_P_v_4 and _I_P_v_6 require that the domain name in a
+          URL sender address be resolved into an IPv4 or IPv6 address.  The
+          address is then written as a reversed string of decimal octets to
+          check the DNS blacklist, as in 2.0.0.127.example.com,
+
+          More than one blacklist can be specified and blacklists can be
+          grouped.  All searching within a group is stopped at the first posi-
+          tive result.
+
+          Positive results are ignored after being logged unless an
+          _o_p_t_i_o_n _D_N_S_B_L_-_o_n line appears in the global or per-user _w_h_i_t_e_c_l_n_t
+          file.
+
+          --BB _s_e_t_:_n_o_-_c_l_i_e_n_t
+               says that SMTP client IP addresses and reverse DNS domain names
+               should not be checked in the following blacklists.
+               --BB _s_e_t_:_c_l_i_e_n_t restores the default for the following black-
+               lists.
+
+          --BB _s_e_t_:_n_o_-_m_a_i_l___h_o_s_t
+               says that SMTP envelope Mail_From sender domain names should
+               not be checked in the following blacklists.  --BB _s_e_t_:_m_a_i_l___h_o_s_t
+               restores the default.
+
+          --BB _s_e_t_:_n_o_-_U_R_L
+               says that URLs in the message body should not be checked in the
+               in the following blacklists.  --BB _s_e_t_:_U_R_L restores the default.
+
+          --BB _s_e_t_:_n_o_-_M_X
+               says MX servers of sender Mail_From domain names and host names
+               in URLs should not be checked in the following blacklists.
+               --BB _s_e_t_:_M_X restores the default.
+
+          --BB _s_e_t_:_n_o_-_N_S
+               says DNS servers of sender Mail_From domain names and host
+               names in URLs should not be checked in the following black-
+               lists.  --BB _s_e_t_:_N_S restores the default.
+
+          --BB _s_e_t_:_d_e_f_a_u_l_t_s
+               is equivalent to all of --BB _s_e_t_:_n_o_-_t_e_m_p_-_f_a_i_l --BB _s_e_t_:_c_l_i_e_n_t
+               --BB _s_e_t_:_m_a_i_l___h_o_s_t --BB _s_e_t_:_U_R_L --BB _s_e_t_:_M_X and --BB _s_e_t_:_N_S
+
+          --BB _s_e_t_:_g_r_o_u_p_=_X
+               adds later DNS blacklists specified with
+                   --BB _d_o_m_a_i_n[_,_I_P_a_d_d_r[_/_x_x[_,_b_l_t_y_p_e]]]
+               to group 1, 2, or 3.
+
+          --BB _s_e_t_:_d_e_b_u_g_=_X
+               sets the DNS blacklist logging level
+
+          --BB _s_e_t_:_m_s_g_-_s_e_c_s_=_S
+               limits ddccccmm to _S seconds total for checking all DNS blacklists.
+               The default is 25.
+
+          --BB _s_e_t_:_U_R_L_-_s_e_c_s_=_S
+               limits ddccccmm to at most _S seconds resolving and checking any
+               single URL.  The default is 11.  Some spam contains dozens of
+               URLs and that some "spamvertised" URLs contain host names that
+               need minutes to resolve.  Busy mail systems cannot afford to
+               spend minutes checking each incoming mail message.
+
+          --BB _s_e_t_:_r_e_j_-_m_s_g_=_r_e_j_e_c_t_i_o_n_-_m_s_g
+               sets the SMTP rejection message for the following blacklists.
+               _R_e_j_e_c_t_i_o_n_-_m_s_g must be in the same format as for --rr.  If
+               _r_e_j_e_c_t_i_o_n_-_m_s_g is null, the default is restored.  The default
+               DNS blacklist rejection message is the first message set with
+               --rr.
+
+          --BB _s_e_t_:_t_e_m_p_-_f_a_i_l
+               causes ddccccmm to the MTA to answer the SMTP DATA command with
+                  452 4.2.1 mail %ID from %CIP temporary delayed for DNSBL
+               if any DNS answer required for a DNSBL in the current group
+               times out, including resolving names in URLs.
+
+          --BB _s_e_t_:_n_o_-_t_e_m_p_-_f_a_i_l
+               restores the default of assuming a negative answer for DNS
+               responses that take too long.
+
+          --BB _s_e_t_:_m_a_x_j_o_b_s_=_X
+               sets maximum number of helper processes to _X.  In order to use
+               typical single-threaded DNS resolver libraries, ddccccmm uses
+               fleets of helper processes.  It is rarely a good idea to change
+               the default, which is the same as the maximum number of simul-
+               taneous jobs set with --jj.
+
+          --BB _s_e_t_:_p_r_o_g_p_a_t_h_=_/_v_a_r_/_d_c_c_/_l_i_b_e_x_e_c_/_d_n_s_-_h_e_l_p_e_r
+               changes the path to the helper program.
+
+     --LL _l_t_y_p_e_,_f_a_c_i_l_i_t_y_._l_e_v_e_l
+          specifies how messages should be logged.  _L_t_y_p_e must be _e_r_r_o_r, _i_n_f_o,
+          or _o_f_f to indicate which of the two types of messages are being con-
+          trolled or to turn off all syslog(3) messages from ddccccmm.  _L_e_v_e_l must
+          be a syslog(3) level among _E_M_E_R_G, _A_L_E_R_T, _C_R_I_T, _E_R_R, _W_A_R_N_I_N_G, _N_O_T_I_C_E,
+          _I_N_F_O, and _D_E_B_U_G.  _F_a_c_i_l_i_t_y must be among _A_U_T_H, _A_U_T_H_P_R_I_V, _C_R_O_N,
+          _D_A_E_M_O_N, _F_T_P, _K_E_R_N, _L_P_R, _M_A_I_L, _N_E_W_S, _U_S_E_R, _U_U_C_P, and _L_O_C_A_L_0 through
+          _L_O_C_A_L_7.  The default is equivalent to
+                --LL _i_n_f_o_,_M_A_I_L_._N_O_T_I_C_E --LL _e_r_r_o_r_,_M_A_I_L_._E_R_R
+
+     ddccccmm normally sends counts of mail rejected and so forth the to system
+     log at midnight.  The SIGUSR1 signal sends an immediate report to the
+     system log.  They will be repeated every 24 hours instead of at midnight.
+
+SSEENNDDMMAAIILL MMAACCRROOSS
+     Sendmail can affect ddccccmm with the values of some _s_e_n_d_m_a_i_l_._c_f macros.
+     These macro names must be added to the Milter.macros option statements in
+     _s_e_n_d_m_a_i_l_._c_f as in the example "Feature" file dcc.m4.
+
+     _$_{_d_c_c___i_s_s_p_a_m_}  causes a mail message to be reported to the DCC server as
+                    having been addressed to "MANY" recipients.  The
+                    _$_{_d_c_c___i_s_s_p_a_m_} macro is ignored if the _$_{_d_c_c___n_o_t_s_p_a_m_} macro
+                    is set to a non-null string
+
+                    If the value of the _$_{_d_c_c___i_s_s_p_a_m_} is null, ddccccmm uses SMTP
+                    rejection messages controlled by --aa and --rr.  If the value
+                    of the _$_{_d_c_c___i_s_s_p_a_m_} macro starts with "DISCARD", the mail
+                    message is silently discarded as with --aa _D_I_S_C_A_R_D_. If value
+                    of the macro not null and does not start with "DISCARD",
+                    it is used as the SMTP error message given to the SMTP
+                    client trying to send the rejected message.  The message
+                    starts with an optional SMTP error type and number fol-
+                    lowed by text.
+
+                    The --aa option does not effect messages marked spam with
+                    _$_{_d_c_c___i_s_s_p_a_m_}.  When the _$_{_d_c_c___i_s_s_p_a_m_} macro is set, the
+                    message is rejected or discarded despite local or DCC
+                    database whitelist entries.  The local whitelist does con-
+                    trol whether the message's checksums will be reported to
+                    the DCC server and an _X_-_D_C_C SMTP header line will be
+                    added.
+
+     _$_{_d_c_c___n_o_t_s_p_a_m_}
+                    causes a message not be considered unsolicited bulk
+                    despite evidence to the contrary.  It also prevents ddccccmm
+                    from reporting the checksums of the message to the DCC
+                    server and from adding an _X_-_D_C_C header line.
+
+                    When the macro is set by the _s_e_n_d_m_a_i_l_._c_f rules,
+                    _$_{_d_c_c___n_o_t_s_p_a_m_} macros overrides DCC threshlds that say the
+                    message should be rejected as well as the effects of the
+                    _$_{_d_c_c___i_s_s_p_a_m_} macro.
+
+     _$_{_d_c_c___m_a_i_l___h_o_s_t_}
+                    specifies the name of the SMTP client that is sending the
+                    message.  This macro is usually the same as the _m_a_i_l___h_o_s_t
+                    macro.  They can differ when a sendmail "smart relay" is
+                    involved.  The _$_{_d_c_c___m_a_i_l___h_o_s_t_} macro does not work if
+                    _F_E_A_T_U_R_E_(_d_e_l_a_y___c_h_e_c_k_s_) is used.
+
+     _$_{_d_c_c___u_s_e_r_d_i_r_}
+                    is the per-user whitelist and log directory for a recipi-
+                    ent.  If the macro is not set in sendmail.cf,
+                    $&{rcpt_mailer}/$&{rcpt_addr} is assumed, but with the
+                    recipient address converted to lower case.  Whatever value
+                    is used, the directory name after the last slash (/) char-
+                    acter is converted to lower case.  Any value containing
+                    the string "/../" is ignored.
+
+                    This macro also does not work if _F_E_A_T_U_R_E_(_d_e_l_a_y___c_h_e_c_k_s_) is
+                    used.
+
+                    The following two lines in a sendmail mc file have the
+                    same effect as not defining the ${dcc_userdir} macro, pro-
+                    vided _F_E_A_T_U_R_E_(_d_c_c_) is also used and the sendmail
+                    _c_f_/_f_e_a_t_u_r_e directory has a symbolic link to the
+                    _m_i_s_c_/_d_c_c_._m_4 file.
+
+     SLocal_check_rcpt
+     R$*     $: $1 $(macro {dcc_userdir} $@ $&{rcpt_mailer}/$&{rcpt_addr} $))
+
+FFIILLEESS
+     /var/dcc   is the DCC home directory in which other files are found.
+     /var/dcc/libexec/start-dccm
+                is a script used to ddccccmm.
+     dcc/dcc_conf
+                contains parameters used by the scripts to start DCC daemons
+                and cron jobs.
+     logdir     is an optional directory specified with --ll and containing
+                marked mail.  Each file in the directory contains one message,
+                at least one of whose checksums reached its --tt thresholds or
+                that is interesting for some other reason.  Each file starts
+                with lines containing the date when the message was received,
+                the IP address of the SMTP client, and SMTP envelope values.
+                Those lines are followed by the body of the SMTP message
+                including its header as it was received by sendmail and with-
+                out any new or changed header lines.  Only approximately the
+                first 32 KBytes of the body are recorded unless modified by
+                _._/_c_o_n_f_i_g_u_r_e _-_-_w_i_t_h_-_m_a_x_-_l_o_g_-_s_i_z_e_=_x_x The checksums for the mes-
+                sage follow the body.  They are followed by lines indicating
+                that the _$_{_d_c_c___i_s_s_p_a_m_} or _$_{_d_c_c___n_o_t_s_p_a_m_} _s_e_n_d_m_a_i_l_._c_f macros
+                were set or one of the checksums is white- or blacklisted by
+                the --ww _w_h_i_t_e_c_l_n_t file.  Each file ends with the _X_-_D_C_C header
+                line added to the message and the disposition of the message
+                including SMTP status message if appropriate.
+     map        is the memory mapped file of information concerning DCC
+                servers in the DCC home directory.
+     whiteclnt  contains the client whitelist in the format described in
+                dcc(8).
+     whiteclnt.dccw
+                is a memory mapped hash table of the _w_h_i_t_e_c_l_n_t file.
+     dccm.pid   in the --RR _r_u_n_d_i_r directory contains daemon's process ID.  The
+                string ``dccm'' is replaced by the file name containing the
+                daemon to facilitate running multiple daemons, probably con-
+                nected to remote instances of sendmail using TCP/IP instead of
+                a UNIX domain socket.  See also --RR.
+     /var/run/dcc/dccm
+                is the default UNIX domain socket used by the sendmail milter
+                interface.  See also --RR.
+     sendmail.cf
+                is the sendmail(8) control file.
+     misc/dcc.m4
+                sendmail mc file that should have a symbolic link in the send-
+                mail cf/feature directory so that _F_E_A_T_U_R_E_(_d_c_c_) can be used in
+                a sendmail mc file.
+
+EEXXAAMMPPLLEESS
+     DDccccmm should be started before sendmail with something like the script
+     _/_v_a_r_/_d_c_c_/_l_i_b_e_x_e_c_/_s_t_a_r_t_-_d_c_c_m_. It looks for common DCC parameters in the
+     _d_c_c___c_o_n_f file in the DCC home directory, _/_v_a_r_/_d_c_c_.
+
+     Those numbers should modified to fit local conditions.  It might be wise
+     to replace the "100" numbers with much larger values or with "MANY" until
+     a few weeks of monitoring the log directory show that sources of mailing
+     lists are in the server's whitelist file (see dccd(8)) or the local
+     _w_h_i_t_e_c_l_n_t file.
+
+     It is usually necessary to regularly delete old log files with a script
+     like /var/dcc/libexec/cron-dccd.
+
+     On systems unlike modern FreeBSD and other UNIX-like systems which
+     include sendmail milter support, sendmail must be built with the milter
+     interface, such as by creating a _d_e_v_t_o_o_l_s_/_S_i_t_e_/_s_i_t_e_._c_o_n_f_i_g_._m_4 or similar
+     file containing something like the following lines:
+
+           APPENDDEF(`conf_sendmail_ENVDEF', `-D_FFR_MILTER=1')
+           APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_MILTER=1')
+
+     Appropriate lines invoking the milter interface must be added to
+     _s_e_n_d_m_a_i_l_._c_f_. That can be done by putting a symbolic link to the the
+     misc/dcc.m4 file in the DCC source to the sendmail cf/feature directory
+     and adding the line
+
+           FEATURE(dcc)
+
+     to the local .mc file.
+
+     Note that ddccccmm should not be used with the Postfix milter mechanism.
+     Instead use dccifd(8) as a before-queue filter as described in that man
+     page.
+
+SSEEEE AALLSSOO
+     cdcc(8), dbclean(8), dcc(8), dccd(8), dblist(8), dccifd(8), dccproc(8),
+     dccsight(8), sendmail(8).
+
+HHIISSTTOORRYY
+     Distributed Checksum Clearinghouses are based on an idea of Paul Vixie.
+     Implementation of ddccccmm was started at Rhyolite Software in 2000.  This
+     document describes version 1.3.103.
+
+BBUUGGSS
+     ddccccmm uses --tt where dccproc(8) uses --cc.
+
+     Systems without setrlimit(2) and getrlimit(2) RLIMIT_NOFILE can have
+     problems with the default limit on the number of simultaneous jobs, the
+     value of --jj.  Every job requires four open files.  These problems are
+     usually seen with errors messages that say something like
+           dccm[24448]: DCC: accept() returned invalid socket
+     A fix is to use a smaller value for --jj or to allow ddccccmm to open more
+     files.  Sendmail version 8.13 and later can be told to poll() instead of
+     select with SM_CONF_POLL.  Some older versions of sendmail knew about
+     FFR_USE_POLL.  One of the following lines in your devtools/Site/site.con-
+     fig.m4 file can help:
+
+           APPENDDEF(`conf_libmilter_ENVDEF', `-DSM_CONF_POLL')
+           APPENDDEF(`conf_libmilter_ENVDEF', `-DFFR_USE_POLL')
+
+     On many systems with sendmail 8.11.3 and preceding, a bug in the sendmail
+     milter mechanism causes ddccccmm to die with a core file when given a signal.
+
+                               February 26, 2009
diff -r 000000000000 -r c7f6b056b673 dccm.8.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccm.8.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,1239 @@
+.\" Copyright (c) 2008 by Rhyolite Software, LLC
+.\"
+.\" This agreement is not applicable to any entity which sells anti-spam
+.\" solutions to others or provides an anti-spam solution as part of a
+.\" security solution sold to other entities, or to a private network
+.\" which employs the DCC or uses data provided by operation of the DCC
+.\" but does not provide corresponding data to other users.
+.\"
+.\" Permission to use, copy, modify, and distribute this software without
+.\" changes for any purpose with or without fee is hereby granted, provided
+.\" that the above copyright notice and this permission notice appear in all
+.\" copies and any distributed versions or copies are either unchanged
+.\" or not called anything similar to "DCC" or "Distributed Checksum
+.\" Clearinghouse".
+.\"
+.\" Parties not eligible to receive a license under this agreement can
+.\" obtain a commercial license to use DCC by contacting Rhyolite Software
+.\" at sales@rhyolite.com.
+.\"
+.\" A commercial license would be for Distributed Checksum and Reputation
+.\" Clearinghouse software.  That software includes additional features.  This
+.\" free license for Distributed ChecksumClearinghouse Software does not in any
+.\" way grant permision to use Distributed Checksum and Reputation Clearinghouse
+.\" software
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+.\" 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.
+.\"
+.\" Rhyolite Software DCC 1.3.103-1.168 $Revision$
+.\"
+.Dd February 26, 2009
+.ds volume-ds-DCC Distributed Checksum Clearinghouse
+.Dt dccm 8 DCC
+.Os " "
+.Sh NAME
+.Nm dccm
+.Nd Distributed Checksum Clearinghouse Milter Interface
+.Sh SYNOPSIS
+.Bk -words
+.Nm
+.Op Fl VdbxANQ
+.Op Fl G Ar on | off | noIP | IPmask/xx
+.Op Fl h Ar homedir
+.Op Fl I Ar user
+.br
+.Op Fl p Ar protocol:filename | protocol:port@host
+.Op Fl m Ar map
+.br
+.Op Fl w Ar whiteclnt
+.Op Fl U Ar userdirs
+.Op Fl a Ar IGNORE | REJECT | DISCARD
+.br
+.Oo
+.Fl t Xo
+.Sm off
+.Ar type,
+.Op Ar log-thold,
+.Ar rej-thold
+.Sm on
+.Xc
+.Oc
+.Oo
+.Fl g Xo
+.Sm off
+.Op Ar not-
+.Ar type
+.Sm on
+.Xc
+.Oc
+.Op Fl S Ar header
+.br
+.Op Fl l Ar logdir
+.Op Fl R Ar rundir
+.Op Fl r Ar rejection-msg
+.Op Fl j Ar maxjobs
+.Op Fl B Ar dnsbl-option
+.Op Fl L Ar ltype,facility.level
+.Ek
+.Sh DESCRIPTION
+.Nm
+is a daemon built with the sendmail milter interface intended to connect
+.Xr sendmail 8
+to DCC servers.
+When built with the milter filter machinery and configured to talk to
+.Nm
+in the
+.Pa sendmail.cf
+file,
+sendmail passes all email to
+.Nm
+which in turn reports related checksums to the nearest DCC server.
+.Nm
+then adds an
+.Em X-DCC
+SMTP header line to the message.
+Sendmail is told to reject the message if it is unsolicited bulk mail.
+.Pp
+.Nm Dccm
+sends reports of checksums related to mail received by DCC clients
+and queries about the total number of reports of particular checksums.
+A DCC server receives
+.Em no
+mail, address, headers, or other information,
+but only cryptographically secure checksums of such information.
+A DCC server cannot determine the text or other information that corresponds
+to the checksums it receives.
+Its only acts as a clearinghouse of counts for checksums computed by clients.
+For complete privacy as far as the DCC is concerned,
+the checksums of purely internal mail or other
+mail that is known to not be unsolicited bulk can be listed in a whitelist
+to not be reported to the DCC server.
+.Pp
+Since the checksums of messages that are whitelisted locally
+by the
+.Fl w Ar whiteclnt
+file are not reported to the DCC server,
+.Nm
+knows nothing about the total recipient counts for their checksums and
+so cannot add
+.Em X-DCC
+header lines to such messages.
+Sendmail does not tell
+.Nm
+about messages that are not received by sendmail via SMTP, including messages
+submitted locally and received via UUCP, and so they also do not receive
+.Em X-DCC
+header lines.
+.Pp
+Enable the daemon and put its parameters in the
+.Pa dcc_conf
+file and start the daemon with the
+.Pa @libexecdir@/start-dccm
+or
+.Pa var/dcc/libexec/rcDCC
+script.
+.Pp
+The list of servers that
+.Nm
+contacts is in the memory mapped file
+.Pa map
+shared by local DCC clients.
+The file is  maintained with
+.Xr cdcc 8 .
+.Ss OPTIONS
+The following options are available:
+.Bl -tag -width 3n
+.It Fl V
+displays the version of
+.Nm .
+.It Fl d
+enables debugging output from the DCC client software.
+Additional
+.Fl d
+options increase the number of messages.
+A single
+.Fl d
+ aborted SMTP transactions including those from some "dictionary attacks."
+.It Fl b
+causes the daemon to not detach itself from the controlling tty
+and put itself into the background.
+.It Fl x
+causes the daemon to try "extra hard" to contact a DCC server.
+Since it is usually more important to deliver mail than to report its
+checksums,
+.Nm
+normally does not delay too long while trying to contact a DCC server.
+It will not try again for several seconds after a failure.
+With
+.Fl x ,
+it will always try to contact the DCC server
+and it will tell the MTA to answer the DATA command with a 4yz
+temporary failure.
+.It Fl A
+adds to existing X-DCC headers in the message
+instead of replacing existing headers
+of the brand of the current server.
+.It Fl N
+neither adds, deletes, nor replaces existing X-DCC headers in the message.
+Each message is logged, rejected, and otherwise handled the same.
+.It Fl Q
+only queries the DCC server about the checksums of messages
+instead of reporting and querying.
+This is useful when
+.Nm
+is used to filter mail that has already been reported to a DCC
+server by another DCC client.
+No single mail message should be reported to a DCC
+server more than once per recipient,
+because each report will increase the apparent "bulkness" of the message.
+.Pp
+It is better to use
+.Em MXDCC
+lines in the global
+.Pa whiteclnt
+file for your MX mail servers that use DCC than
+.Fl Q .
+.It Fl G Ar on | off | noIP | IPmask/xx
+controls
+.Em greylisting .
+At least one working greylist server must be listed in the
+.Pa map
+file in the DCC home directory.
+If more than one is named,
+they must "flood" or change checksums and they must use the
+same
+.Fl G
+parameters.
+See
+.Xr dccd 8 .
+Usually all dccm or dccifd DCC client processes use the same
+.Fl G
+parameters.
+.Pp
+.Ar IPmask/xx
+and
+.Ar noIP
+remove part or all of the IP address from the greylist triple.
+The CIDR block size,
+.Ar xx ,
+must be between 1 and 128.
+96 is added to block sizes smaller than 33 to make them appropriate for
+the IPv6 addresses used by the DCC.
+.Ar IPmask/96
+differs from
+.Ar noIP
+for IPv4 addresses,
+because the former retains the IPv4 to IPv6 mapping prefix.
+.It Fl h Ar homedir
+overrides the default DCC home directory,
+.Pa @prefix@ .
+.It Fl I Ar user
+specifies the UID and GID of the process.
+.It Fl p Ar protocol:filename | protocol:port@host
+specifies the protocol and address by which sendmail will contact
+.Nm dccm .
+The default is a UNIX domain socket in the "run" directory,
+.Pa @dcc_rundir@/dccm .
+(See also
+.Fl R)
+This protocol and address must match the value in
+.Pa sendmail.cf .
+This mechanism can be used to connect
+.Nm
+on one computer to sendmail on another computer
+when a port and host name or IP address are used.
+.It Fl m Ar map
+specifies a name or path of the memory mapped parameter file instead
+of the default
+.Pa map
+file in the DCC home directory.
+It should be created with the
+.Xr cdcc 8
+command.
+.It Fl w Ar whiteclnt
+specifies an optional file containing filtering parameters
+as well as SMTP client IP addresses,
+SMTP envelope values, and header values
+of mail that is spam or is not spam and does not need a
+.Em X-DCC
+header,
+and whose checksums should not be reported to the DCC server.
+.Pp
+If the pathname
+.Ar whiteclnt
+is not absolute, it is relative to the DCC home directory.
+.Pp
+The format of the
+.Nm
+whiteclnt file is the same as the
+.Pa whitelist
+files used by
+.Xr dbclean 8
+and the
+.Pa whiteclnt
+file used by
+.Xr dccproc 8 .
+See
+.Xr dcc 8
+for a description of DCC white and blacklists.
+Because the contents of the
+.Ar whiteclnt
+file are used frequently, a companion file is automatically
+created and maintained.
+It has the same pathname but with an added suffix of
+.Ar .dccw
+and contains a memory mapped hash table of the main file.
+.Pp
+A whitelist entry ("OK") or two or more semi-whitelistings ("OK2")
+for one of the message's checksums prevents all of
+the message's checksums from being reported to the DCC server 
+and the addition of a
+.Em X-DCC
+header line by
+.Nm
+A whitelist entry for a checksum
+also prevents rejecting or discarding the message based on DCC recipient
+counts as specified by
+.Fl a
+and
+.Fl t .
+Otherwise, one or more checksums with blacklisting entries ("MANY") cause
+all of the message's
+checksums to be reported to the server with an addressee count of "MANY".
+.Pp
+If the message has a single recipient, an
+.Ar env_To
+.Ar whiteclnt
+entry of "OK" for the checksum of its recipient address acts like any other
+.Ar whiteclnt
+entry of "OK."
+When the SMTP message has more than one recipient,
+the effects can be complicated.
+When a message has several recipients with some but not all listed in the
+.Ar whiteclnt
+file,
+.Nm
+tries comply with the wishes of the users who want filtering as
+well as those who don't by silently not delivering the message to 
+those who want filtering (i.e. are not whitelisted) and delivering
+the message to don't want filtering.
+.It Fl U Ar userdirs
+enables per-user
+.Pa whiteclnt
+files and log directories.
+Each target of a message can have a directory of log files named
+.Ar usedirs/${dcc_userdir}/log
+where
+.Em ${dcc_userdir}
+is the
+.Pa sendmail.cf
+macro described below.
+If
+.Em ${dcc_userdir}
+is not set,
+.Ar userdirs/${rcpt_mailer}/${rcpt_addr}/log
+is used.
+The most likely value of
+.Ar mailer
+is
+.Ar local .
+Appropriate values for both
+.Ar ${rcpt_mailer}
+and
+.Ar ${rcpt_addr}
+can be seen by examining
+.Em env_To
+lines in
+.Fl l Ar logdir
+files.
+If it is not absolute,
+.Ar userdirs
+is relative to the DCC home directory.
+The directory containing the log files must be named
+.Ar log
+and it must be writable by the
+.Nm
+process.
+Each log directory must exist or logging for the corresponding
+is silently disabled.
+The files created in the log directory are owned by the UID of the
+.Nm
+process,
+but they have
+.Em group
+and
+.Em other
+read and write permissions copied from the corresponding
+.Ar log
+directory.
+To ensure the privacy of mail,
+it may be good to make the directories readable only by
+.Em owner
+and
+.Em group ,
+and to use a
+.Xr cron
+script that changes the owner of each file to match the grandparent
+.Ar addr
+directory.
+.Pp
+There can also be a per -user whitelist file named
+.Ar userdirs/${dcc_userdir}/whiteclnt
+or if
+.Ar ${dcc_userdir}
+is not set,
+.Ar userdirs/${rcpt_mailer}/${rcpt_addr}
+per-user whitelist files.
+Any checksum that is not white- or blacklisted by an individual
+addressee's 
+.Pa whiteclnt
+file  is checked in the main
+.Fl w whiteclnt
+file.
+A missing per-addressee
+.Ar whiteclnt
+file is the same as an empty file.
+Relative paths for files included in per-addressee files
+are resolved in the DCC home directory.
+The
+.Ar whiteclnt
+files and the
+.Ar addr
+directories containing them must be writable by the
+.Nm
+process.
+.Pp
+.Ar Option
+lines in per-user whiteclnt files can be used to modify many aspects of
+.Nm
+filtering,
+as described in the main
+.Xr dcc
+man page.
+For example, an
+.Ar option dcc-off
+line turns off DCC filtering for individual mailboxes.
+.It Fl a Ar IGNORE | REJECT | DISCARD
+specifies the action taken when
+DCC server counts or
+.Fl t
+thresholds say that a message is unsolicited and bulk.
+.Ar IGNORE
+causes the message to be unaffected except for adding the
+.Em X-DCC
+header line to the message.
+This turns off DCC filtering.
+.Pp
+Spam can also be
+.Ar REJECT Ns ed
+or accepted and silently
+.Ar DISCARD Ns ed
+without being delivered to local mailboxes.
+The default is
+.Ar REJECT .
+.Pp
+Mail forwarded via IP addresses marked
+.Em MX
+or
+.Em MXDCC
+in the main
+.Pa whiteclnt
+file is treated
+as if
+.Fl a Ar DISCARD
+were specified.
+This prevents "bouncing" spam.
+.Pp
+Determinations that mail is or is not spam from sendmail via
+.Em ${dcc_isspam}
+or
+.Em ${dcc_notspam}
+macros override
+.Fl a .
+The effects of the
+.Fl w Ar whiteclnt
+are not affected by
+.Fl a .
+.It Fl t Xo
+.Sm off
+.Ar type,
+.Op Ar log-thold,
+.Ar rej-thold
+.Sm on
+.Xc
+sets logging and "spam" thresholds for checksum
+.Ar type .
+The checksum types are
+.Ar IP ,
+.Ar env_From ,
+.Ar From ,
+.Ar Message-ID ,
+.Ar substitute ,
+.Ar Received ,
+.Ar Body ,
+.Ar Fuz1 ,
+.Ar Fuz2 ,
+.Ar rep-total ,
+and
+.Ar rep .
+The first six,
+.Ar IP
+through
+.Ar substitute ,
+have no effect except when a local DCC server configured with
+.Fl K
+is used.
+The
+.Ar substitute
+thresholds apply to the first substitute heading encountered in the mail
+message.
+The string
+.Ar ALL
+sets thresholds for all types, but is unlikely to be useful except for
+setting logging thresholds.
+The string
+.Ar CMN
+specifies the commonly used checksums
+.Ar Body ,
+.Ar Fuz1 ,
+and
+.Ar Fuz2 .
+.Ar Rej-thold
+and
+.Ar log-thold
+must be numbers, the string
+.Ar NEVER ,
+or the string
+.Ar MANY
+indicating millions of targets.
+Counts from the DCC server as large as the threshold for any single type
+are taken as sufficient evidence
+that the message should be logged or rejected.
+.Pp
+.Ar Log-thold
+is the threshold at which messages are logged.
+It can be handy to log messages at a lower threshold to find
+solicited bulk mail sources such as mailing lists.
+If no logging threshold is set,
+only rejected mail and messages with complicated combinations of white
+and blacklisting are logged.
+Messages that reach at least one of their rejection thresholds are
+logged regardless of logging thresholds.
+.Pp
+.Ar Rej-thold
+is the threshold at which messages are considered "bulk,"
+and so should be rejected or discarded if not whitelisted.
+.Pp
+DCC Reputation thresholds in the commercial version
+of the DCC are controlled by thresholds on checksum types
+.Ar rep
+and
+.Ar rep-total .
+Messages from an IP address that the DCC database says has sent
+more than
+.Fl t Ar rep-total,log-thold
+messages are logged.
+A DCC Reputation is computed for messages received
+from IP addresses that
+have sent more than
+.Fl t Ar rep-total,log-thold
+messages.
+The DCC Reputation of an IP address is the percentage of its messages
+that have been detected as bulk
+or having at least 10 recipients.
+The defaults are equivalent to
+.Fl t Ar rep,never
+and
+.Fl t Ar rep-total,never,20 .
+.Pp
+Bad DCC Reputations do not reject mail unless enabled by an
+.Ar option DCC-rep-on
+line in a
+.Pa whiteclnt
+file.
+.Pp
+The checksums of locally whitelisted messages are not checked with
+the DCC server and so only the number of targets of the current copy of
+a whitelisted message are compared against the thresholds.
+.Pp
+The default is
+.Ar ALL,NEVER ,
+so that nothing is discarded, rejected, or logged.
+A common choice is
+.Ar CMN,25,50
+to reject or discard
+mail with common bodies except as overridden by
+the whitelist of the DCC server, the sendmail
+.Em ${dcc_isspam}
+and
+.Em ${dcc_notspam}
+macros, and
+.Fl g ,
+and
+.Fl w .
+.It Fl g Xo
+.Sm off
+.Op Ar not-
+.Ar type
+.Sm on
+.Xc
+indicates that whitelisted,
+.Ar OK
+or
+.Ar OK2 ,
+counts from the DCC server for a type of checksum are to be believed.
+They should be ignored if prefixed with
+.Ar not- .
+.Ar Type
+is one of the same set of strings as for
+.Fl t .
+Only
+.Ar IP ,
+.Ar env_From ,
+and
+.Ar From
+are likely choices.
+By default all three are honored,
+and hence the need for
+.Ar not- .
+.It Fl S Ar hdr
+adds to the list of substitute or locally chosen headers that
+are checked with the
+.Fl w Ar whiteclnt
+file and sent to the DCC server.
+The checksum of the last header of type
+.Ar hdr
+found in the message is checked.
+.Ar Hdr
+can be
+.Em HELO
+to specify the SMTP envelope HELO value.
+.Ar Hdr
+can also be
+.Em mail_host
+to specify the sendmail "resolved" host name from
+the Mail_from value in the SMTP envelope.
+As many as six different substitute headers can be specified, but only
+the checksum of the first of the six will be sent to the DCC server.
+.It Fl l Ar logdir
+specifies a directory in which files containing copies of messages processed by
+.Nm
+are kept.
+They can be copied to per-user directories specified with
+.Fl U .
+Information about other recipients of a message is deleted from
+the per-user copies.
+.Pp
+See the FILES section below concerning the contents of the files.
+See also the
+.Ar option log-subdirectory-{day,hour,minute}
+lines in
+.Pa whiteclnt
+files described in
+.Xr dcc 8 .
+.Pp
+The directory is relative to the DCC home directory if it is not absolute
+.It Fl R Ar rundir
+specifies the "run" directory where the UNIX domain socket and file
+containing the daemon's process ID are stored.
+The default value is @dcc_rundir@ .
+.It Fl r Ar rejection-msg
+specifies the rejection message
+in
+.Fl o
+proxy mode
+for unsolicited bulk mail or for mail temporarily blocked by
+.Em greylisting
+when
+.Fl G
+is specified.
+The first
+.Fl r Ar rejection-msg
+replaces the default bulk mail rejection message,
+.Bk -words
+"5.7.1 550 mail %ID from %CIP rejected by DCC".
+.Ek
+." see rej_def in reply.c
+The second replaces
+.Bk -words
+"4.2.1 452 mail %ID from %CIP temporary greylist embargoed".
+.Ek
+." see grey_def in reply.c
+The third
+.Fl r Ar rejection-msg
+replaces the default SMTP rejection message
+.Bk -words
+"5.7.1 550 %ID bad reputation; see http://commercial-dcc.rhyolite.com/cgi-bin/reps.cgi?tgt=%CIP"
+.Ek
+for mail with bad DCC Reputations.
+If
+.Ar rejection-msg
+is the zero-length string,
+the
+.Fl r
+setting is counted but the corresponding message is not changed.
+.Pp
+.Ar Rejection-msg
+can contain specific information about the mail message.
+The following strings starting with % are replaced with the corresponding
+values:
+.Bl -tag -width "%BRESULT" -offset 4n -compact
+.It %ID
+message ID such as the unique part of log file name or sendmail queue ID
+.It %CIP
+SMTP client IP address
+.It %BTYPE
+type of DNS blacklist hit, such as "SMTP client", "mail_host", or "URL NS"
+.It %BTGT
+IP address or name declared bad by DNS blacklist
+.It %BPROBE
+domain name found in DNS blacklist such as 4.3.2.10.example.com
+.It %BRESULT
+value of the %BPROBE domain name found in DNS blacklist
+.El
+.Pp
+A common alternate for the bulk mail rejection message is
+.Bk -words
+"4.7.1 451 Access denied by DCC"
+.Ek
+to tell the sending mail system to continue trying.
+Use a 4yz response with caution, because it is likely to delay for days
+a delivery failure message for false positives.
+If the rejection message
+does not start with an RFC 1893 status code and RFC 2821 reply code,
+5.7.1 and 550 or 4.2.1 and 452 are used.
+.Pp
+See also
+.Fl B Ar set:rej-msg=rejection-msg
+to set the status message for mail rejected by DNS blacklists.
+.It Fl j Ar maxjobs
+limits the number of simultaneous requests that will be processed.
+The default value is the maximum number that seems to be possible given system
+limits on open files, select() bit masks, and so forth.
+Start
+.Nm
+with
+.Fl d
+and see the starting message in the system log to see the limit.
+.It Fl B Ar dnsbl-option
+enables DNS blacklist checks of the SMTP client IP address, SMTP envelope
+Mail_From sender domain name, and of host names in URLs in the message body.
+Body URL blacklisting has too many false positives to use on
+abuse mailboxes.
+It is less effective than greylisting with
+.Xr dccm 8
+or
+.Xr dccifd 8
+but can be useful in situations where
+greylisting cannot be used.
+.Pp
+.Ar Dnsbl-option
+is either one of the
+.Fl B Ar set:option
+forms or
+.Bd -literal -compact -offset 4n
+.Fl B Xo
+.Sm off
+.Ar domain Oo Ar ,IPaddr
+.Op Ar /xx Op Ar ,bltype Oc
+.Sm on
+.Xc
+.Ed
+.Ar Domain
+is a DNS blacklist domain such as example.com
+that will be searched.
+.Ar IPaddr Ns Op Ar /xxx
+is the string "any"
+an IP address in the DNS blacklist
+that indicates that the mail message
+should be rejected,
+or a CIDR block covering results from the DNS blacklist.
+"127.0.0.2" is assumed if
+.Ar IPaddr
+is absent.
+IPv6 addresses can be specified with the usual colon (:) notation.
+Names can be used instead of numeric addresses.
+The type of DNS blacklist
+is specified by
+.Ar bltype
+as
+.Ar name ,
+.Ar IPv4 ,
+or
+.Ar IPv6 .
+Given an envelope sender domain name or a domain name in a URL of
+spam.domain.org
+and a blacklist of type
+.Ar name ,
+spam.domain.org.example.com will be tried.
+Blacklist types of
+.Ar IPv4
+and
+.Ar IPv6
+require that the domain name in a URL sender address
+be resolved into an IPv4 or IPv6
+address.
+The address is then written as a reversed string of decimal
+octets to check the DNS blacklist, as in 2.0.0.127.example.com,
+.Pp
+More than one blacklist can be specified and blacklists can be grouped.
+All searching within a group is stopped at the first positive result.
+.Pp
+Positive results are ignored after being logged unless an
+.Ar option\ DNSBL-on
+line appears in the global or per-user
+.Pa whiteclnt
+file.
+.Pp
+.Bl -tag -width 3n
+.It Fl B Ar set:no-client
+says that SMTP client IP addresses and reverse DNS domain names should
+not be checked in the following blacklists.
+.br
+.Fl B Ar set:client
+restores the default for the following blacklists.
+.It Fl B Ar set:no-mail_host
+says that SMTP envelope Mail_From sender domain names should
+not be checked in the following blacklists.
+.Fl B Ar set:mail_host
+restores the default.
+.It Fl B Ar set:no-URL
+says that URLs in the message body should not be checked in the
+in the following blacklists.
+.Fl B Ar set:URL
+restores the default.
+.It Fl B Ar set:no-MX
+says MX servers of sender Mail_From domain names and host names in URLs
+should not be checked in the following blacklists.
+.br
+.Fl B Ar set:MX
+restores the default.
+.It Fl B Ar set:no-NS
+says DNS servers of sender Mail_From domain names and host names in URLs
+should not be checked in the following blacklists.
+.Fl B Ar set:NS
+restores the default.
+.It Fl B Ar set:defaults
+is equivalent to all of
+.Fl B Ar set:no-temp-fail
+.Fl B Ar set:client
+.br
+.Fl B Ar set:mail_host
+.Fl B Ar set:URL
+.Fl B Ar set:MX
+and
+.Fl B Ar set:NS
+.It Fl B Ar set:group=X
+adds later DNS blacklists specified with
+.Bd -literal -compact -offset 4n
+.Fl B Xo
+.Sm off
+.Ar domain Oo Ar ,IPaddr
+.Op Ar /xx Op Ar ,bltype Oc
+.Sm on
+.Xc
+.Ed
+to group 1, 2, or 3.
+.It Fl B Ar set:debug=X
+sets the DNS blacklist logging level
+.It Fl B Ar set:msg-secs=S
+limits
+.Nm
+to
+.Ar S
+seconds total for checking all DNS blacklists.
+The default is 25.
+.It Fl B Ar set:URL-secs=S
+limits
+.Nm
+to at most
+.Ar S
+seconds resolving and checking any single URL.
+The default is 11.
+Some spam contains dozens of URLs and that
+some "spamvertised" URLs contain host names that need minutes to
+resolve.
+Busy mail systems cannot afford to spend minutes checking each incoming
+mail message.
+.It Fl B Ar set:rej-msg=rejection-msg
+sets the SMTP rejection message for the following blacklists.
+.Ar Rejection-msg
+must be in the same format as for
+.Fl r .
+If
+.Ar rejection-msg
+is null, the default is restored.
+The default DNS blacklist rejection message is the first message set
+with
+.Fl r .
+.It Fl B Ar set:temp-fail
+causes
+.Nm
+to the MTA to answer the SMTP DATA command with
+.Bd -literal -offset 3n -compact
+452 4.2.1 mail %ID from %CIP temporary delayed for DNSBL
+.Ed
+if any DNS answer required for a DNSBL in the current group times out,
+including resolving names in URLs.
+.It Fl B Ar set:no-temp-fail
+restores the default of assuming a negative answer for DNS responses
+that take too long.
+.It Fl B Ar set:maxjobs=X
+sets maximum number of helper processes to
+.Ar X .
+In order to use typical single-threaded DNS resolver libraries,
+.Nm
+uses fleets of helper processes.
+It is rarely a good idea to change the default,
+which is the same as the maximum number of simultaneous jobs set with
+.Fl j .
+.It Fl B Ar set:progpath=@libexecdir@/dns-helper
+changes the path to the helper program.
+.El
+.It Fl L Ar ltype,facility.level
+specifies how messages should be logged.
+.Ar Ltype
+must be
+.Ar error ,
+.Ar info ,
+or
+.Ar off
+to indicate which of the two types of messages are being controlled or
+to turn off all
+.Xr syslog 3
+messages from
+.Nm .
+.Ar Level
+must be a
+.Xr syslog 3
+level among
+.Ar EMERG ,
+.Ar ALERT ,
+.Ar CRIT , ERR ,
+.Ar WARNING ,
+.Ar NOTICE ,
+.Ar INFO ,
+and
+.Ar DEBUG .
+.Ar Facility
+must be among
+.Ar AUTH ,
+.Ar AUTHPRIV ,
+.Ar CRON ,
+.Ar DAEMON ,
+.Ar FTP ,
+.Ar KERN ,
+.Ar LPR ,
+.Ar MAIL ,
+.Ar NEWS ,
+.Ar USER ,
+.Ar UUCP ,
+and
+.Ar LOCAL0
+through
+.Ar LOCAL7 .
+The default is equivalent to
+.Dl Fl L Ar info,MAIL.NOTICE  Fl L Ar error,MAIL.ERR
+.El
+.Pp
+.Nm
+normally sends counts of mail rejected and so forth the to system log at
+midnight.
+The SIGUSR1 signal sends an immediate report to the system log.
+They will be repeated every 24 hours instead of at midnight.
+.Sh SENDMAIL MACROS
+Sendmail can affect
+.Nm
+with the values of some
+.Pa sendmail.cf
+macros.
+These macro names must be added to the
+Milter.macros option statements in
+.Pa sendmail.cf
+as in the example "Feature" file dcc.m4.
+.Bl -tag -width dcc_mail_host
+.It Em ${dcc_isspam}
+causes a mail message to be reported to the DCC server
+as having been addressed to "MANY" recipients.
+The
+.Em ${dcc_isspam}
+macro is ignored if the
+.Em ${dcc_notspam}
+macro is set to a non-null string
+.Pp
+If the value of the
+.Ar ${dcc_isspam}
+is null,
+.Nm
+uses SMTP rejection messages controlled by
+.Fl a
+and
+.Fl r .
+If the value of the
+.Ar ${dcc_isspam}
+macro starts with "DISCARD",
+the mail message is silently discarded
+as with
+.Fl a Ar DISCARD.
+If value of the macro not null and does not start with "DISCARD",
+it is used as the SMTP error
+message given to the SMTP client trying to send the rejected message.
+The message starts with an optional SMTP error type and number
+followed by text.
+.Pp
+The
+.Fl a
+option does not effect messages
+marked spam with
+.Em ${dcc_isspam} .
+When the
+.Em ${dcc_isspam}
+macro is set, the message is rejected or discarded despite
+local or DCC database whitelist entries.
+The local whitelist does control whether the message's
+checksums will be reported to the DCC server and an
+.Em X-DCC
+SMTP header line will be added.
+.It Em ${dcc_notspam}
+causes a message not be considered unsolicited bulk despite
+evidence to the contrary.
+It also prevents
+.Nm
+from reporting the checksums of the message to the DCC server
+and from adding an
+.Em X-DCC
+header line.
+.Pp
+When the macro is set by the
+.Pa sendmail.cf
+rules,
+.Ar ${dcc_notspam}
+macros overrides DCC threshlds that say the message should be
+rejected as well as the effects of the
+.Em ${dcc_isspam}
+macro.
+.It Em ${dcc_mail_host}
+specifies the name of the SMTP client that is sending the message.
+This macro is usually the same as the
+.Em mail_host
+macro.
+They can differ when a sendmail "smart relay" is involved.
+The
+.Em ${dcc_mail_host}
+macro does not work if
+.Em FEATURE(delay_checks)
+is used.
+.It Em ${dcc_userdir}
+is the per-user whitelist and log directory for a recipient.
+If the macro is not set in sendmail.cf,
+$&{rcpt_mailer}/$&{rcpt_addr}
+is assumed, but with the recipient address converted to lower case.
+Whatever value is used,
+the directory name after the last slash (/) character is converted to
+lower case.
+Any value containing the string "/../" is ignored.
+.Pp
+This macro also does not work if
+.Em FEATURE(delay_checks)
+is used.
+.Pp
+The following two lines in a sendmail mc file have the same effect
+as not defining the ${dcc_userdir} macro, provided
+.Em FEATURE(dcc)
+is also used and
+the sendmail
+.Pa cf/feature
+directory has a symbolic link to the
+.Pa misc/dcc.m4
+file.
+.El
+.Pp
+.Bd -literal -compact
+SLocal_check_rcpt
+R$*	$: $1 $(macro {dcc_userdir} $@ $&{rcpt_mailer}/$&{rcpt_addr} $))
+.Ed
+.Sh FILES
+.Bl -tag -width whiteclnt -compact
+.It Pa @prefix@
+is the DCC home directory in which other files are found.
+.It Pa @libexecdir@/start-dccm
+is a script used to
+.Nm .
+.It Pa dcc/dcc_conf
+contains parameters used by the scripts to start DCC daemons and cron jobs.
+.It Pa logdir
+is an optional directory specified with
+.Fl l
+and containing marked mail.
+Each file in the directory contains one message, at least one of whose
+checksums reached its
+.Fl t
+thresholds or that is interesting for some other reason.
+Each file starts with lines containing the date when the message
+was received, the IP address of the SMTP client, and SMTP envelope
+values.
+Those lines are followed by the body of the SMTP message including its header
+as it was received by sendmail and without any new or changed header lines.
+Only approximately the first 32 KBytes of the body are recorded
+unless modified by
+.Em ./configure --with-max-log-size=xx
+The checksums for the message follow the body.
+They are followed by lines indicating that the
+.Em ${dcc_isspam}
+or
+.Em ${dcc_notspam}
+.Pa sendmail.cf
+macros were set or one of the checksums is white- or blacklisted by the
+.Fl w Ar whiteclnt
+file.
+Each file ends with the
+.Em X-DCC
+header line added to the message and the disposition of
+the message including SMTP status message if appropriate.
+.It Pa map
+is the memory mapped file of information concerning DCC servers
+in the DCC home directory.
+.It Pa whiteclnt
+contains the client whitelist in
+the format described in
+.Xr dcc 8 .
+.It Pa whiteclnt.dccw
+is a memory mapped hash table of the
+.Pa whiteclnt
+file.
+.It Pa dccm.pid
+in the
+.Fl R Ar rundir
+directory contains daemon's process ID.
+The string
+.Dq dccm
+is replaced by the file name containing the daemon to facilitate
+running multiple daemons, probably connected to remote instances of
+sendmail using TCP/IP instead of a UNIX domain socket.
+See also
+.Fl R .
+.It Pa @dcc_rundir@/dccm
+is the default UNIX domain socket used by the sendmail milter interface.
+See also
+.Fl R .
+.It Pa sendmail.cf
+is the
+.Xr sendmail 8
+control file.
+.It Pa misc/dcc.m4
+sendmail mc file that should have a symbolic link in the sendmail
+cf/feature directory so that
+.Em FEATURE(dcc)
+can be used in a sendmail mc file.
+.El
+.Sh EXAMPLES
+.Nm Dccm
+should be started before sendmail with something like the
+script
+.Pa @libexecdir@/start-dccm.
+It looks for common DCC parameters in the
+.Pa dcc_conf
+file in the DCC home directory,
+.Pa @prefix@.
+.Pp
+Those numbers should modified to fit local conditions.
+It might be wise to replace the "100" numbers with much larger
+values or with "MANY" until a few weeks of monitoring the log directory
+show that sources of mailing lists are in the server's whitelist file
+(see
+.Xr dccd 8 )
+or the local
+.Pa whiteclnt
+file.
+.Pp
+It is usually necessary to regularly delete old log files
+with a script like @libexecdir@/cron-dccd.
+.Pp
+On systems unlike modern FreeBSD and other UNIX-like systems which
+include sendmail milter support,
+sendmail must be built with the milter interface, such as by creating a
+.Pa devtools/Site/site.config.m4
+or similar file containing something like the following lines:
+.Bd -literal -offset indent
+APPENDDEF(`conf_sendmail_ENVDEF', `-D_FFR_MILTER=1')
+APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_MILTER=1')
+.Ed
+.Pp
+Appropriate lines invoking the milter interface must be added to
+.Pa sendmail.cf.
+That can be done by putting a symbolic link to the
+the misc/dcc.m4 file in the DCC source to the sendmail cf/feature directory
+and adding the line
+.Pp
+.Dl FEATURE(dcc)
+.Pp
+to the local .mc file.
+.Pp
+Note that
+.Nm
+should not be used with the Postfix milter mechanism.
+Instead use
+.Xr dccifd 8
+as a before-queue filter as described in that man page.
+.Sh SEE ALSO
+.Xr cdcc 8 ,
+.Xr dbclean 8 ,
+.Xr dcc 8 ,
+.Xr dccd 8 ,
+.Xr dblist 8 ,
+.Xr dccifd 8 ,
+.Xr dccproc 8 ,
+.Xr dccsight 8 ,
+.Xr sendmail 8 .
+.Sh HISTORY
+Distributed Checksum Clearinghouses are based on an idea of Paul Vixie.
+Implementation of
+.Nm
+was started at Rhyolite Software in 2000.
+This document describes version 1.3.103.
+.Sh BUGS
+.Nm
+uses
+.Fl t
+where
+.Xr dccproc 8
+uses
+.Fl c .
+.Pp
+Systems without
+.Xr setrlimit 2
+and
+.Xr getrlimit 2
+RLIMIT_NOFILE
+can have problems with the default limit on the number of simultaneous
+jobs, the value of
+.Fl j .
+Every job requires four open files.
+These problems are usually seen with errors messages that say something like
+.Dl dccm[24448]: DCC: accept() returned invalid socket
+A fix is to use a smaller value for
+.Fl j
+or to allow
+.Nm
+to open more files.
+Sendmail version 8.13 and later can be told to poll() instead of select
+with SM_CONF_POLL.
+Some older versions of sendmail knew about FFR_USE_POLL.
+One of the following lines in your devtools/Site/site.config.m4
+file can help:
+.Bd -literal -offset indent
+APPENDDEF(`conf_libmilter_ENVDEF', `-DSM_CONF_POLL')
+APPENDDEF(`conf_libmilter_ENVDEF', `-DFFR_USE_POLL')
+.Ed
+.Pp
+On many systems with sendmail 8.11.3 and preceding,
+a bug in the sendmail milter mechanism causes
+.Nm
+to die with a core file when given a signal.
diff -r 000000000000 -r c7f6b056b673 dccm.html.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccm.html.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,687 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<HTML>
+<HEAD>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <TITLE>dccm.0.8</TITLE>
+    <META http-equiv="Content-Style-Type" content="text/css">
+    <STYLE type="text/css">
+	BODY {background-color:white; color:black}
+	ADDRESS {font-size:smaller}
+        IMG.logo {width:6em; vertical-align:middle}
+    </STYLE>
+</HEAD>
+<BODY>
+<PRE>
+<!-- Manpage converted by man2html 3.0.1 -->
+<B><A HREF="dccm.html">dccm(8)</A></B>               Distributed Checksum Clearinghouse               <B><A HREF="dccm.html">dccm(8)</A></B>
+
+
+</PRE>
+<H2><A NAME="NAME">NAME</A></H2><PRE>
+     <B>dccm</B> -- Distributed Checksum Clearinghouse Milter Interface
+
+
+</PRE>
+<H2><A NAME="SYNOPSIS">SYNOPSIS</A></H2><PRE>
+     <B>dccm</B> [<B>-VdbxANQ</B>] [<B>-G</B> <I>on</I> | <I>off</I> | <I>noIP</I> | <I>IPmask/xx</I>] [<B>-h</B> <I>homedir</I>] [<B>-I</B> <I>user</I>]
+          [<B>-p</B> <I>protocol:filename</I> | <I>protocol:port@host</I>] [<B>-m</B> <I>map</I>]
+          [<B>-w</B> <I>whiteclnt</I>] [<B>-U</B> <I>userdirs</I>] [<B>-a</B> <I>IGNORE</I> | <I>REJECT</I> | <I>DISCARD</I>]
+          [<B>-t</B> <I>type,</I>[<I>log-thold,</I>]<I>rej-thold</I>] [<B>-g</B> [<I>not-</I>]<I>type</I>] [<B>-S</B> <I>header</I>]
+          [<B>-l</B> <I>logdir</I>] [<B>-R</B> <I>rundir</I>] [<B>-r</B> <I>rejection-msg</I>] [<B>-j</B> <I>maxjobs</I>]
+          [<B>-B</B> <I>dnsbl-option</I>] [<B>-L</B> <I>ltype,facility.level</I>]
+
+
+</PRE>
+<H2><A NAME="DESCRIPTION">DESCRIPTION</A></H2><PRE>
+     <B>dccm</B> is a daemon built with the sendmail milter interface intended to
+     connect <B>sendmail(8)</B> to DCC servers.  When built with the milter filter
+     machinery and configured to talk to <B>dccm</B> in the <I>sendmail.cf</I> file, send-
+     mail passes all email to <B>dccm</B> which in turn reports related checksums to
+     the nearest DCC server.  <B>dccm</B> then adds an <I>X-DCC</I> SMTP header line to the
+     message.  Sendmail is told to reject the message if it is unsolicited
+     bulk mail.
+
+     <B>Dccm</B> sends reports of checksums related to mail received by DCC clients
+     and queries about the total number of reports of particular checksums.  A
+     DCC server receives <I>no</I> mail, address, headers, or other information, but
+     only cryptographically secure checksums of such information.  A DCC
+     server cannot determine the text or other information that corresponds to
+     the checksums it receives.  Its only acts as a clearinghouse of counts
+     for checksums computed by clients.  For complete privacy as far as the
+     DCC is concerned, the checksums of purely internal mail or other mail
+     that is known to not be unsolicited bulk can be listed in a whitelist to
+     not be reported to the DCC server.
+
+     Since the checksums of messages that are whitelisted locally by the <B>-w</B>
+     <I>whiteclnt</I> file are not reported to the DCC server, <B>dccm</B> knows nothing
+     about the total recipient counts for their checksums and so cannot add
+     <I>X-DCC</I> header lines to such messages.  Sendmail does not tell <B>dccm</B> about
+     messages that are not received by sendmail via SMTP, including messages
+     submitted locally and received via UUCP, and so they also do not receive
+     <I>X-DCC</I> header lines.
+
+     Enable the daemon and put its parameters in the <I>dcc</I><B>_</B><I>conf</I> file and start
+     the daemon with the <I>@libexecdir@/start-dccm</I> or <I>var/dcc/libexec/rcDCC</I>
+     script.
+
+     The list of servers that <B>dccm</B> contacts is in the memory mapped file <I>map</I>
+     shared by local DCC clients.  The file is  maintained with <B><A HREF="cdcc.html">cdcc(8)</A></B>.
+
+   <A NAME="OPTIONS"><B>OPTIONS</B></A>
+     The following options are available:
+
+     <A NAME="OPTION-V"><B>-V</B></A>   displays the version of <B>dccm</B>.
+
+     <A NAME="OPTION-d"><B>-d</B></A>   enables debugging output from the DCC client software.  Additional
+          <B>-d</B> options increase the number of messages.  A single <B>-d</B>
+           aborted SMTP transactions including those from some "dictionary
+          attacks."
+
+     <A NAME="OPTION-b"><B>-b</B></A>   causes the daemon to not detach itself from the controlling tty and
+          put itself into the background.
+
+     <A NAME="OPTION-x"><B>-x</B></A>   causes the daemon to try "extra hard" to contact a DCC server.
+          Since it is usually more important to deliver mail than to report
+          its checksums, <B>dccm</B> normally does not delay too long while trying to
+          contact a DCC server.  It will not try again for several seconds
+          after a failure.  With <B>-x</B>, it will always try to contact the DCC
+          server and it will tell the MTA to answer the DATA command with a
+          4yz temporary failure.
+
+     <A NAME="OPTION-A"><B>-A</B></A>   adds to existing X-DCC headers in the message instead of replacing
+          existing headers of the brand of the current server.
+
+     <A NAME="OPTION-N"><B>-N</B></A>   neither adds, deletes, nor replaces existing X-DCC headers in the
+          message.  Each message is logged, rejected, and otherwise handled
+          the same.
+
+     <A NAME="OPTION-Q"><B>-Q</B></A>   only queries the DCC server about the checksums of messages instead
+          of reporting and querying.  This is useful when <B>dccm</B> is used to fil-
+          ter mail that has already been reported to a DCC server by another
+          DCC client.  No single mail message should be reported to a DCC
+          server more than once per recipient, because each report will
+          increase the apparent "bulkness" of the message.
+
+          It is better to use <I>MXDCC</I> lines in the global <I>whiteclnt</I> file for
+          your MX mail servers that use DCC than <B>-Q</B>.
+
+     <A NAME="OPTION-G"><B>-G</B></A> <I>on</I> | <I>off</I> | <I>noIP</I> | <I>IPmask/xx</I>
+          controls <I>greylisting</I>.  At least one working greylist server must be
+          listed in the <I>map</I> file in the DCC home directory.  If more than one
+          is named, they must "flood" or change checksums and they must use
+          the same <B>-G</B> parameters.  See <B><A HREF="dccd.html">dccd(8)</A></B>.  Usually all dccm or dccifd
+          DCC client processes use the same <B>-G</B> parameters.
+
+          <I>IPmask/xx</I> and <I>noIP</I> remove part or all of the IP address from the
+          greylist triple.  The CIDR block size, <I>xx</I>, must be between 1 and
+          128.  96 is added to block sizes smaller than 33 to make them appro-
+          priate for the IPv6 addresses used by the DCC.  <I>IPmask/96</I> differs
+          from <I>noIP</I> for IPv4 addresses, because the former retains the IPv4 to
+          IPv6 mapping prefix.
+
+     <A NAME="OPTION-h"><B>-h</B></A> <I>homedir</I>
+          overrides the default DCC home directory, <I>@prefix@</I>.
+
+     <A NAME="OPTION-I"><B>-I</B></A> <I>user</I>
+          specifies the UID and GID of the process.
+
+     <A NAME="OPTION-p"><B>-p</B></A> <I>protocol:filename</I> | <I>protocol:port@host</I>
+          specifies the protocol and address by which sendmail will contact
+          <B>dccm</B>.  The default is a UNIX domain socket in the "run" directory,
+          <I>@dcc_rundir@/dccm</I>.  (See also <B>-R)</B> This protocol and address must
+          match the value in <I>sendmail.cf</I>.  This mechanism can be used to con-
+          nect <B>dccm</B> on one computer to sendmail on another computer when a
+          port and host name or IP address are used.
+
+     <A NAME="OPTION-m"><B>-m</B></A> <I>map</I>
+          specifies a name or path of the memory mapped parameter file instead
+          of the default <I>map</I> file in the DCC home directory.  It should be
+          created with the <B><A HREF="cdcc.html">cdcc(8)</A></B> command.
+
+     <A NAME="OPTION-w"><B>-w</B></A> <I>whiteclnt</I>
+          specifies an optional file containing filtering parameters as well
+          as SMTP client IP addresses, SMTP envelope values, and header values
+          of mail that is spam or is not spam and does not need a <I>X-DCC</I>
+          header, and whose checksums should not be reported to the DCC
+          server.
+
+          If the pathname <I>whiteclnt</I> is not absolute, it is relative to the DCC
+          home directory.
+
+          The format of the <B>dccm</B> whiteclnt file is the same as the <I>whitelist</I>
+          files used by <B><A HREF="dbclean.html">dbclean(8)</A></B> and the <I>whiteclnt</I> file used by <B><A HREF="dccproc.html">dccproc(8)</A></B>.
+          See <B><A HREF="dcc.html">dcc(8)</A></B> for a description of DCC white and blacklists.  Because
+          the contents of the <I>whiteclnt</I> file are used frequently, a companion
+          file is automatically created and maintained.  It has the same path-
+          name but with an added suffix of <I>.dccw</I> and contains a memory mapped
+          hash table of the main file.
+
+          A whitelist entry ("OK") or two or more semi-whitelistings ("OK2")
+          for one of the message's checksums prevents all of the message's
+          checksums from being reported to the DCC server and the addition of
+          a <I>X-DCC</I> header line by <B>dccm</B> A whitelist entry for a checksum also
+          prevents rejecting or discarding the message based on DCC recipient
+          counts as specified by <B>-a</B> and <B>-t</B>.  Otherwise, one or more checksums
+          with blacklisting entries ("MANY") cause all of the message's check-
+          sums to be reported to the server with an addressee count of "MANY".
+
+          If the message has a single recipient, an <I>env</I><B>_</B><I>To</I> <I>whiteclnt</I> entry of
+          "OK" for the checksum of its recipient address acts like any other
+          <I>whiteclnt</I> entry of "OK."  When the SMTP message has more than one
+          recipient, the effects can be complicated.  When a message has sev-
+          eral recipients with some but not all listed in the <I>whiteclnt</I> file,
+          <B>dccm</B> tries comply with the wishes of the users who want filtering as
+          well as those who don't by silently not delivering the message to
+          those who want filtering (i.e. are not whitelisted) and delivering
+          the message to don't want filtering.
+
+     <A NAME="OPTION-U"><B>-U</B></A> <I>userdirs</I>
+          enables per-user <I>whiteclnt</I> files and log directories.  Each target
+          of a message can have a directory of log files named
+          <I>usedirs/${dcc</I><B>_</B><I>userdir}/log</I> where <I>${dcc</I><B>_</B><I>userdir}</I> is the <I>sendmail.cf</I>
+          macro described below.  If <I>${dcc</I><B>_</B><I>userdir}</I> is not set,
+          <I>userdirs/${rcpt</I><B>_</B><I>mailer}/${rcpt</I><B>_</B><I>addr}/log</I> is used.  The most likely
+          value of <I>mailer</I> is <I>local</I>.  Appropriate values for both
+          <I>${rcpt</I><B>_</B><I>mailer}</I> and <I>${rcpt</I><B>_</B><I>addr}</I> can be seen by examining <I>env</I><B>_</B><I>To</I>
+          lines in <B>-l</B> <I>logdir</I> files.  If it is not absolute, <I>userdirs</I> is rela-
+          tive to the DCC home directory.  The directory containing the log
+          files must be named <I>log</I> and it must be writable by the <B>dccm</B> process.
+          Each log directory must exist or logging for the corresponding is
+          silently disabled.  The files created in the log directory are owned
+          by the UID of the <B>dccm</B> process, but they have <I>group</I> and <I>other</I> read
+          and write permissions copied from the corresponding <I>log</I> directory.
+          To ensure the privacy of mail, it may be good to make the directo-
+          ries readable only by <I>owner</I> and <I>group</I>, and to use a cron script that
+          changes the owner of each file to match the grandparent <I>addr</I> direc-
+          tory.
+
+          There can also be a per -user whitelist file named
+          <I>userdirs/${dcc</I><B>_</B><I>userdir}/whiteclnt</I> or if <I>${dcc</I><B>_</B><I>userdir}</I> is not set,
+          <I>userdirs/${rcpt</I><B>_</B><I>mailer}/${rcpt</I><B>_</B><I>addr}</I> per-user whitelist files.  Any
+          checksum that is not white- or blacklisted by an individual
+          addressee's <I>whiteclnt</I> file  is checked in the main <B>-w -whiteclnt</B>
+          file.  A missing per-addressee <I>whiteclnt</I> file is the same as an
+          empty file.  Relative paths for files included in per-addressee
+          files are resolved in the DCC home directory.  The <I>whiteclnt</I> files
+          and the <I>addr</I> directories containing them must be writable by the
+          <B>dccm</B> process.
+
+          <I>Option</I> lines in per-user whiteclnt files can be used to modify many
+          aspects of <B>dccm</B> filtering, as described in the main dcc man page.
+          For example, an <I>option</I> <I>dcc-off</I> line turns off DCC filtering for
+          individual mailboxes.
+
+     <A NAME="OPTION-a"><B>-a</B></A> <I>IGNORE</I> | <I>REJECT</I> | <I>DISCARD</I>
+          specifies the action taken when DCC server counts or <B>-t</B> thresholds
+          say that a message is unsolicited and bulk.  <I>IGNORE</I> causes the mes-
+          sage to be unaffected except for adding the <I>X-DCC</I> header line to the
+          message.  This turns off DCC filtering.
+
+          Spam can also be <I>REJECT</I>ed or accepted and silently <I>DISCARD</I>ed without
+          being delivered to local mailboxes.  The default is <I>REJECT</I>.
+
+          Mail forwarded via IP addresses marked <I>MX</I> or <I>MXDCC</I> in the main
+          <I>whiteclnt</I> file is treated as if <B>-a</B> <I>DISCARD</I> were specified.  This
+          prevents "bouncing" spam.
+
+          Determinations that mail is or is not spam from sendmail via
+          <I>${dcc</I><B>_</B><I>isspam}</I> or <I>${dcc</I><B>_</B><I>notspam}</I> macros override <B>-a</B>.  The effects of
+          the <B>-w</B> <I>whiteclnt</I> are not affected by <B>-a</B>.
+
+     <A NAME="OPTION-t"><B>-t</B></A> <I>type,</I>[<I>log-thold,</I>]<I>rej-thold</I>
+          sets logging and "spam" thresholds for checksum <I>type</I>.  The checksum
+          types are <I>IP</I>, <I>env</I><B>_</B><I>From</I>, <I>From</I>, <I>Message-ID</I>, <I>substitute</I>, <I>Received</I>,
+          <I>Body</I>, <I>Fuz1</I>, <I>Fuz2</I>, <I>rep-total</I>, and <I>rep</I>.  The first six, <I>IP</I> through
+          <I>substitute</I>, have no effect except when a local DCC server configured
+          with <B>-K</B> is used.  The <I>substitute</I> thresholds apply to the first sub-
+          stitute heading encountered in the mail message.  The string <I>ALL</I>
+          sets thresholds for all types, but is unlikely to be useful except
+          for setting logging thresholds.  The string <I>CMN</I> specifies the com-
+          monly used checksums <I>Body</I>, <I>Fuz1</I>, and <I>Fuz2</I>.  <I>Rej-thold</I> and <I>log-thold</I>
+          must be numbers, the string <I>NEVER</I>, or the string <I>MANY</I> indicating
+          millions of targets.  Counts from the DCC server as large as the
+          threshold for any single type are taken as sufficient evidence that
+          the message should be logged or rejected.
+
+          <I>Log-thold</I> is the threshold at which messages are logged.  It can be
+          handy to log messages at a lower threshold to find solicited bulk
+          mail sources such as mailing lists.  If no logging threshold is set,
+          only rejected mail and messages with complicated combinations of
+          white and blacklisting are logged.  Messages that reach at least one
+          of their rejection thresholds are logged regardless of logging
+          thresholds.
+
+          <I>Rej-thold</I> is the threshold at which messages are considered "bulk,"
+          and so should be rejected or discarded if not whitelisted.
+
+          DCC Reputation thresholds in the commercial version of the DCC are
+          controlled by thresholds on checksum types <I>rep</I> and <I>rep-total</I>.  Mes-
+          sages from an IP address that the DCC database says has sent more
+          than <B>-t</B> <I>rep-total,log-thold</I> messages are logged.  A DCC Reputation
+          is computed for messages received from IP addresses that have sent
+          more than <B>-t</B> <I>rep-total,log-thold</I> messages.  The DCC Reputation of an
+          IP address is the percentage of its messages that have been detected
+          as bulk or having at least 10 recipients.  The defaults are equiva-
+          lent to <B>-t</B> <I>rep,never</I> and <B>-t</B> <I>rep-total,never,20</I>.
+
+          Bad DCC Reputations do not reject mail unless enabled by an <I>option</I>
+          <I>DCC-rep-on</I> line in a <I>whiteclnt</I> file.
+
+          The checksums of locally whitelisted messages are not checked with
+          the DCC server and so only the number of targets of the current copy
+          of a whitelisted message are compared against the thresholds.
+
+          The default is <I>ALL,NEVER</I>, so that nothing is discarded, rejected, or
+          logged.  A common choice is <I>CMN,25,50</I> to reject or discard mail with
+          common bodies except as overridden by the whitelist of the DCC
+          server, the sendmail <I>${dcc</I><B>_</B><I>isspam}</I> and <I>${dcc</I><B>_</B><I>notspam}</I> macros, and
+          <B>-g</B>, and <B>-w</B>.
+
+     <A NAME="OPTION-g"><B>-g</B></A> [<I>not-</I>]<I>type</I>
+          indicates that whitelisted, <I>OK</I> or <I>OK2</I>, counts from the DCC server
+          for a type of checksum are to be believed.  They should be ignored
+          if prefixed with <I>not-</I>.  <I>Type</I> is one of the same set of strings as
+          for <B>-t</B>.  Only <I>IP</I>, <I>env</I><B>_</B><I>From</I>, and <I>From</I> are likely choices.  By default
+          all three are honored, and hence the need for <I>not-</I>.
+
+     <A NAME="OPTION-S"><B>-S</B></A> <I>hdr</I>
+          adds to the list of substitute or locally chosen headers that are
+          checked with the <B>-w</B> <I>whiteclnt</I> file and sent to the DCC server.  The
+          checksum of the last header of type <I>hdr</I> found in the message is
+          checked.  <I>Hdr</I> can be <I>HELO</I> to specify the SMTP envelope HELO value.
+          <I>Hdr</I> can also be <I>mail</I><B>_</B><I>host</I> to specify the sendmail "resolved" host
+          name from the Mail_from value in the SMTP envelope.  As many as six
+          different substitute headers can be specified, but only the checksum
+          of the first of the six will be sent to the DCC server.
+
+     <A NAME="OPTION-l"><B>-l</B></A> <I>logdir</I>
+          specifies a directory in which files containing copies of messages
+          processed by <B>dccm</B> are kept.  They can be copied to per-user directo-
+          ries specified with <B>-U</B>.  Information about other recipients of a
+          message is deleted from the per-user copies.
+
+          See the FILES section below concerning the contents of the files.
+          See also the <I>option</I> <I>log-subdirectory-{day,hour,minute}</I> lines in
+          <I>whiteclnt</I> files described in <B><A HREF="dcc.html">dcc(8)</A></B>.
+
+          The directory is relative to the DCC home directory if it is not
+          absolute
+
+     <A NAME="OPTION-R"><B>-R</B></A> <I>rundir</I>
+          specifies the "run" directory where the UNIX domain socket and file
+          containing the daemon's process ID are stored.  The default value is
+          @dcc_rundir@ .
+
+     <A NAME="OPTION-r"><B>-r</B></A> <I>rejection-msg</I>
+          specifies the rejection message in <B>-o</B> proxy mode for unsolicited
+          bulk mail or for mail temporarily blocked by <I>greylisting</I> when <B>-G</B> is
+          specified.  The first <B>-r</B> <I>rejection-msg</I> replaces the default bulk
+          mail rejection message, "5.7.1 550 mail %ID from %CIP rejected by
+          DCC".  The second replaces "4.2.1 452 mail %ID from %CIP temporary
+          greylist embargoed".  The third <B>-r</B> <I>rejection-msg</I> replaces the
+          default SMTP rejection message "5.7.1 550 %ID bad reputation; see
+          http://commercial-dcc.rhyolite.com/cgi-bin/reps.cgi?tgt=%CIP" for
+          mail with bad DCC Reputations.  If <I>rejection-msg</I> is the zero-length
+          string, the <B>-r</B> setting is counted but the corresponding message is
+          not changed.
+
+          <I>Rejection-msg</I> can contain specific information about the mail mes-
+          sage.  The following strings starting with % are replaced with the
+          corresponding values:
+              %ID       message ID such as the unique part of log file name or
+                        sendmail queue ID
+              %CIP      SMTP client IP address
+              %BTYPE    type of DNS blacklist hit, such as "SMTP client",
+                        "mail_host", or "URL NS"
+              %BTGT     IP address or name declared bad by DNS blacklist
+              %BPROBE   domain name found in DNS blacklist such as
+                        4.3.2.10.example.com
+              %BRESULT  value of the %BPROBE domain name found in DNS black-
+                        list
+
+          A common alternate for the bulk mail rejection message is "4.7.1 451
+          Access denied by DCC" to tell the sending mail system to continue
+          trying.  Use a 4yz response with caution, because it is likely to
+          delay for days a delivery failure message for false positives.  If
+          the rejection message does not start with an RFC 1893 status code
+          and RFC 2821 reply code, 5.7.1 and 550 or 4.2.1 and 452 are used.
+
+          See also <B>-B</B> <I>set:rej-msg=rejection-msg</I> to set the status message for
+          mail rejected by DNS blacklists.
+
+     <A NAME="OPTION-j"><B>-j</B></A> <I>maxjobs</I>
+          limits the number of simultaneous requests that will be processed.
+          The default value is the maximum number that seems to be possible
+          given system limits on open files, select() bit masks, and so forth.
+          Start <B>dccm</B> with <B>-d</B> and see the starting message in the system log to
+          see the limit.
+
+     <A NAME="OPTION-B"><B>-B</B></A> <I>dnsbl-option</I>
+          enables DNS blacklist checks of the SMTP client IP address, SMTP
+          envelope Mail_From sender domain name, and of host names in URLs in
+          the message body.  Body URL blacklisting has too many false posi-
+          tives to use on abuse mailboxes.  It is less effective than
+          greylisting with <B><A HREF="dccm.html">dccm(8)</A></B> or <B><A HREF="dccifd.html">dccifd(8)</A></B> but can be useful in situa-
+          tions where greylisting cannot be used.
+
+          <I>Dnsbl-option</I> is either one of the <B>-B</B> <I>set:option</I> forms or
+              <B>-B</B> <I>domain</I>[<I>,IPaddr</I>[<I>/xx</I>[<I>,bltype</I>]]]
+          <I>Domain</I> is a DNS blacklist domain such as example.com that will be
+          searched.  <I>IPaddr</I>[<I>/xxx</I>] is the string "any" an IP address in the DNS
+          blacklist that indicates that the mail message should be rejected,
+          or a CIDR block covering results from the DNS blacklist.
+          "127.0.0.2" is assumed if <I>IPaddr</I> is absent.  IPv6 addresses can be
+          specified with the usual colon (:) notation.  Names can be used
+          instead of numeric addresses.  The type of DNS blacklist is speci-
+          fied by <I>bltype</I> as <I>name</I>, <I>IPv4</I>, or <I>IPv6</I>.  Given an envelope sender
+          domain name or a domain name in a URL of spam.domain.org and a
+          blacklist of type <I>name</I>, spam.domain.org.example.com will be tried.
+          Blacklist types of <I>IPv4</I> and <I>IPv6</I> require that the domain name in a
+          URL sender address be resolved into an IPv4 or IPv6 address.  The
+          address is then written as a reversed string of decimal octets to
+          check the DNS blacklist, as in 2.0.0.127.example.com,
+
+          More than one blacklist can be specified and blacklists can be
+          grouped.  All searching within a group is stopped at the first posi-
+          tive result.
+
+          Positive results are ignored after being logged unless an
+          <I>option</I> <I>DNSBL-on</I> line appears in the global or per-user <I>whiteclnt</I>
+          file.
+
+          <B>-B</B> <I>set:no-client</I>
+               says that SMTP client IP addresses and reverse DNS domain names
+               should not be checked in the following blacklists.
+               <B>-B</B> <I>set:client</I> restores the default for the following black-
+               lists.
+
+          <B>-B</B> <I>set:no-mail</I><B>_</B><I>host</I>
+               says that SMTP envelope Mail_From sender domain names should
+               not be checked in the following blacklists.  <B>-B</B> <I>set:mail</I><B>_</B><I>host</I>
+               restores the default.
+
+          <B>-B</B> <I>set:no-URL</I>
+               says that URLs in the message body should not be checked in the
+               in the following blacklists.  <B>-B</B> <I>set:URL</I> restores the default.
+
+          <B>-B</B> <I>set:no-MX</I>
+               says MX servers of sender Mail_From domain names and host names
+               in URLs should not be checked in the following blacklists.
+               <B>-B</B> <I>set:MX</I> restores the default.
+
+          <B>-B</B> <I>set:no-NS</I>
+               says DNS servers of sender Mail_From domain names and host
+               names in URLs should not be checked in the following black-
+               lists.  <B>-B</B> <I>set:NS</I> restores the default.
+
+          <B>-B</B> <I>set:defaults</I>
+               is equivalent to all of <B>-B</B> <I>set:no-temp-fail</I> <B>-B</B> <I>set:client</I>
+               <B>-B</B> <I>set:mail</I><B>_</B><I>host</I> <B>-B</B> <I>set:URL</I> <B>-B</B> <I>set:MX</I> and <B>-B</B> <I>set:NS</I>
+
+          <B>-B</B> <I>set:group=X</I>
+               adds later DNS blacklists specified with
+                   <B>-B</B> <I>domain</I>[<I>,IPaddr</I>[<I>/xx</I>[<I>,bltype</I>]]]
+               to group 1, 2, or 3.
+
+          <B>-B</B> <I>set:debug=X</I>
+               sets the DNS blacklist logging level
+
+          <B>-B</B> <I>set:msg-secs=S</I>
+               limits <B>dccm</B> to <I>S</I> seconds total for checking all DNS blacklists.
+               The default is 25.
+
+          <B>-B</B> <I>set:URL-secs=S</I>
+               limits <B>dccm</B> to at most <I>S</I> seconds resolving and checking any
+               single URL.  The default is 11.  Some spam contains dozens of
+               URLs and that some "spamvertised" URLs contain host names that
+               need minutes to resolve.  Busy mail systems cannot afford to
+               spend minutes checking each incoming mail message.
+
+          <B>-B</B> <I>set:rej-msg=rejection-msg</I>
+               sets the SMTP rejection message for the following blacklists.
+               <I>Rejection-msg</I> must be in the same format as for <B>-r</B>.  If
+               <I>rejection-msg</I> is null, the default is restored.  The default
+               DNS blacklist rejection message is the first message set with
+               <B>-r</B>.
+
+          <B>-B</B> <I>set:temp-fail</I>
+               causes <B>dccm</B> to the MTA to answer the SMTP DATA command with
+                  452 4.2.1 mail %ID from %CIP temporary delayed for DNSBL
+               if any DNS answer required for a DNSBL in the current group
+               times out, including resolving names in URLs.
+
+          <B>-B</B> <I>set:no-temp-fail</I>
+               restores the default of assuming a negative answer for DNS
+               responses that take too long.
+
+          <B>-B</B> <I>set:maxjobs=X</I>
+               sets maximum number of helper processes to <I>X</I>.  In order to use
+               typical single-threaded DNS resolver libraries, <B>dccm</B> uses
+               fleets of helper processes.  It is rarely a good idea to change
+               the default, which is the same as the maximum number of simul-
+               taneous jobs set with <B>-j</B>.
+
+          <B>-B</B> <I>set:progpath=@libexecdir@/dns-helper</I>
+               changes the path to the helper program.
+
+     <A NAME="OPTION-L"><B>-L</B></A> <I>ltype,facility.level</I>
+          specifies how messages should be logged.  <I>Ltype</I> must be <I>error</I>, <I>info</I>,
+          or <I>off</I> to indicate which of the two types of messages are being con-
+          trolled or to turn off all <B>syslog(3)</B> messages from <B>dccm</B>.  <I>Level</I> must
+          be a <B>syslog(3)</B> level among <I>EMERG</I>, <I>ALERT</I>, <I>CRIT</I>, <I>ERR</I>, <I>WARNING</I>, <I>NOTICE</I>,
+          <I>INFO</I>, and <I>DEBUG</I>.  <I>Facility</I> must be among <I>AUTH</I>, <I>AUTHPRIV</I>, <I>CRON</I>,
+          <I>DAEMON</I>, <I>FTP</I>, <I>KERN</I>, <I>LPR</I>, <I>MAIL</I>, <I>NEWS</I>, <I>USER</I>, <I>UUCP</I>, and <I>LOCAL0</I> through
+          <I>LOCAL7</I>.  The default is equivalent to
+                <B>-L</B> <I>info,MAIL.NOTICE</I> <B>-L</B> <I>error,MAIL.ERR</I>
+
+     <B>dccm</B> normally sends counts of mail rejected and so forth the to system
+     log at midnight.  The SIGUSR1 signal sends an immediate report to the
+     system log.  They will be repeated every 24 hours instead of at midnight.
+
+
+</PRE>
+<H2><A NAME="SENDMAIL-MACROS">SENDMAIL MACROS</A></H2><PRE>
+     Sendmail can affect <B>dccm</B> with the values of some <I>sendmail.cf</I> macros.
+     These macro names must be added to the Milter.macros option statements in
+     <I>sendmail.cf</I> as in the example "Feature" file dcc.m4.
+
+     <I>${dcc</I><B>_</B><I>isspam}</I>  causes a mail message to be reported to the DCC server as
+                    having been addressed to "MANY" recipients.  The
+                    <I>${dcc</I><B>_</B><I>isspam}</I> macro is ignored if the <I>${dcc</I><B>_</B><I>notspam}</I> macro
+                    is set to a non-null string
+
+                    If the value of the <I>${dcc</I><B>_</B><I>isspam}</I> is null, <B>dccm</B> uses SMTP
+                    rejection messages controlled by <B>-a</B> and <B>-r</B>.  If the value
+                    of the <I>${dcc</I><B>_</B><I>isspam}</I> macro starts with "DISCARD", the mail
+                    message is silently discarded as with <B>-a</B> <I>DISCARD.</I> If value
+                    of the macro not null and does not start with "DISCARD",
+                    it is used as the SMTP error message given to the SMTP
+                    client trying to send the rejected message.  The message
+                    starts with an optional SMTP error type and number fol-
+                    lowed by text.
+
+                    The <B>-a</B> option does not effect messages marked spam with
+                    <I>${dcc</I><B>_</B><I>isspam}</I>.  When the <I>${dcc</I><B>_</B><I>isspam}</I> macro is set, the
+                    message is rejected or discarded despite local or DCC
+                    database whitelist entries.  The local whitelist does con-
+                    trol whether the message's checksums will be reported to
+                    the DCC server and an <I>X-DCC</I> SMTP header line will be
+                    added.
+
+     <I>${dcc</I><B>_</B><I>notspam}</I>
+                    causes a message not be considered unsolicited bulk
+                    despite evidence to the contrary.  It also prevents <B>dccm</B>
+                    from reporting the checksums of the message to the DCC
+                    server and from adding an <I>X-DCC</I> header line.
+
+                    When the macro is set by the <I>sendmail.cf</I> rules,
+                    <I>${dcc</I><B>_</B><I>notspam}</I> macros overrides DCC threshlds that say the
+                    message should be rejected as well as the effects of the
+                    <I>${dcc</I><B>_</B><I>isspam}</I> macro.
+
+     <I>${dcc</I><B>_</B><I>mail</I><B>_</B><I>host}</I>
+                    specifies the name of the SMTP client that is sending the
+                    message.  This macro is usually the same as the <I>mail</I><B>_</B><I>host</I>
+                    macro.  They can differ when a sendmail "smart relay" is
+                    involved.  The <I>${dcc</I><B>_</B><I>mail</I><B>_</B><I>host}</I> macro does not work if
+                    <I>FEATURE(delay</I><B>_</B><I>checks)</I> is used.
+
+     <I>${dcc</I><B>_</B><I>userdir}</I>
+                    is the per-user whitelist and log directory for a recipi-
+                    ent.  If the macro is not set in sendmail.cf,
+                    $&amp;{rcpt_mailer}/$&amp;{rcpt_addr} is assumed, but with the
+                    recipient address converted to lower case.  Whatever value
+                    is used, the directory name after the last slash (/) char-
+                    acter is converted to lower case.  Any value containing
+                    the string "/../" is ignored.
+
+                    This macro also does not work if <I>FEATURE(delay</I><B>_</B><I>checks)</I> is
+                    used.
+
+                    The following two lines in a sendmail mc file have the
+                    same effect as not defining the ${dcc_userdir} macro, pro-
+                    vided <I>FEATURE(dcc)</I> is also used and the sendmail
+                    <I>cf/feature</I> directory has a symbolic link to the
+                    <I>misc/dcc.m4</I> file.
+
+     SLocal_check_rcpt
+     R$*     $: $1 $(macro {dcc_userdir} $@ $&amp;{rcpt_mailer}/$&amp;{rcpt_addr} $))
+
+
+</PRE>
+<H2><A NAME="FILES">FILES</A></H2><PRE>
+     <A NAME="FILE-@prefix@">@prefix@</A>   is the DCC home directory in which other files are found.
+     <A NAME="FILE-@libexecdir@/start">@libexecdir@/start</A>-dccm
+                is a script used to <B>dccm</B>.
+     <A NAME="FILE-dcc/dcc_conf">dcc/dcc_conf</A>
+                contains parameters used by the scripts to start DCC daemons
+                and cron jobs.
+     <A NAME="FILE-logdir">logdir</A>     is an optional directory specified with <B>-l</B> and containing
+                marked mail.  Each file in the directory contains one message,
+                at least one of whose checksums reached its <B>-t</B> thresholds or
+                that is interesting for some other reason.  Each file starts
+                with lines containing the date when the message was received,
+                the IP address of the SMTP client, and SMTP envelope values.
+                Those lines are followed by the body of the SMTP message
+                including its header as it was received by sendmail and with-
+                out any new or changed header lines.  Only approximately the
+                first 32 KBytes of the body are recorded unless modified by
+                <I>./configure</I> <I>--with-max-log-size=xx</I> The checksums for the mes-
+                sage follow the body.  They are followed by lines indicating
+                that the <I>${dcc</I><B>_</B><I>isspam}</I> or <I>${dcc</I><B>_</B><I>notspam}</I> <I>sendmail.cf</I> macros
+                were set or one of the checksums is white- or blacklisted by
+                the <B>-w</B> <I>whiteclnt</I> file.  Each file ends with the <I>X-DCC</I> header
+                line added to the message and the disposition of the message
+                including SMTP status message if appropriate.
+     <A NAME="FILE-map">map</A>        is the memory mapped file of information concerning DCC
+                servers in the DCC home directory.
+     <A NAME="FILE-whiteclnt">whiteclnt</A>  contains the client whitelist in the format described in
+                <B><A HREF="dcc.html">dcc(8)</A></B>.
+     <A NAME="FILE-whiteclnt.dccw">whiteclnt.dccw</A>
+                is a memory mapped hash table of the <I>whiteclnt</I> file.
+     <A NAME="FILE-dccm.pid">dccm.pid</A>   in the <B>-R</B> <I>rundir</I> directory contains daemon's process ID.  The
+                string ``dccm'' is replaced by the file name containing the
+                daemon to facilitate running multiple daemons, probably con-
+                nected to remote instances of sendmail using TCP/IP instead of
+                a UNIX domain socket.  See also <B>-R</B>.
+     <A NAME="FILE-@dcc_rundir@/dccm">@dcc_rundir@/dccm</A>
+                is the default UNIX domain socket used by the sendmail milter
+                interface.  See also <B>-R</B>.
+     <A NAME="FILE-sendmail.cf">sendmail.cf</A>
+                is the <B>sendmail(8)</B> control file.
+     <A NAME="FILE-misc/dcc.m4">misc/dcc.m4</A>
+                sendmail mc file that should have a symbolic link in the send-
+                mail cf/feature directory so that <I>FEATURE(dcc)</I> can be used in
+                a sendmail mc file.
+
+
+</PRE>
+<H2><A NAME="EXAMPLES">EXAMPLES</A></H2><PRE>
+     <B>Dccm</B> should be started before sendmail with something like the script
+     <I>@libexecdir@/start-dccm.</I> It looks for common DCC parameters in the
+     <I>dcc</I><B>_</B><I>conf</I> file in the DCC home directory, <I>@prefix@.</I>
+
+     Those numbers should modified to fit local conditions.  It might be wise
+     to replace the "100" numbers with much larger values or with "MANY" until
+     a few weeks of monitoring the log directory show that sources of mailing
+     lists are in the server's whitelist file (see <B><A HREF="dccd.html">dccd(8)</A></B>) or the local
+     <I>whiteclnt</I> file.
+
+     It is usually necessary to regularly delete old log files with a script
+     like @libexecdir@/cron-dccd.
+
+     On systems unlike modern FreeBSD and other UNIX-like systems which
+     include sendmail milter support, sendmail must be built with the milter
+     interface, such as by creating a <I>devtools/Site/site.config.m4</I> or similar
+     file containing something like the following lines:
+
+           APPENDDEF(`conf_sendmail_ENVDEF', `-D_FFR_MILTER=1')
+           APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_MILTER=1')
+
+     Appropriate lines invoking the milter interface must be added to
+     <I>sendmail.cf.</I> That can be done by putting a symbolic link to the the
+     misc/dcc.m4 file in the DCC source to the sendmail cf/feature directory
+     and adding the line
+
+           FEATURE(dcc)
+
+     to the local .mc file.
+
+     Note that <B>dccm</B> should not be used with the Postfix milter mechanism.
+     Instead use <B><A HREF="dccifd.html">dccifd(8)</A></B> as a before-queue filter as described in that man
+     page.
+
+
+</PRE>
+<H2><A NAME="SEE-ALSO">SEE ALSO</A></H2><PRE>
+     <B><A HREF="cdcc.html">cdcc(8)</A></B>, <B><A HREF="dbclean.html">dbclean(8)</A></B>, <B><A HREF="dcc.html">dcc(8)</A></B>, <B><A HREF="dccd.html">dccd(8)</A></B>, <B><A HREF="dblist.html">dblist(8)</A></B>, <B><A HREF="dccifd.html">dccifd(8)</A></B>, <B><A HREF="dccproc.html">dccproc(8)</A></B>,
+     <B><A HREF="dccsight.html">dccsight(8)</A></B>, <B>sendmail(8)</B>.
+
+
+</PRE>
+<H2><A NAME="HISTORY">HISTORY</A></H2><PRE>
+     Distributed Checksum Clearinghouses are based on an idea of Paul Vixie.
+     Implementation of <B>dccm</B> was started at Rhyolite Software in 2000.  This
+     document describes version 1.3.103.
+
+
+</PRE>
+<H2><A NAME="BUGS">BUGS</A></H2><PRE>
+     <B>dccm</B> uses <B>-t</B> where <B><A HREF="dccproc.html">dccproc(8)</A></B> uses <B>-c</B>.
+
+     Systems without <B>setrlimit(2)</B> and <B>getrlimit(2)</B> RLIMIT_NOFILE can have
+     problems with the default limit on the number of simultaneous jobs, the
+     value of <B>-j</B>.  Every job requires four open files.  These problems are
+     usually seen with errors messages that say something like
+           dccm[24448]: DCC: accept() returned invalid socket
+     A fix is to use a smaller value for <B>-j</B> or to allow <B>dccm</B> to open more
+     files.  Sendmail version 8.13 and later can be told to poll() instead of
+     select with SM_CONF_POLL.  Some older versions of sendmail knew about
+     FFR_USE_POLL.  One of the following lines in your devtools/Site/site.con-
+     fig.m4 file can help:
+
+           APPENDDEF(`conf_libmilter_ENVDEF', `-DSM_CONF_POLL')
+           APPENDDEF(`conf_libmilter_ENVDEF', `-DFFR_USE_POLL')
+
+     On many systems with sendmail 8.11.3 and preceding, a bug in the sendmail
+     milter mechanism causes <B>dccm</B> to die with a core file when given a signal.
+
+                               February 26, 2009
+</PRE>
+<HR>
+<ADDRESS>
+Man(1) output converted with
+<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
+modified for the DCC $Date 2001/04/29 03:22:18 $
+<BR>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG SRC="http://logos.dcc-servers.net/border.png"
+            class=logo ALT="DCC logo">
+    </A>
+<A HREF="http://validator.w3.org/check?uri=referer">
+    <IMG class=logo ALT="Valid HTML 4.01 Strict"
+        SRC="http://www.w3.org/Icons/valid-html401">
+    </A>
+</ADDRESS>
+</BODY>
+</HTML>
diff -r 000000000000 -r c7f6b056b673 dccm/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccm/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,3 @@
+Makefile.in
+dccm.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 dccm/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccm/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,53 @@
+# make the Distributed Checksum Clearinghouse sendmail milter interface
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.25 $Revision$
+# @configure_input@
+
+DEPTH	=..
+PROG	=dccm
+SRCS	=$(PROG).c
+
+SENDMAIL_LIB=@SENDMAIL_LIB@
+
+CFLAGS	+=$(THRINC) @SENDMAIL_INC@
+LDADD	+=$(SENDMAIL_LIB) $(THR_LDADD)
+DPADD	+=$(SENDMAIL_LIB) $(THR_DPADD)
+LDFLAGS	+=@PTHREAD_LDFLAGS@
+
+DCC_BINDIR=@installroot@@libexecdir@
+@MAKE_PROG@
+@MAKE_INC2@
diff -r 000000000000 -r c7f6b056b673 dccm/dccm.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccm/dccm.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,1506 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * sendmail milter interface
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.238 $Revision$
+ */
+
+#include "libmilter/mfapi.h"
+#include "cmn_defs.h"
+
+#undef NEW_MFAPI
+#ifdef SM_LM_VRS_MAJOR
+#if SM_LM_VRS_MAJOR(SMFI_VERSION) >= 1
+#define NEW_MFAPI
+#endif
+#endif
+
+u_char cannot_discard = 0;		/* can trim targets after DATA */
+u_char cannot_reject = 0;
+
+static u_char background = 1;
+static DCC_PATH pidpath;
+
+static const char *progpath = DCC_LIBEXECDIR"/dccm";
+
+static DCC_PATH conn_def;
+static char *milter_conn = conn_def;	/* MILTER socket specification */
+
+static char sm_isspam_macro_def[] = "{dcc_isspam}";
+static char *sm_isspam_macro = sm_isspam_macro_def;
+static char sm_notspam_macro_def[] = "{dcc_notspam}";
+static char *sm_notspam_macro = sm_notspam_macro_def;
+
+/* DCC-milter state or context */
+typedef struct work {
+    SMFICTX	*milter_ctx;
+#    define	 WORK_MILTER_CTX_IDLE ((SMFICTX *)DCC_SRVR_PORT)
+    CMN_WORK	cw;
+#    define	 NUM_XHDRS 5
+    struct {				/* existing X-DCC headers */
+	u_char	    num;
+	u_char	    len;
+	char	    brand[DCC_BRAND_MAXLEN];
+    } xhdrs[NUM_XHDRS];
+    REPLY_TPLT	sendmail_reply;
+    /* from here down is zeroed when the structure is allocated */
+#define WORK_ZERO fwd
+    struct work *fwd;
+    /* from here down is zeroed when the structure is used for a 2nd msg */
+#define WORK_REZERO num_x_dcc
+    u_char	num_x_dcc;
+} WORK;
+
+#define WORK_EXCESS ((WORK *)1)
+
+
+/* use a free list to avoid malloc() overhead */
+static WORK *work_free;
+static int work_too_many;
+static time_t work_msg_time;
+
+/* each dccm job involves
+ *	a socket connected to sendmail,
+ *	a log file,
+ *	and a socket to talk to the DCC server.
+ * The file descriptors for the whitelists are accounted for in EXTRA_FILES */
+#define FILES_PER_JOB	3
+int max_max_work = MAX_SELECT_WORK;
+
+
+static sfsistat dccm_conn(SMFICTX *, char *, _SOCK_ADDR *);
+static sfsistat dccm_helo(SMFICTX *, char *);
+static sfsistat dccm_envfrom(SMFICTX *, char **);
+static sfsistat dccm_envrcpt(SMFICTX *, char **);
+static sfsistat dccm_header(SMFICTX *, char *, char *);
+static sfsistat dccm_eoh(SMFICTX *);
+static sfsistat dccm_body(SMFICTX *, u_char *, size_t);
+static sfsistat dccm_eom(SMFICTX *);
+static sfsistat dccm_abort(SMFICTX *);
+static sfsistat dccm_close(SMFICTX *);
+#ifdef NEW_MFAPI
+static sfsistat dccm_negotiate(SMFICTX *, unsigned long, unsigned long,
+			       unsigned long, unsigned long,
+			       unsigned long *, unsigned long *,
+			       unsigned long *, unsigned long *);
+#endif
+
+static char dccm_name[] = {"DCC"};
+static struct smfiDesc smfilter = {
+    dccm_name,				/* filter name */
+    SMFI_VERSION,			/* version code -- do not change */
+    SMFIF_CHGHDRS | SMFIF_ADDHDRS | SMFIF_DELRCPT,  /* flags */
+    dccm_conn,				/* connection info filter */
+    dccm_helo,				/* SMTP HELO command filter */
+    dccm_envfrom,			/* envelope sender filter */
+    dccm_envrcpt,			/* envelope recipient filter */
+    dccm_header,			/* header filter */
+    dccm_eoh,				/* end of header */
+    dccm_body,				/* body block filter */
+    dccm_eom,				/* end of message */
+    dccm_abort,				/* message aborted */
+    dccm_close,				/* connection finished */
+#ifdef NEW_MFAPI
+    0,					/* unknown SMTP command */
+    0,					/* xxfi_data */
+    dccm_negotiate,			/* negotiate new milter options */
+#endif
+};
+
+
+static REPLY_TPLT too_many_reply = {
+	DCC_XHDR_TOO_MANY_RCPTS, {REPLY_TPLT_NULL},
+	"452", "4.5.3", 0, DCC_XHDR_TOO_MANY_RCPTS};
+
+static REPLY_TPLT incompat_white_reply = {
+	DCC_XHDR_INCOMPAT_WLIST, {REPLY_TPLT_NULL},
+	"452", "4.5.3", 0, DCC_XHDR_INCOMPAT_WLIST};
+
+
+static char *add_braces(const char *);
+static void del_sock(void);
+static void add_work(int);
+
+
+static void
+usage(const char* barg, const char *bvar)
+{
+	const char str[] = {
+	    "usage: [-VdbxANQ] [-G on | off | noIP | IPmask/xx] [-h homedir]"
+	    " [-I user]\n"
+	    "    [-p protocol:filename | protocol:port@host] [-m map]\n"
+	    "    [-w whiteclnt] [-U userdirs] [-a IGNORE | REJECT | DISCARD]\n"
+	    "    [-t type,[log-thold,][spam-thold]]"
+	    " [-g [not-]type] [-S header]\n"
+	    "    [-l logdir] [-R rundir] [-r rejection-msg] [-j maxjobs]\n"
+	    "    [-B dnsbl-option] [-L ltype,facility.level]"
+	};
+	static u_char complained;
+
+	if (!complained) {
+		if (barg)
+			dcc_error_msg("unrecognized \"%s%s\"\n%s\n..."
+				      " continuing",
+				      barg, bvar, str);
+		else
+			dcc_error_msg("%s\n... continuing", str);
+		complained = 1;
+	}
+}
+
+
+int NRATTRIB
+main(int argc, char **argv)
+{
+	DCC_EMSG emsg;
+#ifdef RLIMIT_NOFILE
+	struct rlimit nofile;
+	int old_rlim_cur;
+#endif
+	long l;
+	u_char log_tgts_set = 0;
+	time_t smfi_main_start;
+	char *p;
+	const char *rundir = DCC_RUNDIR;
+	const char *homedir = 0;
+	const char *logdir = 0;
+	int result, i;
+
+	emsg[0] = '\0';
+	if (*argv[0] == '/')
+		progpath = argv[0];
+	dcc_syslog_init(1, argv[0], 0);
+	dcc_clear_tholds();
+
+#ifdef RLIMIT_NOFILE
+	if (0 > getrlimit(RLIMIT_NOFILE, &nofile)) {
+		dcc_error_msg("getrlimit(RLIMIT_NOFILE): %s", ERROR_STR());
+		old_rlim_cur = 1000*1000;
+	} else {
+		old_rlim_cur = nofile.rlim_cur;
+		if (nofile.rlim_max < 1000*1000) {
+			i = nofile.rlim_max;
+#ifndef USE_POLL
+			if (i > FD_SETSIZE)
+				i = FD_SETSIZE;
+#endif
+			max_max_work = (i - EXTRA_FILES)/FILES_PER_JOB;
+			max_max_work_src = "RLIMIT_NOFILE limit";
+		}
+	}
+#endif /* RLIMIT_NOFILE */
+	if (max_max_work <= 0) {
+		dcc_error_msg("too few open files allowed");
+		max_max_work = MIN_MAX_WORK;
+	}
+	max_work = max_max_work;
+
+#define SLARGS "VdbxANQW"		/* change start-dccm if these change */
+	while (EOF != (i = getopt(argc, argv, SLARGS"G:h:I:"
+				  "p:m:w:U:a:t:g:S:l:R:r:s:o:j:B:L:"))) {
+		switch (i) {
+		case 'V':
+			fprintf(stderr, DCC_VERSION"\n");
+			exit(EX_OK);
+			break;
+
+		case 'd':
+			++dcc_clnt_debug;
+			break;
+
+		case 'b':
+			background = 0;
+			break;
+
+		case 'x':
+			try_extra_hard = DCC_CLNT_FG_NO_FAIL;
+			break;
+
+		case 'A':
+			chghdr = ADDHDR;
+			smfilter.xxfi_flags &= ~SMFIF_CHGHDRS;
+			smfilter.xxfi_flags |= SMFIF_ADDHDRS;
+			break;
+
+		case 'N':
+			chghdr = NOHDR;
+			smfilter.xxfi_flags &= ~(SMFIF_ADDHDRS | SMFIF_CHGHDRS);
+			break;
+
+		case 'Q':
+			dcc_query_only = 1;
+			break;
+
+		case 'W':		/* obsolete DCC off by default */
+			to_white_only = 1;
+			break;
+
+		case 'G':
+			if (!dcc_parse_client_grey(optarg))
+				usage("-G", optarg);
+			break;
+
+		case 'h':
+			homedir = optarg;
+			break;
+
+		case 'I':
+			dcc_daemon_su(optarg);
+			break;
+
+		case 'p':
+			milter_conn = optarg;
+			break;
+
+		case 'm':
+			mapfile_nm = optarg;
+			break;
+
+		case 'w':
+			main_white_nm = optarg;
+			break;
+
+		case 'U':
+			parse_userdirs(optarg);
+			break;
+
+		case 'a':
+			if (!strcasecmp(optarg, "IGNORE")) {
+				action = CMN_IGNORE;
+			} else if (!strcasecmp(optarg, "REJECT")) {
+				action = CMN_REJECT;
+			} else if (!strcasecmp(optarg, "DISCARD")) {
+				action = CMN_DISCARD;
+			} else {
+				dcc_error_msg("unrecognized -a action: %s",
+					      optarg);
+			}
+			break;
+
+		case 't':
+			if (dcc_parse_tholds("-t ", optarg))
+				log_tgts_set = 1;
+			break;
+
+		case 'g':		/* honor not-spam "counts" */
+			dcc_parse_honor(optarg);
+			break;
+
+		case 'S':
+			dcc_add_sub_hdr(0, optarg);
+			break;
+
+		case 'l':		/* log rejected mail here */
+			logdir = optarg;
+			break;
+
+		case 'R':
+			rundir = optarg;
+			break;
+
+		case 'r':
+			parse_reply_arg(optarg);
+			break;
+
+		case 's':		/* deprecated: set dcc_isspam */
+			sm_isspam_macro = add_braces(optarg);
+			break;
+
+		case 'o':		/* deprecated: set dcc_notspam */
+			sm_notspam_macro = add_braces(optarg);
+			break;
+
+		case 'j':		/* maximum simultaneous jobs */
+			l = strtoul(optarg, &p, 10);
+			if (*p != '\0' || l < MIN_MAX_WORK) {
+				dcc_error_msg("invalid queue length %s",
+					      optarg);
+			} else if (l > max_max_work) {
+				dcc_error_msg("-j queue length %s"
+					      " larger than %s; using %d",
+					      optarg,
+					      max_max_work_src, max_max_work);
+				max_work = max_max_work;
+			} else {
+				max_work = l;
+			}
+			break;
+
+		case 'B':
+			if (!dcc_parse_dnsbl(emsg, optarg, progpath, 0))
+				dcc_error_msg("%s", emsg);
+			break;
+
+		case 'L':
+			if (dcc_parse_log_opt(optarg))
+				helper_save_arg("-L", optarg);
+			break;
+
+		default:
+			usage(optopt2str(optopt), "");
+		}
+	}
+	if (argc != optind)
+		usage(argv[optind], "");
+
+	snprintf(conn_def, sizeof(conn_def), "%s/%s", rundir, dcc_progname);
+
+	dcc_cdhome(emsg, homedir, 0);
+	dcc_main_logdir_init(0, logdir);
+	if (dcc_main_logdir[0] == '\0') {
+		/* if not logging,
+		 * tell sendmail to not bother with some stuff */
+		smfilter.xxfi_helo = 0;
+
+		if (log_tgts_set)
+			dcc_error_msg("log thresholds set with -t"
+				      " but no -l directory");
+		if (userdirs != '\0')
+			dcc_error_msg("no -l directory prevents per-user"
+				      " logging with -U");
+	}
+
+
+#ifdef RLIMIT_NOFILE
+	i = max_work*FILES_PER_JOB+EXTRA_FILES;
+	if (old_rlim_cur < i) {
+		nofile.rlim_cur = i;
+		if (0 > setrlimit(RLIMIT_NOFILE, &nofile)) {
+			dcc_error_msg("setrlimit(RLIMIT_NOFILE,%d): %s",
+				      i, ERROR_STR());
+			max_work = old_rlim_cur/FILES_PER_JOB - EXTRA_FILES;
+			if (max_work <= 0) {
+				dcc_error_msg("only %d open files allowed"
+					      " by RLIMIT_NOFILE",
+					      old_rlim_cur);
+				max_work = MIN_MAX_WORK;
+			}
+		}
+	}
+#endif /* RLIMIT_NOFILE */
+
+	helper_init(max_work);
+
+	if (MI_SUCCESS != smfi_setconn(milter_conn))
+		dcc_logbad(EX_USAGE, "illegal sendmail connection"
+			   " \"%s\"\n", optarg);
+
+	del_sock();
+
+	if (smfi_register(smfilter) == MI_FAILURE)
+		dcc_logbad(EX_UNAVAILABLE, "smfi_register failed\n");
+
+	if (background) {
+		if (daemon(1, 0) < 0)
+			dcc_logbad(EX_OSERR, "daemon(): %s", ERROR_STR());
+
+		dcc_daemon_restart(rundir, del_sock);
+		dcc_pidfile(pidpath, rundir);
+	}
+	/* Be careful to start all threads only after the fork() in daemon(),
+	 * because some POSIX threads packages (e.g. FreeBSD) get confused
+	 * about threads in the parent.  */
+
+	cmn_init();
+	add_work(init_work);
+
+	dcc_trace_msg(DCC_VERSION" listening to %s with %s",
+		      milter_conn, dcc_homedir);
+	if (dcc_clnt_debug)
+		dcc_trace_msg("init_work=%d max_work=%d max_max_work=%d (%s)",
+			      total_work, max_work, max_max_work,
+			      max_max_work_src);
+
+	/* It would be nice to remove the UNIX domain socket and PID file
+	 * when smfi_main() returns, but we dare not because the library
+	 * delays for several seconds after being signalled to stop.
+	 * Our files might have been unlinked and the files now in
+	 * the filesystem might belong to some other process. */
+	smfi_main_start = time(0);
+	result = smfi_main();
+
+	if (pidpath[0] != '\0')
+		unlink(pidpath);
+
+	totals_stop();
+
+	/* The sendmail libmilter machinery sometimes gets confused and
+	 * gives up.  Try to start over if we had been running for at least
+	 * 10 minutes */
+	if (result != MI_SUCCESS
+	    && time(0) > smfi_main_start+10*60) {
+		dcc_error_msg("try to restart after smfi_main() = %d", result);
+		exit(EX_DCC_RESTART);
+	}
+
+	if (result != MI_SUCCESS)
+		dcc_error_msg("smfi_main() = %d", result);
+	exit((result == MI_SUCCESS) ? EX_OK : EX_UNAVAILABLE);
+}
+
+
+
+static char *
+add_braces(const char *s)
+{
+	int i;
+	char *new;
+
+	i = strlen(s);
+	if (i >= 2 && s[0] == '{' && s[i-1] == '}')
+		return strdup(s);
+	new = dcc_malloc(i+3);
+	new[0] = '{';
+	memcpy(new+1, s, i);
+	new[i+1] = '}';
+	new[i+2] = '\0';
+	return new;
+}
+
+
+
+/* remove the Unix domain socket of a previous instance of this daemon */
+static void
+del_sock(void)
+{
+	int s;
+	struct stat sb;
+	const char *conn;
+	struct sockaddr_un conn_sun;
+	int len, i;
+
+	/* Ignore the sendmail milter "local|whatever:" prefix.
+	 * If it is a UNIX domain socket, fine.  If not, no harm is done */
+	conn = strchr(milter_conn, ':');
+	if (conn)
+		++conn;
+	else
+		conn = milter_conn;
+
+	len = strlen(conn);
+	if (len >= ISZ(conn_sun.sun_path))
+		return;			/* perhaps not a UNIX domain socket */
+
+	memset(&conn_sun, 0, sizeof(conn_sun));
+	conn_sun.sun_family = AF_LOCAL;
+	strcpy(conn_sun.sun_path, conn);
+#ifdef HAVE_SA_LEN
+	conn_sun.sun_len = SUN_LEN(&conn_sun);
+#endif
+
+	if (0 > stat(conn_sun.sun_path, &sb))
+		return;
+	if (!(S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode)))
+		dcc_logbad(EX_UNAVAILABLE, "non-socket present at %s",
+			   conn_sun.sun_path);
+
+	/* The sendmail libmilter seems to delay as long as 5 seconds
+	 * before stopping.  It delays indefinitely if an SMTP client
+	 * is stuck. */
+	i = 0;
+	for (;;) {
+		s = socket(AF_UNIX, SOCK_STREAM, 0);
+		if (s < 0) {
+			dcc_logbad(EX_OSERR, "socket(AF_UNIX): %s",
+				   ERROR_STR());
+			return;
+		}
+		if (++i > 5*10)
+			dcc_logbad(EX_UNAVAILABLE,
+				   "DCCM or something already or still running"
+				   " with socket at %s",
+				   conn_sun.sun_path);
+		if (0 > connect(s, (struct sockaddr *)&conn_sun,
+				sizeof(conn_sun))) {
+			/* unlink it only if it looks like a dead socket */
+			if (errno == ECONNREFUSED || errno == ECONNRESET
+			    || errno == EACCES) {
+				if (0 > unlink(conn_sun.sun_path))
+					dcc_error_msg("unlink(old %s): %s",
+						      conn_sun.sun_path,
+						      ERROR_STR());
+			} else {
+				dcc_error_msg("connect(old %s): %s",
+					      conn_sun.sun_path, ERROR_STR());
+			}
+			close(s);
+			break;
+		}
+		close(s);
+		usleep(100*1000);
+	}
+}
+
+
+
+/* create some contexts. */
+static void
+add_work(int i)
+{
+	WORK *wp;
+
+	total_work += i;
+
+	wp = dcc_malloc(sizeof(*wp)*i);
+	memset(wp, 0, sizeof(*wp)*i);
+
+	while (i-- != 0) {
+		wp->milter_ctx = WORK_MILTER_CTX_IDLE;
+		cmn_create(&wp->cw);
+		wp->fwd = work_free;
+		work_free = wp;
+		++wp;
+	}
+}
+
+
+
+static WORK *
+work_alloc(void)
+{
+	WORK *wp;
+
+	lock_work();
+	wp = work_free;
+	if (!wp) {
+		if (total_work > max_work) {
+			++work_too_many;
+			unlock_work();
+			return 0;
+		}
+		if (dcc_clnt_debug > 1)
+			dcc_trace_msg("add %d work blocks to %d",
+				      init_work, total_work);
+		add_work(init_work);
+		wp = work_free;
+	}
+	if (wp->milter_ctx != WORK_MILTER_CTX_IDLE)
+		dcc_logbad(EX_SOFTWARE, "corrupt WORK area");
+	work_free = wp->fwd;
+	unlock_work();
+
+	/* clear most of it */
+	cmn_clear(&wp->cw, wp, 1);
+	wp->cw.helo[0] = '\0';
+	memset(&wp->WORK_ZERO, 0,
+	       sizeof(*wp) - ((char*)&wp->WORK_ZERO - (char*)wp));
+
+	return wp;
+}
+
+
+
+/* ocassionally close sockets to recover from dictionary attacks */
+void
+work_clean(void)
+{
+	WORK *wp;
+	int keep, delete;
+
+	lock_work();
+	keep = 5;
+	delete = init_work;
+	for (wp = work_free; wp; wp = wp->fwd) {
+		if (!wp->cw.dcc_ctxt)
+			break;
+		if (--keep > 0)
+			continue;
+		dcc_clnt_soc_close(wp->cw.dcc_ctxt);
+		if (--delete <= 0)
+			break;
+	}
+	unlock_work();
+}
+
+
+
+typedef enum {GET_WP_START,		/* not yet seen dccm_envfrom() */
+	GET_WP_GOING,			/* have seen dccm_envfrom() */
+	GET_WP_ABORT,			/* dccm_abort() */
+	GET_WP_CLOSE			/* dccm_close() */
+} GET_WP_MODE;
+static WORK *
+get_wp(SMFICTX *milter_ctx,
+       GET_WP_MODE mode)
+{
+	WORK *wp;
+
+	wp = (WORK *)smfi_getpriv(milter_ctx);
+	if (!wp) {
+		/* milter context is not active */
+		if (mode == GET_WP_CLOSE || mode == GET_WP_ABORT)
+			return 0;
+		dcc_logbad(EX_SOFTWARE, "null SMFICTX pointer");
+	} else if (wp == WORK_EXCESS) {
+		if (mode == GET_WP_START || mode == GET_WP_GOING)
+			dcc_logbad(EX_SOFTWARE, "tardy WORK_EXCESS");
+		if (dcc_clnt_debug)
+			dcc_trace_msg("%s for excessive message",
+				      mode == GET_WP_ABORT
+				      ? "abort" : "close");
+		return 0;
+	}
+	if (wp->milter_ctx != milter_ctx)
+		dcc_logbad(EX_SOFTWARE,
+			   "bogus SMFICTX pointer or corrupt WORK area");
+
+	if (!wp->cw.dcc_ctxt && (mode == GET_WP_START || mode == GET_WP_GOING))
+		dcc_logbad(EX_SOFTWARE, "tardy failure to find ctxt");
+
+	if (wp->cw.env_from[0] == '\0' && mode == GET_WP_GOING)
+		dcc_logbad(EX_SOFTWARE, "work cleared?");
+
+	return wp;
+}
+
+
+
+static void
+set_sendmail_reply(WORK *wp,
+		   const char *rcode, const char *xcode, const char *str)
+{
+	int i;
+
+	/* kludge to fix lack of const declaration */
+	typedef int (*SR)(SMFICTX *, const char *, const char *, const char *);
+	static SR sr = (SR)smfi_setreply;
+	i = (*sr)(wp->milter_ctx, rcode, xcode, str);
+
+	if (i != MI_SUCCESS)
+		thr_error_msg(&wp->cw, "smfi_setreply(\"%s\",\"%s\",\"%s\")=%d",
+			      rcode, xcode, str, i);
+}
+
+
+
+/* refuse one recipient */
+static sfsistat
+rcpt_tempfail(WORK *wp, RCPT_ST *rcpt_st, const REPLY_TPLT *tplt)
+{
+	REPLY_STRS strs;
+
+	make_reply(&strs, tplt, &wp->cw, 0);
+	set_sendmail_reply(wp, strs.rcode, strs.xcode, strs.str);
+	wp->cw.ask_st |= ASK_ST_LOGIT;
+	if (rcpt_st) {
+		snprintf(rcpt_st->rej_msg, sizeof(rcpt_st->rej_msg),
+			 "%s %s %s", strs.rcode, strs.xcode, strs.str);
+		rcpt_st->rej_result = strs.log_result;
+		rcpt_st->fgs |= RCPT_FG_REJ_FILTER;
+	}
+	return SMFIS_TEMPFAIL;
+}
+
+
+
+static void
+msg_clear(WORK *wp)
+{
+	cmn_clear(&wp->cw, wp, 0);
+	memset(&wp->WORK_REZERO, 0,
+	       sizeof(*wp) - ((char*)&wp->WORK_REZERO - (char*)wp));
+}
+
+
+
+/* we are finished with one SMTP message.
+ * get ready for the next from the same connection to an SMTP client */
+static void
+msg_done(WORK *wp, const char *result)
+{
+	LOG_CAPTION(wp, DCC_XHDR_RESULT);
+	log_write(&wp->cw, result ? result : DCC_XHDR_RESULT_ACCEPT, 0);
+	LOG_EOL(wp);
+
+	msg_clear(wp);
+}
+
+
+
+/* give up on entire message */
+static sfsistat
+msg_tempfail(WORK *wp, const REPLY_TPLT *tplt)
+{
+	make_reply(&wp->cw.reply, tplt, &wp->cw, 0);
+	set_sendmail_reply(wp, wp->cw.reply.rcode, wp->cw.reply.xcode,
+			   wp->cw.reply.str);
+	log_smtp_reply(&wp->cw);
+	wp->cw.ask_st |= ASK_ST_LOGIT;
+	msg_done(wp, wp->cw.reply.log_result);
+	return SMFIS_TEMPFAIL;
+}
+
+
+
+static sfsistat
+msg_reject(WORK *wp)
+{
+	sfsistat result;
+
+	/* temporize if we have not figured out what to say */
+	if (!wp->cw.reply.log_result) {
+		thr_error_msg(&wp->cw, "rejection reason undecided");
+		make_reply(&wp->cw.reply, &dcc_fail_reply, &wp->cw, 0);
+	}
+
+	set_sendmail_reply(wp, wp->cw.reply.rcode, wp->cw.reply.xcode,
+			   wp->cw.reply.str);
+	log_smtp_reply(&wp->cw);
+
+	result = (wp->cw.reply.rcode[0] == '4') ? SMFIS_TEMPFAIL : SMFIS_REJECT;
+	msg_done(wp, wp->cw.reply.log_result);
+	return result;
+}
+
+
+
+/* see what sendmail had to say about the message */
+static void
+ask_sm(SMFICTX *milter_ctx, WORK *wp)
+{
+	const char *m;
+
+	/* Do this only until we get an answer.
+	 * The sendmail macro might not be set on the first rcpt_to command.
+	 * If the is-spam macro is set before the not-spam macro, then this
+	 * will get the wrong answer.  However, undoing the effects of an
+	 * is-spam setting would be a mess, because they included turning
+	 * off DNSBL checks.  */
+	if ((wp->cw.ask_st & (ASK_ST_MTA_NOTSPAM | ASK_ST_MTA_ISSPAM)) != 0)
+		return;
+
+	if (0 != (m = smfi_getsymval(milter_ctx, sm_notspam_macro))
+	    && *m != '\0') {
+		/* We have a sendmail macro name that indicates a
+		 * whitelisting from sendmail rules and databases,
+		 * and the macro is set. */
+		wp->cw.ask_st |= ASK_ST_MTA_NOTSPAM;
+		wp->cw.ask_st &= ~ASK_ST_MTA_ISSPAM;
+		thr_log_print(&wp->cw, 1,
+			      "sendmail.cf"DCC_XHDR_ISOK": \"%s\"\n", m);
+
+	} else if (!(wp->cw.ask_st & ASK_ST_MTA_ISSPAM)
+		   && 0 != (m = smfi_getsymval(milter_ctx, sm_isspam_macro))
+		   && *m != '\0') {
+		wp->cw.ask_st |= ASK_ST_MTA_ISSPAM;
+
+		make_tplt(&wp->sendmail_reply, 0, DCC_XCODE, DCC_RCODE, m,
+			  DCC_XHDR_RESULT_REJECT);
+
+		thr_log_print(&wp->cw, 1, "sendmail.cf-->%s: \"%s\"\n",
+			      sm_isspam_macro, wp->sendmail_reply.pat);
+
+		make_reply(&wp->cw.reply, &wp->sendmail_reply, &wp->cw, 0);
+
+		if (!CLITCMP(wp->cw.reply.str, "DISCARD")) {
+			wp->cw.reply.str += LITZ("DISCARD");
+			wp->cw.reply.str += strspn(wp->cw.reply.str,
+						   DCC_WHITESPACE":");
+			wp->cw.action = CMN_DISCARD;
+		} else {
+			wp->cw.action = CMN_REJECT;
+		}
+	}
+}
+
+
+
+void
+user_reject_discard(CMN_WORK *cwp, RCPT_ST *rcpt_st)
+{
+	int i;
+
+	/* one of the other targets wants this message,
+	 * try to remove this address from sendmail's list */
+	i = smfi_delrcpt(cwp->wp->milter_ctx, rcpt_st->env_to);
+	if (MI_SUCCESS != i)
+		thr_error_msg(cwp, "delrcpt(%s)=%d", rcpt_st->env_to, i);
+}
+
+
+
+#ifdef NEW_MFAPI
+/* ask sendmail to tell us about rejected recipients */
+static sfsistat
+dccm_negotiate(SMFICTX *milter_ctx UATTRIB,
+	       unsigned long f0, unsigned long f1,
+	       unsigned long f2 UATTRIB, unsigned long f3 UATTRIB,
+	       unsigned long *pf0, unsigned long *pf1 UATTRIB,
+	       unsigned long *pf2 UATTRIB, unsigned long *pf3 UATTRIB)
+{
+	*pf0 = f0;
+	*pf1 = SMFIP_RCPT_REJ & f1;
+
+	return SMFIS_CONTINUE;
+}
+#endif /* NEW_MFAPI */
+
+
+
+/* start a new connection to an SMTP client */
+static sfsistat
+dccm_conn(SMFICTX *milter_ctx,
+	  char *name,			/* SMTP client hostname */
+	  _SOCK_ADDR *sender)
+{
+	WORK *wp;
+
+	wp = (WORK *)smfi_getpriv(milter_ctx);
+	if (wp) {
+		dcc_error_msg("bogus initial SMFICTX pointer");
+		smfi_setpriv(milter_ctx, 0);
+		return SMFIS_TEMPFAIL;
+	}
+	wp = work_alloc();
+	if (!wp) {
+		smfi_setpriv(milter_ctx, WORK_EXCESS);
+		return SMFIS_TEMPFAIL;
+	}
+	smfi_setpriv(milter_ctx, wp);
+	wp->milter_ctx = milter_ctx;
+
+	log_start(&wp->cw);
+
+	if (!name) {
+		if (dcc_clnt_debug)
+			thr_trace_msg(&wp->cw, "null sender name");
+		strcpy(wp->cw.clnt_name, "(null name)");
+	} else {
+		BUFCPY(wp->cw.clnt_name, name);
+	}
+
+	if (!sender) {
+		if (!strcasecmp(wp->cw.clnt_name, "localhost")) {
+			wp->cw.clnt_addr.s6_addr32[3] = htonl(0x7f000001);
+			wp->cw.clnt_addr.s6_addr32[0] = 0;
+			wp->cw.clnt_addr.s6_addr32[1] = 0;
+			wp->cw.clnt_addr.s6_addr32[2] = htonl(0xffff);
+			strcpy(wp->cw.clnt_str, "127.0.0.1");
+		} else {
+			if (dcc_clnt_debug)
+				thr_trace_msg(&wp->cw,
+					      "null sender address for \"%s\"",
+					      wp->cw.clnt_name);
+			wp->cw.clnt_str[0] = '\0';
+		}
+	} else if (sender->sa_family != AF_INET
+		   && sender->sa_family != AF_INET6) {
+		dcc_error_msg("unexpected sender address family %d",
+			      sender->sa_family);
+		wp->cw.clnt_str[0] = '\0';
+	} else {
+		if (sender->sa_family == AF_INET) {
+			dcc_ipv4toipv6(&wp->cw.clnt_addr,
+				       ((struct sockaddr_in*)sender)->sin_addr);
+			dcc_ipv6tostr(wp->cw.clnt_str, sizeof(wp->cw.clnt_str),
+				      &wp->cw.clnt_addr);
+		} else if (sender->sa_family == AF_INET6) {
+			memcpy(&wp->cw.clnt_addr,
+			       &((struct sockaddr_in6 *)sender)->sin6_addr,
+			       sizeof(wp->cw.clnt_addr));
+			dcc_ipv6tostr(wp->cw.clnt_str, sizeof(wp->cw.clnt_str),
+				      &wp->cw.clnt_addr);
+		} else {
+			dcc_error_msg("unknown address family for \"%s\"",
+				      wp->cw.clnt_name);
+			wp->cw.clnt_str[0] = '\0';
+		}
+	}
+
+	/* quit now if we cannot find a free client context */
+	if (!ck_dcc_ctxt(&wp->cw))
+		return msg_tempfail(wp, &dcc_fail_reply);
+
+	/* This much is common for all of the messages that might
+	 * arrive through this connection to the SMTP client */
+
+	return SMFIS_CONTINUE;
+}
+
+
+
+/* log HELO */
+static sfsistat
+dccm_helo(SMFICTX *milter_ctx, char *helo)
+{
+	WORK *wp;
+	int i;
+
+	wp = get_wp(milter_ctx, GET_WP_START);
+
+	i = strlen(helo);
+	if (i < ISZ(wp->cw.helo)) {
+		memcpy(wp->cw.helo, helo, i+1);
+	} else {
+		memcpy(wp->cw.helo, helo, ISZ(wp->cw.helo)-ISZ(DCC_HELO_CONT));
+		strcpy(&wp->cw.helo[ISZ(wp->cw.helo)-ISZ(DCC_HELO_CONT)],
+		       DCC_HELO_CONT);
+	}
+
+	return SMFIS_CONTINUE;
+}
+
+
+
+/* deal with Mail From envelope value */
+static sfsistat
+dccm_envfrom(SMFICTX *milter_ctx, char **from)
+{
+	static char dollar_i[] = "i";
+	static char mail_host_macro[] = "{mail_host}";
+	static char dcc_mail_host_macro[] = "{dcc_mail_host}";
+	const char *id, *mail_host;
+	WORK *wp;
+
+	wp = get_wp(milter_ctx, GET_WP_START);
+
+	log_start(&wp->cw);
+
+	dcc_cks_init(&wp->cw.cks);
+	dcc_dnsbl_init(&wp->cw.cks, wp->cw.dcc_ctxt, &wp->cw, wp->cw.id);
+
+	/* Assume for now (and again if this is not the first transaction
+	 * for this SMTP session) that the sender is the current SMTP client
+	 * whiteclnt.  Received: headers might have the real sender */
+	strcpy(wp->cw.sender_name, wp->cw.clnt_name);
+	strcpy(wp->cw.sender_str, wp->cw.clnt_str);
+
+	/* see if the SMTP client is one of our MX forwarders */
+	if (wp->cw.sender_str[0] != '\0') {
+		/* we need the IP checksum in the usual place to look in
+		 * the whitelist for it */
+		dcc_get_ipv6_ck(&wp->cw.cks, &wp->cw.clnt_addr);
+		check_mx_listing(&wp->cw);
+	}
+
+	/* replace the message ID generated when the log file was started
+	 * with the sendmail message ID */
+	id = smfi_getsymval(milter_ctx, dollar_i);
+	if (id)
+		BUFCPY(wp->cw.id, id);
+
+	BUFCPY(wp->cw.env_from, from[0]);
+
+	/* Even if sendmail.cf sets the ${dcc_mail_host} macro,
+	 * FEATURE(delay_checks) can delay its setting until after
+	 * the MAIL command has been processed and this milter function
+	 * has been called. */
+	mail_host = smfi_getsymval(milter_ctx, dcc_mail_host_macro);
+	if (!mail_host || !*mail_host)
+		mail_host = smfi_getsymval(milter_ctx, mail_host_macro);
+	if (mail_host)
+		BUFCPY(wp->cw.mail_host, mail_host);
+
+	return SMFIS_CONTINUE;
+}
+
+
+
+/* note another recipient */
+static sfsistat
+dccm_envrcpt(SMFICTX *milter_ctx, char **rcpt)
+{
+	static char rcpt_mailer[] = "{rcpt_mailer}";
+	static char rcpt_addr[] = "{rcpt_addr}";
+	static char dcc_userdir[] = "{dcc_userdir}";
+	const char *mailer, *addr, *dir;
+	WORK *wp;
+	RCPT_ST *rcpt_st;
+
+	wp = get_wp(milter_ctx, GET_WP_GOING);
+
+	rcpt_st = alloc_rcpt_st(&wp->cw, 1);
+	if (!rcpt_st)
+		return rcpt_tempfail(wp, 0, &too_many_reply);
+
+	BUFCPY(rcpt_st->env_to, rcpt[0]);
+
+	addr = smfi_getsymval(milter_ctx, rcpt_addr);
+	mailer = smfi_getsymval(milter_ctx, rcpt_mailer);
+
+#ifdef NEW_MFAPI
+	/* count rejected recipient as if the message would have been
+	 * delivered to it */
+	if (mailer && !strcmp(mailer, "error")) {
+		rcpt_st->fgs |= RCPT_FG_BAD_USERNAME;
+		if (!addr || addr[0] != '4')
+			++wp->cw.mta_rej_tgts;
+		return SMFIS_CONTINUE;
+	}
+#endif
+
+	if (addr)
+		BUFCPY(rcpt_st->user, addr);
+
+	/* pick a per-user whitelist and log directory */
+	dir = smfi_getsymval(milter_ctx, dcc_userdir);
+	if (dir) {
+		if (!get_user_dir(rcpt_st, dir, strlen(dir), 0, 0))
+			thr_trace_msg(&wp->cw, "%s", wp->cw.emsg);
+	} else if (mailer && addr) {
+		if (!get_user_dir(rcpt_st, mailer, strlen(mailer),
+				  addr, strlen(addr)))
+			thr_trace_msg(&wp->cw, "%s", wp->cw.emsg);
+	}
+
+	/* sendmail might need to force discarding */
+	ask_sm(milter_ctx, wp);
+	if (!cmn_compat_whitelist(&wp->cw, rcpt_st))
+		return rcpt_tempfail(wp, rcpt_st, &incompat_white_reply);
+
+	++wp->cw.tgts;
+
+	return SMFIS_CONTINUE;
+}
+
+
+
+static sfsistat
+dccm_header(SMFICTX *milter_ctx, char *headerf, char *headerv)
+{
+	WORK *wp;
+	int f_len, v_len;
+	const char *cp;
+	int i, j;
+
+	wp = get_wp(milter_ctx, GET_WP_GOING);
+
+	if (!(wp->cw.cmn_fgs & CMN_FG_ENV_LOGGED))
+		thr_log_envelope(&wp->cw, 1);
+
+	f_len = strlen(headerf);
+	v_len = strlen(headerv);
+	if (wp->cw.log_fd >= 0) {
+		log_body_write(&wp->cw, headerf, f_len);
+		log_body_write(&wp->cw, ": ", LITZ(": "));
+		log_body_write(&wp->cw, headerv, v_len);
+		log_body_write(&wp->cw, "\n", 1);
+	}
+
+	/* compute DCC checksums for favored headers */
+	if (!strcasecmp(headerf, DCC_XHDR_TYPE_FROM)) {
+		dcc_get_cks(&wp->cw.cks, DCC_CK_FROM, headerv, 1);
+		return SMFIS_CONTINUE;
+	}
+	if (!strcasecmp(headerf, DCC_XHDR_TYPE_MESSAGE_ID)) {
+		dcc_get_cks(&wp->cw.cks, DCC_CK_MESSAGE_ID, headerv, 1);
+		return SMFIS_CONTINUE;
+	}
+	if (!strcasecmp(headerf, DCC_XHDR_TYPE_RECEIVED)) {
+		dcc_get_cks(&wp->cw.cks, DCC_CK_RECEIVED, headerv, 1);
+
+		/* parse Received: headers if we do not have a
+		 * non-MX-whitelisted sender IP address
+		 * and sendmail gave us a valid address so that
+		 * there is a slot in the log file for an address.
+		 * Parsing a Received header offered by a spammer is
+		 * prevented by only parsing those added by MX-whitelisted
+		 * IP ddresses */
+		if (wp->cw.cks.sums[DCC_CK_IP].type == DCC_CK_INVALID
+		    && wp->cw.log_ip_pos != 0) {
+			const char *rh;
+			int old_eof;
+
+			rh = parse_received(headerv, &wp->cw.cks,
+					    0, 0,   /* already have HELO */
+					    wp->cw.sender_str,
+					    sizeof(wp->cw.sender_str),
+					    wp->cw.sender_name,
+					    sizeof(wp->cw.sender_name));
+			if (rh == 0) {
+				/* to avoid being fooled by forged Received:
+				 * fields, do not skip unrecognized forms */
+				wp->cw.log_ip_pos = 0;
+
+			} else if (*rh != '\0') {
+				thr_log_print(&wp->cw, 1,
+					      "skip %s Received: header\n", rh);
+
+			} else if (!check_mx_listing(&wp->cw)) {
+				/* put the IP address in the log file
+				 * if now know it */
+				i = strlen(wp->cw.sender_str);
+				if (i > wp->cw.log_ip_len)
+					i = wp->cw.log_ip_len;
+				old_eof = log_lseek_get(&wp->cw);
+				if (old_eof == 0) {
+					;
+				} else if (-1 == lseek(wp->cw.log_fd,
+						       wp->cw.log_ip_pos,
+						       SEEK_SET)) {
+					thr_error_msg(&wp->cw,
+						      "lseek(%s,%d,SEEK_SET):"
+						      " %s",
+						      wp->cw.log_nm,
+						      (int)wp->cw.log_ip_pos,
+						      ERROR_STR());
+				} else {
+					j = write(wp->cw.log_fd,
+						  wp->cw.sender_str, i);
+					/* cannot log errors from that write()
+					 * because the file is at the wrong
+					 * position */
+					lseek(wp->cw.log_fd,
+					      old_eof, SEEK_SET);
+				}
+			}
+		}
+		return SMFIS_CONTINUE;
+	}
+
+	/* remember existing X-DCC headers so that we can delete them */
+	if (chghdr == SETHDR
+	    && (j = f_len - LITZ(DCC_XHDR_START DCC_XHDR_END)) >= 0
+	    && !CLITCMP(headerf, DCC_XHDR_START)
+	    && !CLITCMP(headerf+f_len-LITZ(DCC_XHDR_END), DCC_XHDR_END)) {
+		cp = headerf+LITZ(DCC_XHDR_START);
+		for (i = 0; ; ++i) {
+			if (i >= wp->num_x_dcc) {
+				if (i < NUM_XHDRS) {
+					++wp->num_x_dcc;
+					wp->xhdrs[i].num = 1;
+					wp->xhdrs[i].len = j;
+					memcpy(wp->xhdrs[i].brand, cp, j);
+				}
+				break;
+			}
+
+			if (j == wp->xhdrs[i].len
+			    && !strncasecmp(cp, wp->xhdrs[i].brand, j)) {
+				/* this is a familiar X-DCC header */
+				if (wp->xhdrs[i].num < 255)
+					++wp->xhdrs[i].num;
+				break;
+			}
+		}
+	}
+
+	dcc_ck_get_sub(&wp->cw.cks, headerf, headerv);
+
+	/* Notice MIME multipart boundary definitions */
+	dcc_ck_mime_hdr(&wp->cw.cks, headerf, headerv);
+
+	return SMFIS_CONTINUE;
+}
+
+
+
+static sfsistat
+dccm_eoh(SMFICTX *milter_ctx)
+{
+	WORK *wp;
+
+	wp = get_wp(milter_ctx, GET_WP_GOING);
+
+	/* finish logging the envelope on the first header,
+	 * but if there were no headers we must do it now */
+	if (!(wp->cw.cmn_fgs & CMN_FG_ENV_LOGGED))
+		thr_log_envelope(&wp->cw, 1);
+
+	/* Create a checksum for a null Message-ID header if there
+	 * was no Message-ID header.  */
+	if (wp->cw.cks.sums[DCC_CK_MESSAGE_ID].type != DCC_CK_MESSAGE_ID)
+		dcc_get_cks(&wp->cw.cks, DCC_CK_MESSAGE_ID, "", 0);
+
+	/* log the blank line between the header and the body */
+	log_body_write(&wp->cw, "\n", 1);
+
+	/* Check DNS blacklists for STMP client and envelope sender
+	 * unless DNSBL checks are turned off for all of the recipients */
+	if (wp->cw.cks.dnsbl) {
+		if (wp->cw.cks.sums[DCC_CK_IP].type == DCC_CK_IP)
+			dcc_client_dnsbl(wp->cw.cks.dnsbl, &wp->cw.cks.ip_addr,
+					 wp->cw.sender_name);
+		if (wp->cw.mail_host[0] != '\0')
+			dcc_mail_host_dnsbl(wp->cw.cks.dnsbl, wp->cw.mail_host);
+	}
+
+	return SMFIS_CONTINUE;
+}
+
+
+
+static sfsistat
+dccm_body(SMFICTX *milter_ctx, u_char *bodyp, size_t bodylen)
+{
+	WORK *wp;
+
+	wp = get_wp(milter_ctx, GET_WP_GOING);
+
+	/* Log the body block */
+	log_body_write(&wp->cw, (const char *)bodyp, bodylen);
+
+	dcc_ck_body(&wp->cw.cks, bodyp, bodylen);
+
+	return SMFIS_CONTINUE;
+}
+
+
+
+static void
+msg_fin(SMFICTX *milter_ctx, WORK *wp)
+{
+	dcc_cks_fin(&wp->cw.cks);
+
+	LOG_CAPTION(wp, DCC_LOG_MSG_SEP);
+	thr_log_late(&wp->cw);
+
+	/* get sendmail's final say */
+	ask_sm(milter_ctx, wp);
+
+	/* check the grey and white lists */
+	cmn_ask_white(&wp->cw);
+}
+
+
+
+/* deal with the end of the SMTP message as announced by sendmail */
+static sfsistat
+dccm_eom(SMFICTX *milter_ctx)
+{
+	static char null[] = "";	/* libmilter doesn't know about const */
+	WORK *wp;
+	char *hdr;
+	char delbuf[LITZ(DCC_XHDR_START)+DCC_BRAND_MAXLEN+LITZ(DCC_XHDR_END)+1];
+	int xhdr_fname_len;
+	int i, j;
+
+	wp = get_wp(milter_ctx, GET_WP_GOING);
+
+	msg_fin(milter_ctx, wp);
+
+	/* delete pre-existing X-DCC headers to prevent tricks on MUAs that
+	 * pay attention to them */
+	if (chghdr == SETHDR) {
+		for (i = 0; i < wp->num_x_dcc; ++i) {
+			snprintf(delbuf, sizeof(delbuf), DCC_XHDR_PAT,
+				 wp->xhdrs[i].len, wp->xhdrs[i].brand);
+			do {
+				j = smfi_chgheader(wp->milter_ctx, delbuf,
+						   wp->xhdrs[i].num, null);
+				if (MI_SUCCESS != j) {
+					thr_error_msg(&wp->cw,
+						      "smfi_delheader(\"%s\","
+						      "\"\")=%d",
+						      delbuf, j);
+				}
+			} while (--wp->xhdrs[i].num > 0);
+		}
+	}
+
+	wp->cw.header.buf[0] = '\0';
+	wp->cw.header.used = 0;
+	if (wp->cw.tgts <= wp->cw.white_tgts) {
+		/* it is whitelist for all targets,
+		 * so add X-DCC header saying so */
+		if (chghdr != NOHDR)
+			xhdr_whitelist(&wp->cw.header);
+		xhdr_fname_len = DCC_XHDR_WHITELIST_FNAME_LEN+2;
+
+		/* log it if the target count is high enough */
+		dcc_honor_log_cnts(&wp->cw.ask_st, &wp->cw.cks, wp->cw.tgts);
+
+	} else {
+		/* Report to the DCC
+		 * Request a temporary failure if the DCC failed and we
+		 * are trying hard */
+		i = cmn_ask_dcc(&wp->cw);
+		if (i <= 0) {
+			if (!i && try_extra_hard)
+				return msg_tempfail(wp, &dcc_fail_reply);
+
+			/* after unrecoverable errors without even a fake
+			 * header from local blacklisting, act as if the
+			 * DCC server said not-spam but without a header */
+		}
+		xhdr_fname_len = wp->cw.xhdr_fname_len+2;
+	}
+	/* install the X-DCC header */
+	if (chghdr != NOHDR && wp->cw.header.buf[0] != '\0') {
+		/* kludge the trailing '\n' that sendmail hates */
+		wp->cw.header.buf[wp->cw.header.used-1] = '\0';
+		hdr = &wp->cw.header.buf[xhdr_fname_len];
+		i = smfi_addheader(wp->milter_ctx, wp->cw.xhdr_fname, hdr);
+		if (MI_SUCCESS != i)
+			thr_error_msg(&wp->cw,
+				      "smfi_addheader(\"%s\",\"%s\")=%d",
+				      wp->cw.xhdr_fname, hdr, i);
+		wp->cw.header.buf[wp->cw.header.used-1] = '\n';
+	}
+
+	++totals.msgs;
+	totals.tgts += wp->cw.tgts;
+
+	/* get consensus of targets' wishes */
+	users_process(&wp->cw);
+	/* log the consensus & generate SMTP rejection message if needed */
+	users_log_result(&wp->cw, 0);
+
+	if (wp->cw.ask_st & ASK_ST_GREY_EMBARGO) {
+		totals.tgts_embargoed += wp->cw.tgts;
+		++totals.msgs_embargoed;
+		return msg_reject(wp);
+	}
+
+	/* tell sendmail to deliver it if all (remaining) targets want it */
+	if (wp->cw.reject_tgts == 0) {
+		msg_done(wp, 0);
+		return SMFIS_ACCEPT;
+	}
+
+	/* it is rejectable spam unless we are ignoring results */
+	switch (wp->cw.action) {
+	case CMN_IGNORE:
+		if (wp->cw.reject_tgts != 0) {
+			totals.tgts_ignored += wp->cw.reject_tgts;
+			++totals.msgs_spam;
+		}
+		msg_done(wp, DCC_XHDR_RESULT_I_A);
+		return SMFIS_ACCEPT;
+
+	case CMN_DISCARD:
+		/* discard it if that is our choice
+		 * or if sendmail said to */
+		if (wp->cw.reject_tgts != 0) {
+			totals.tgts_discarded += wp->cw.reject_tgts;
+			++totals.msgs_spam;
+		}
+		msg_done(wp, DCC_XHDR_RESULT_DISCARD);
+		return SMFIS_DISCARD;
+
+	case CMN_REJECT:
+		if (wp->cw.reject_tgts != 0) {
+			totals.tgts_rejected += wp->cw.reject_tgts;
+			++totals.msgs_spam;
+		}
+	}
+
+	/* tell sendmail what to do with it */
+	return msg_reject(wp);
+}
+
+
+
+/* deal with an aborted SMTP transaction */
+static void
+msg_abort(WORK *wp)
+{
+	if (wp->cw.env_from[0] == '\0')
+		return;
+
+	wp->cw.ask_st |= ASK_ST_INVALID_MSG;
+	if (!(wp->cw.cmn_fgs & CMN_FG_ENV_LOGGED))
+		thr_log_envelope(&wp->cw, 0);
+	msg_fin(wp->milter_ctx, wp);
+
+	users_process(&wp->cw);
+	users_log_result(&wp->cw, "STMP message aborted");
+
+	/* create log files for -d
+	 * and without any recipents but with "option log-all" */
+	if (dcc_clnt_debug
+	    || (wp->cw.init_sws & FLTR_SW_LOG_ALL))
+		wp->cw.ask_st |= ASK_ST_LOGIT;
+
+	if (wp->cw.ask_st & ASK_ST_LOGIT)
+		LOG_CAPTION(wp, DCC_XHDR_RESULT"STMP message aborted\n");
+}
+
+
+
+/* end of the SMTP session */
+static sfsistat
+dccm_close(SMFICTX *milter_ctx)
+{
+	int msg_cnt;
+	struct timeval tv;
+	WORK *wp;
+
+	wp = get_wp(milter_ctx, GET_WP_CLOSE);
+	if (!wp) {
+		smfi_setpriv(milter_ctx, 0);
+		return SMFIS_TEMPFAIL;
+	}
+
+	msg_abort(wp);
+
+	/* finished with the context */
+	log_stop(&wp->cw);
+	lock_work();
+	free_rcpt_sts(&wp->cw, 0);
+
+	wp->milter_ctx = WORK_MILTER_CTX_IDLE;
+	wp->fwd = work_free;
+	work_free = wp;
+
+	msg_cnt = work_too_many;
+	if (msg_cnt != 0) {
+		gettimeofday(&tv, 0);
+		if (work_msg_time == tv.tv_sec) {
+			msg_cnt = 0;
+		} else {
+			work_msg_time = tv.tv_sec;
+			work_too_many = 0;
+		}
+	}
+	unlock_work();
+	if (msg_cnt != 0)
+		dcc_error_msg("%d too many simultaneous mail messages",
+			      msg_cnt);
+
+	smfi_setpriv(milter_ctx, 0);
+
+	return SMFIS_CONTINUE;
+}
+
+
+
+static sfsistat
+dccm_abort(SMFICTX *milter_ctx)
+{
+	WORK *wp;
+
+	wp = get_wp(milter_ctx, GET_WP_ABORT);
+	if (!wp)
+		return SMFIS_TEMPFAIL;
+
+	msg_abort(wp);
+
+	/* get ready for possible new message */
+	msg_clear(wp);
+	return SMFIS_CONTINUE;
+}
diff -r 000000000000 -r c7f6b056b673 dccproc.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccproc.0	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,385 @@
+dccproc(8)            Distributed Checksum Clearinghouse            dccproc(8)
+
+NNAAMMEE
+     ddccccpprroocc -- Distributed Checksum Clearinghouse Procmail Interface
+
+SSYYNNOOPPSSIISS
+     ddccccpprroocc [--VVddAAQQCCHHEERR] [--hh _h_o_m_e_d_i_r] [--mm _m_a_p] [--ww _w_h_i_t_e_c_l_n_t] [--TT _t_m_p_d_i_r]
+             [--aa _I_P_-_a_d_d_r_e_s_s] [--ff _e_n_v___f_r_o_m] [--tt _t_a_r_g_e_t_s] [--xx _e_x_i_t_c_o_d_e]
+             [--cc _t_y_p_e_,[_l_o_g_-_t_h_o_l_d_,]_r_e_j_-_t_h_o_l_d] [--gg [_n_o_t_-]_t_y_p_e] [--SS _h_e_a_d_e_r]
+             [--ii _i_n_f_i_l_e] [--oo _o_u_t_f_i_l_e] [--ll _l_o_g_d_i_r] [--BB _d_n_s_b_l_-_o_p_t_i_o_n]
+             [--LL _l_t_y_p_e_,_f_a_c_i_l_i_t_y_._l_e_v_e_l]
+
+DDEESSCCRRIIPPTTIIOONN
+     DDccccpprroocc copies a complete SMTP message from standard input or a file to
+     standard output or another file.  As it copies the message, it computes
+     the DCC checksums for the message, reports them to a DCC server, and adds
+     a header line to the message.  Another program such as procmail(1) can
+     use the added header line to filter mail.  Dccproc does not support any
+     thresholds of its own, because equivalent effects can be achieved with
+     regular expressions and you can apply dccproc several times using differ-
+     ent DCC servers and then score mail based what all of the DCC servers
+     say.
+
+     Error messages are sent to stderr as well as the system log.  Connect
+     stderr and stdout to the same file to see errors in context, but direct
+     stderr to /dev/null to keep DCC error messages out of the mail.  The --ii
+     option can also be used to separate the error messages.
+
+     DDccccpprroocc sends reports of checksums related to mail received by DCC
+     clients and queries about the total number of reports of particular
+     checksums.  A DCC server receives no mail, address, headers, or other
+     information, but only cryptographically secure checksums of such informa-
+     tion.  A DCC server cannot determine the text or other information that
+     corresponds to the checksums it receives.  It only acts as a clearing-
+     house of counts of checksums computed by clients.
+
+     For the sake of privacy for even the checksums of private mail, the
+     checksums of senders of purely internal mail or other mail that is known
+     to not be unsolicited bulk can be listed in a whitelist to not be
+     reported to the DCC server.
+
+     When sendmail(8) is used, dccm(8) is a better DCC interface.  Dccifd(8)
+     is more efficient than ddccccpprroocc because it is a daemon, but that has costs
+     in complexity.  See dccsight(8) for a way to use previously computed
+     checksums.
+
+   OOPPTTIIOONNSS
+     The following options are available:
+
+     --VV   displays the version of the DCC procmail(1) interface.
+
+     --dd   enables debugging output from the DCC client software.  Additional
+          --dd options increase the number of messages.  One causes error mes-
+          sages to be sent to STDERR as well as the system log.
+
+     --AA   adds to existing X-DCC headers (if any) of the brand of the current
+          server instead of replacing existing headers.
+
+     --QQ   only queries the DCC server about the checksums of messages instead
+          of reporting and then querying.  This is useful when ddccccpprroocc is used
+          to filter mail that has already been reported to a DCC server by
+          another DCC client such as dccm(8).  No single mail message should
+          be reported to a DCC server more than once per recipient.
+
+          It is better to use _M_X_D_C_C lines in the --ww _w_h_i_t_e_c_l_n_t file for your MX
+          mail servers that use DCC than --QQ
+
+     --CC   outputs only the X-DCC header and the checksums for the message.
+
+     --HH   outputs only the X-DCC header.
+
+     --EE   adds lines to the start of the log file turned on with --ll and --cc
+          describing what might have been the envelope of the message.  The
+          information for the inferred envelope comes from arguments including
+          --aa and headers in the message when --RR is used.  No lines are gener-
+          ated for which no information is available, such as the envelope
+          recipient.
+
+     --RR   says the first Received lines have the standard
+          "helo (name [address])..."  format and the address is that of the
+          SMTP client that would otherwise be provided with --aa.  The --aa option
+          should be used if the local SMTP server adds a Received line with
+          some other format or does not add a Received line.  Received headers
+          specifying IP addresses marked _M_X or _M_X_D_C_C in the --ww _w_h_i_t_e_c_l_n_t file
+          are skipped.
+
+     --hh _h_o_m_e_d_i_r
+          overrides the default DCC home directory, _/_v_a_r_/_d_c_c.
+
+     --mm _m_a_p
+          specifies a name or path of the memory mapped parameter file instead
+          of the default _m_a_p in the DCC home directory.  It should be created
+          with the nneeww mmaapp operation of the cdcc(8) command.
+
+     --ww _w_h_i_t_e_c_l_n_t
+          specifies an optional file containing SMTP client IP addresses and
+          SMTP headers of mail that do not need X-DCC headers and whose check-
+          sums should not be reported to the DCC server.  It can also contain
+          checksums of spam.  If the pathname is not absolute, it is relative
+          to the DCC home directory.  Thus, individual users with private
+          whitelists usually specify them with absolute paths.  Common
+          whitelists shared by users must be in the DCC home directory or one
+          of its subdirectories and owned by the set-UID user of ddccccpprroocc.  It
+          is useful to _i_n_c_l_u_d_e a common or system-wide whitelist in private
+          lists.
+
+          Because the contents of the _w_h_i_t_e_c_l_n_t file are used frequently, a
+          companion file is automatically created and maintained.  It has the
+          same pathname but with an added suffix of _._d_c_c_w.  It contains a mem-
+          ory mapped hash table of the main file.
+
+          _O_p_t_i_o_n lines can be used to modify many aspects of ddccccpprroocc filter-
+          ing, as described in the main dcc(8) man page.  For example, an
+          _o_p_t_i_o_n _s_p_a_m_-_t_r_a_p_-_a_c_c_e_p_t line turns off DCC filtering and reports the
+          message as spam.
+
+     --TT _t_m_p_d_i_r
+          changes the default directory for temporary files from the system
+          default.  The system default is _/_t_m_p.
+
+     --aa _I_P_-_a_d_d_r_e_s_s
+          specifies the IP address (not the host name) of the immediately pre-
+          vious SMTP client.  It is often not available.  --aa _0_._0_._0_._0 is
+          ignored.  --aa.  The --aa option should be used instead of --RR if the
+          local SMTP server adds a Received line with some other format or
+          does not add a Received line.
+
+     --ff _e_n_v___f_r_o_m
+          specifies the RFC 821 envelope "Mail From" value with which the mes-
+          sage arrived.  It is often not available.  If --ff is not present, the
+          contents of the first Return-Path: or UNIX style From_ header is
+          used.  The _e_n_v___f_r_o_m string is often but need not be bracketed with
+          "<>".
+
+     --tt _t_a_r_g_e_t_s
+          specifies the number of addressees of the message if other than 1.
+          The string _m_a_n_y instead of a number asserts that there were too many
+          addressees and that the message is unsolicited bulk email.
+
+     --xx _e_x_i_t_c_o_d_e
+          specifies the code or status with which ddccccpprroocc exits if the --cc
+          thresholds are reached or the --ww _w_h_i_t_e_c_l_n_t file blacklists the mes-
+          sage.
+
+          The default value is EX_NOUSER.  EX_NOUSER is 67 on many systems.
+          Use 0 to always exit successfully.
+
+     --cc _t_y_p_e_,[_l_o_g_-_t_h_o_l_d_,]_r_e_j_-_t_h_o_l_d
+          sets logging and "spam" thresholds for checksum _t_y_p_e.  The checksum
+          types are _I_P, _e_n_v___F_r_o_m, _F_r_o_m, _M_e_s_s_a_g_e_-_I_D, _s_u_b_s_t_i_t_u_t_e, _R_e_c_e_i_v_e_d,
+          _B_o_d_y, _F_u_z_1, _F_u_z_2, _r_e_p_-_t_o_t_a_l, and _r_e_p.  The first six, _I_P through
+          _s_u_b_s_t_i_t_u_t_e, have no effect except when a local DCC server configured
+          with --KK is used.  The _s_u_b_s_t_i_t_u_t_e thresholds apply to the first sub-
+          stitute heading encountered in the mail message.  The string _A_L_L
+          sets thresholds for all types, but is unlikely to be useful except
+          for setting logging thresholds.  The string _C_M_N specifies the com-
+          monly used checksums _B_o_d_y, _F_u_z_1, and _F_u_z_2.  _R_e_j_-_t_h_o_l_d and _l_o_g_-_t_h_o_l_d
+          must be numbers, the string _N_E_V_E_R, or the string _M_A_N_Y indicating
+          millions of targets.  Counts from the DCC server as large as the
+          threshold for any single type are taken as sufficient evidence that
+          the message should be logged or rejected.
+
+          _L_o_g_-_t_h_o_l_d is the threshold at which messages are logged.  It can be
+          handy to log messages at a lower threshold to find solicited bulk
+          mail sources such as mailing lists.  If no logging threshold is set,
+          only rejected mail and messages with complicated combinations of
+          white and blacklisting are logged.  Messages that reach at least one
+          of their rejection thresholds are logged regardless of logging
+          thresholds.
+
+          _R_e_j_-_t_h_o_l_d is the threshold at which messages are considered "bulk,"
+          and so should be rejected or discarded if not whitelisted.
+
+          DCC Reputation thresholds in the commercial version of the DCC are
+          controlled by thresholds on checksum types _r_e_p and _r_e_p_-_t_o_t_a_l.  Mes-
+          sages from an IP address that the DCC database says has sent more
+          than --tt _r_e_p_-_t_o_t_a_l_,_l_o_g_-_t_h_o_l_d messages are logged.  A DCC Reputation
+          is computed for messages received from IP addresses that have sent
+          more than --tt _r_e_p_-_t_o_t_a_l_,_l_o_g_-_t_h_o_l_d messages.  The DCC Reputation of an
+          IP address is the percentage of its messages that have been detected
+          as bulk or having at least 10 recipients.  The defaults are equiva-
+          lent to --tt _r_e_p_,_n_e_v_e_r and --tt _r_e_p_-_t_o_t_a_l_,_n_e_v_e_r_,_2_0.
+
+          Bad DCC Reputations do not reject mail unless enabled by an _o_p_t_i_o_n
+          _D_C_C_-_r_e_p_-_o_n line in a _w_h_i_t_e_c_l_n_t file.
+
+          The checksums of locally whitelisted messages are not checked with
+          the DCC server and so only the number of targets of the current copy
+          of a whitelisted message are compared against the thresholds.
+
+          The default is _A_L_L_,_N_E_V_E_R, so that nothing is discarded, rejected, or
+          logged.  A common choice is _C_M_N_,_2_5_,_5_0 to reject or discard mail with
+          common bodies except as overridden by the whitelist of the DCC
+          server, the sendmail _$_{_d_c_c___i_s_s_p_a_m_} and _$_{_d_c_c___n_o_t_s_p_a_m_} macros, and
+          --gg, and --ww.
+
+     --gg [_n_o_t_-]_t_y_p_e
+          indicates that whitelisted, _O_K or _O_K_2, counts from the DCC server
+          for a type of checksum are to be believed.  They should be ignored
+          if prefixed with _n_o_t_-.  _T_y_p_e is one of the same set of strings as
+          for --cc.  Only _I_P, _e_n_v___F_r_o_m, and _F_r_o_m are likely choices.  By default
+          all three are honored, and hence the need for _n_o_t_-.
+
+     --SS _h_d_r
+          adds to the list of substitute or locally chosen headers that are
+          checked with the --ww _w_h_i_t_e_c_l_n_t file and sent to the DCC server.  The
+          checksum of the last header of type _h_d_r found in the message is
+          checked.  As many as 6 different substitute headers can be speci-
+          fied, but only the checksum of the first of the 6 will be sent to
+          the DCC server.
+
+     --ii _i_n_f_i_l_e
+          specifies an input file for the entire message instead of standard
+          input.  If not absolute, the pathname is interpreted relative to the
+          directory in which ddccccpprroocc was started.
+
+     --oo _o_u_t_f_i_l_e
+          specifies an output file for the entire message including headers
+          instead of standard output.  If not absolute, the pathname is inter-
+          preted relative to the directory in which ddccccpprroocc was started.
+
+     --ll _l_o_g_d_i_r
+          specifies a directory for copies of messages whose checksum target
+          counts exceed --cc thresholds.  The format of each file is affected by
+          --EE.
+
+          See the FILES section below concerning the contents of the files.
+          See also the _o_p_t_i_o_n _l_o_g_-_s_u_b_d_i_r_e_c_t_o_r_y_-_{_d_a_y_,_h_o_u_r_,_m_i_n_u_t_e_} lines in
+          _w_h_i_t_e_c_l_n_t files described in dcc(8).
+
+          The directory is relative to the DCC home directory if it is not
+          absolute
+
+     --BB _d_n_s_b_l_-_o_p_t_i_o_n
+          enables DNS blacklist checks of the SMTP client IP address, SMTP
+          envelope Mail_From sender domain name, and of host names in URLs in
+          the message body.  Body URL blacklisting has too many false posi-
+          tives to use on abuse mailboxes.  It is less effective than
+          greylisting with dccm(8) or dccifd(8) but can be useful in situa-
+          tions where greylisting cannot be used.
+
+          _D_n_s_b_l_-_o_p_t_i_o_n is either one of the --BB _s_e_t_:_o_p_t_i_o_n forms or
+              --BB _d_o_m_a_i_n[_,_I_P_a_d_d_r[_/_x_x[_,_b_l_t_y_p_e]]]
+          _D_o_m_a_i_n is a DNS blacklist domain such as example.com that will be
+          searched.  _I_P_a_d_d_r[_/_x_x_x] is the string "any" an IP address in the DNS
+          blacklist that indicates that the mail message should be rejected,
+          or a CIDR block covering results from the DNS blacklist.
+          "127.0.0.2" is assumed if _I_P_a_d_d_r is absent.  IPv6 addresses can be
+          specified with the usual colon (:) notation.  Names can be used
+          instead of numeric addresses.  The type of DNS blacklist is speci-
+          fied by _b_l_t_y_p_e as _n_a_m_e, _I_P_v_4, or _I_P_v_6.  Given an envelope sender
+          domain name or a domain name in a URL of spam.domain.org and a
+          blacklist of type _n_a_m_e, spam.domain.org.example.com will be tried.
+          Blacklist types of _I_P_v_4 and _I_P_v_6 require that the domain name in a
+          URL sender address be resolved into an IPv4 or IPv6 address.  The
+          address is then written as a reversed string of decimal octets to
+          check the DNS blacklist, as in 2.0.0.127.example.com,
+
+          More than one blacklist can be specified and blacklists can be
+          grouped.  All searching within a group is stopped at the first posi-
+          tive result.
+
+          Unlike dccm(8) and dccifd(8), no _o_p_t_i_o_n _D_N_S_B_L_-_o_n line is required in
+          the _w_h_i_t_e_c_l_n_t file.  A --BB argument is sufficient to show that DNSBL
+          filtering is wanted by the ddccccpprroocc user.
+
+          --BB _s_e_t_:_n_o_-_c_l_i_e_n_t
+               says that SMTP client IP addresses and reverse DNS domain names
+               should not be checked in the following blacklists.
+               --BB _s_e_t_:_c_l_i_e_n_t restores the default for the following black-
+               lists.
+
+          --BB _s_e_t_:_n_o_-_m_a_i_l___h_o_s_t
+               says that SMTP envelope Mail_From sender domain names should
+               not be checked in the following blacklists.  --BB _s_e_t_:_m_a_i_l___h_o_s_t
+               restores the default.
+
+          --BB _s_e_t_:_n_o_-_U_R_L
+               says that URLs in the message body should not be checked in the
+               in the following blacklists.  --BB _s_e_t_:_U_R_L restores the default.
+
+          --BB _s_e_t_:_n_o_-_M_X
+               says MX servers of sender Mail_From domain names and host names
+               in URLs should not be checked in the following blacklists.
+               --BB _s_e_t_:_M_X restores the default.
+
+          --BB _s_e_t_:_n_o_-_N_S
+               says DNS servers of sender Mail_From domain names and host
+               names in URLs should not be checked in the following black-
+               lists.  --BB _s_e_t_:_N_S restores the default.
+
+          --BB _s_e_t_:_d_e_f_a_u_l_t_s
+               is equivalent to all of --BB _s_e_t_:_n_o_-_t_e_m_p_-_f_a_i_l --BB _s_e_t_:_c_l_i_e_n_t
+               --BB _s_e_t_:_m_a_i_l___h_o_s_t --BB _s_e_t_:_U_R_L --BB _s_e_t_:_M_X and --BB _s_e_t_:_N_S
+
+          --BB _s_e_t_:_g_r_o_u_p_=_X
+               adds later DNS blacklists specified with
+                   --BB _d_o_m_a_i_n[_,_I_P_a_d_d_r[_/_x_x[_,_b_l_t_y_p_e]]]
+               to group 1, 2, or 3.
+
+          --BB _s_e_t_:_d_e_b_u_g_=_X
+               sets the DNS blacklist logging level
+
+          --BB _s_e_t_:_m_s_g_-_s_e_c_s_=_S
+               limits ddccccpprroocc to _S seconds total for checking all DNS black-
+               lists.  The default is 25.
+
+          --BB _s_e_t_:_U_R_L_-_s_e_c_s_=_S
+               limits ddccccpprroocc to at most _S seconds resolving and checking any
+               single URL.  The default is 11.  Some spam contains dozens of
+               URLs and that some "spamvertised" URLs contain host names that
+               need minutes to resolve.  Busy mail systems cannot afford to
+               spend minutes checking each incoming mail message.
+
+     --LL _l_t_y_p_e_,_f_a_c_i_l_i_t_y_._l_e_v_e_l
+          specifies how messages should be logged.  _L_t_y_p_e must be _e_r_r_o_r, _i_n_f_o,
+          or _o_f_f to indicate which of the two types of messages are being con-
+          trolled or to turn off all syslog(3) messages from ddccccpprroocc.  _L_e_v_e_l
+          must be a syslog(3) level among _E_M_E_R_G, _A_L_E_R_T, _C_R_I_T, _E_R_R, _W_A_R_N_I_N_G,
+          _N_O_T_I_C_E, _I_N_F_O, and _D_E_B_U_G.  _F_a_c_i_l_i_t_y must be among _A_U_T_H, _A_U_T_H_P_R_I_V,
+          _C_R_O_N, _D_A_E_M_O_N, _F_T_P, _K_E_R_N, _L_P_R, _M_A_I_L, _N_E_W_S, _U_S_E_R, _U_U_C_P, and _L_O_C_A_L_0
+          through _L_O_C_A_L_7.  The default is equivalent to
+                --LL _i_n_f_o_,_M_A_I_L_._N_O_T_I_C_E --LL _e_r_r_o_r_,_M_A_I_L_._E_R_R
+
+     ddccccpprroocc exits with 0 on success and with the --xx value if the --cc thresh-
+     olds are reached or the --ww _w_h_i_t_e_c_l_n_t file blacklists the message.  If at
+     all possible, the input mail message is output to standard output or the
+     --oo _o_u_t_f_i_l_e despite errors.  If possible, error messages are put into the
+     system log instead of being mixed with the output mail message.  The exit
+     status is zero for errors so that the mail message will not be rejected.
+
+     If ddccccpprroocc is run more than 500 times in fewer than 5000 seconds, ddccccpprroocc
+     tries to start Dccifd(8).  The attempt is made at most once per hour.
+     Dccifd is significantly more efficient than ddccccpprroocc.  With luck, mecha-
+     nisms such as SpamAssassin will notice when dccifd is running and switch
+     to dccifd.
+
+FFIILLEESS
+     /var/dcc   DCC home directory in which other files are found.
+     map        memory mapped file in the DCC home directory of information
+                concerning DCC servers.
+     whiteclnt  contains the client whitelist in the format described in
+                dcc(8).
+     whiteclnt.dccw
+                is a memory mapped hash table corresponding to the _w_h_i_t_e_c_l_n_t
+                file.
+     tmpdir     contains temporary files created and deleted as ddccccpprroocc pro-
+                cesses the message.
+     logdir     is an optional directory specified with --ll and containing
+                marked mail.  Each file in the directory contains one message,
+                at least one of whose checksums reached one of its --cc thresh-
+                olds.  The entire body of the SMTP message including its
+                header is followed by the checksums for the message.
+
+EEXXAAMMPPLLEESS
+     The following procmailrc(5) rule adds an X-DCC header to passing mail
+
+         :0 f
+         | /usr/local/bin/dccproc -ERw whiteclnt
+
+     This procmailrc(5) recipe rejects mail with total counts of 10 or larger
+     for the commonly used checksums:
+
+         :0 fW
+         | /usr/local/bin/dccproc -ERw whiteclnt -ccmn,10
+         :0 e
+         {
+             EXITCODE=67
+             :0
+             /dev/null
+         }
+
+SSEEEE AALLSSOO
+     cdcc(8), dcc(8), dbclean(8), dccd(8), dblist(8), dccifd(8), dccm(8),
+     dccsight(8), mail(1), procmail(1).
+
+HHIISSTTOORRYY
+     Distributed Checksum Clearinghouses are based on an idea of Paul Vixie.
+     Implementation of ddccccpprroocc was started at Rhyolite Software in 2000.  This
+     document describes version 1.3.103.
+
+BBUUGGSS
+     ddccccpprroocc uses --cc where dccm(8) uses --tt.
+
+                               February 26, 2009
diff -r 000000000000 -r c7f6b056b673 dccproc.8.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccproc.8.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,780 @@
+.\" Copyright (c) 2008 by Rhyolite Software, LLC
+.\"
+.\" This agreement is not applicable to any entity which sells anti-spam
+.\" solutions to others or provides an anti-spam solution as part of a
+.\" security solution sold to other entities, or to a private network
+.\" which employs the DCC or uses data provided by operation of the DCC
+.\" but does not provide corresponding data to other users.
+.\"
+.\" Permission to use, copy, modify, and distribute this software without
+.\" changes for any purpose with or without fee is hereby granted, provided
+.\" that the above copyright notice and this permission notice appear in all
+.\" copies and any distributed versions or copies are either unchanged
+.\" or not called anything similar to "DCC" or "Distributed Checksum
+.\" Clearinghouse".
+.\"
+.\" Parties not eligible to receive a license under this agreement can
+.\" obtain a commercial license to use DCC by contacting Rhyolite Software
+.\" at sales@rhyolite.com.
+.\"
+.\" A commercial license would be for Distributed Checksum and Reputation
+.\" Clearinghouse software.  That software includes additional features.  This
+.\" free license for Distributed ChecksumClearinghouse Software does not in any
+.\" way grant permision to use Distributed Checksum and Reputation Clearinghouse
+.\" software
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+.\" 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.
+.\"
+.\" Rhyolite Software DCC 1.3.103-1.120 $Revision$
+.\"
+.Dd February 26, 2009
+.ds volume-ds-DCC Distributed Checksum Clearinghouse
+.Dt dccproc 8 DCC
+.Os " "
+.Sh NAME
+.Nm dccproc
+.Nd Distributed Checksum Clearinghouse Procmail Interface
+.Sh SYNOPSIS
+.Nm dccproc
+.Bk -words
+.Op Fl VdAQCHER
+.Op Fl h Ar homedir
+.Op Fl m Ar map
+.Op Fl w Ar whiteclnt
+.Op Fl T Ar tmpdir
+.Op Fl a Ar IP-address
+.Op Fl f Ar env_from
+.Op Fl t Ar targets
+.Op Fl x Ar exitcode
+.br
+.Oo
+.Fl c Xo
+.Sm off
+.Ar type,
+.Op Ar log-thold,
+.Ar rej-thold
+.Sm on
+.Xc
+.Oc
+.Oo
+.Fl g Xo
+.Sm off
+.Op Ar not-
+.Ar type
+.Sm on
+.Xc
+.Oc
+.Op Fl S Ar header
+.br
+.Op Fl i Ar infile
+.Op Fl o Ar outfile
+.Op Fl l Ar logdir
+.Op Fl B Ar dnsbl-option
+.Op Fl L Ar ltype,facility.level
+.Ek
+.Sh DESCRIPTION
+.Nm Dccproc
+copies a complete SMTP message from standard input or a file
+to standard output or another file.
+As it copies the message,
+it computes the DCC checksums for the message,
+reports them to a DCC server, and adds
+a header line to the message.
+Another program such as
+.Xr procmail 1
+can use the added header line to filter mail.
+Dccproc does not support any thresholds of its own,
+because equivalent effects can be achieved with regular expressions
+and you can apply dccproc several times using different DCC servers
+and then score mail based what all of the DCC servers say.
+.Pp
+Error messages are sent to stderr as well as the system log.
+Connect stderr and stdout to the same file to see errors in context,
+but direct stderr to /dev/null to keep DCC error messages out of the mail.
+The
+.Fl i
+option can also be used to separate the error messages.
+.Pp
+.Nm Dccproc
+sends reports of checksums related to mail received by DCC clients
+and queries about the total number of reports of particular checksums.
+A DCC server receives no
+mail, address, headers, or other information,
+but only cryptographically secure checksums of such information.
+A DCC server cannot determine the text or other information that corresponds
+to the checksums it receives.
+It only acts as a clearinghouse of counts of checksums computed by clients.
+.Pp
+For the sake of privacy for even the checksums of private mail,
+the checksums of senders of purely internal mail or other
+mail that is known to not be unsolicited bulk can be listed in a whitelist
+to not be reported to the DCC server.
+.Pp
+When
+.Xr sendmail 8
+is used,
+.Xr dccm 8
+is a better DCC interface.
+.Xr Dccifd 8
+is more efficient than
+.Nm
+because it is a daemon, but that has costs in complexity.
+See
+.Xr dccsight 8
+for a way to use previously computed checksums.
+.Ss OPTIONS
+The following options are available:
+.Bl -tag -width 3n
+.It Fl V
+displays the version of the DCC
+.Xr procmail 1
+interface.
+.It Fl d
+enables debugging output from the DCC client software.
+Additional
+.Fl d
+options increase the number of messages.
+One causes error messages to be sent to STDERR as well as the system log.
+.It Fl A
+adds to existing X-DCC headers (if any)
+of the brand of the current server
+instead of
+replacing existing headers.
+.It Fl Q
+only queries the DCC server about the checksums of messages
+instead of reporting and then querying.
+This is useful when
+.Nm
+is used to filter mail that has already been reported to a DCC
+server by another DCC client such as
+.Xr dccm 8 .
+No single mail message should be reported to a DCC
+server more than once per recipient.
+.Pp
+It is better to use
+.Em MXDCC
+lines in the
+.Fl w Ar whiteclnt
+file for your MX mail servers that use DCC than
+.Fl Q
+.It Fl C
+outputs only the X-DCC header
+and the checksums for the message.
+.It Fl H
+outputs only the X-DCC header.
+.It Fl E
+adds lines to the start of the log file turned on with
+.Fl l
+and
+.Fl c
+describing what might have been the envelope of the message.
+The information for the inferred envelope comes from arguments including
+.Fl a
+and headers in the message when
+.Fl R
+is used.
+No lines are generated for which no information is available,
+such as the envelope recipient.
+.It Fl R
+says the first Received lines have the standard
+"helo\ (name\ [address])..."
+format and the address is that of the SMTP client
+that would otherwise be provided with
+.Fl a .
+The
+.Fl a
+option should be used
+if the local SMTP server adds a Received line with some other format
+or does not add a Received line.
+Received headers specifying IP addresses marked
+.Em MX
+or
+.Em MXDCC
+in the
+.Fl w Ar whiteclnt
+file are skipped.
+.It Fl h Ar homedir
+overrides the default DCC home directory,
+.Pa @prefix@ .
+.It Fl m Ar map
+specifies a name or path of the memory mapped parameter file instead
+of the default
+.Pa map
+in the DCC home directory.
+It should be created with the
+.Ic new map
+operation of the
+.Xr cdcc 8
+command.
+.It Fl w Ar whiteclnt
+specifies an optional file containing SMTP client IP addresses and
+SMTP headers
+of mail that do not need X-DCC headers and whose checksums should not
+be reported to the DCC server.
+It can also contain checksums of spam.
+If the pathname is not absolute, it is relative to the DCC home directory.
+Thus, individual users with private whitelists usually specify them
+with absolute paths.
+Common whitelists shared by users must be in the DCC home directory or
+one of its subdirectories and owned by the set-UID user of
+.Nm dccproc .
+It is useful to
+.Ar include
+a common or system-wide whitelist in private lists.
+.Pp
+Because the contents of the
+.Ar whiteclnt
+file are used frequently, a companion file is automatically
+created and maintained.
+It has the same pathname but with an added suffix of
+.Ar .dccw .
+It contains a memory mapped hash table of the main file.
+.Pp
+.Ar Option
+lines can be used to modify many aspects of
+.Nm
+filtering,
+as described in the main
+.Xr dcc 8
+man page.
+For example, an
+.Ar option spam-trap-accept
+line turns off DCC filtering and reports the message as spam.
+.It Fl T Ar tmpdir
+changes the default directory for temporary files from the system default.
+The system default is
+.Pa /tmp .
+.It Fl a Ar IP-address
+specifies the IP address (not the host name) of
+the immediately previous SMTP client.
+It is often not available.
+.Fl a Ar 0.0.0.0
+is ignored.
+.Fl a .
+The
+.Fl a
+option should be used
+instead of
+.Fl R
+if the local SMTP server adds a Received line with some other format
+or does not add a Received line.
+.It Fl f Ar env_from
+specifies the RFC\ 821 envelope "Mail\ From" value with which the
+message arrived.
+It is often not available.
+If
+.Fl f
+is not present, the contents of the first Return-Path: or UNIX style
+From_ header is used.
+The
+.Ar env_from
+string is often but need not be bracketed with "<>".
+.It Fl t Ar targets
+specifies the number of addressees of the message if other than 1.
+The string
+.Ar many
+instead of a number asserts that there were too many addressees
+and that the message is unsolicited bulk email.
+.It Fl x Ar exitcode
+specifies the code or status with which
+.Nm
+exits if the
+.Fl c
+thresholds are reached or the
+.Fl w Ar whiteclnt
+file blacklists the message.
+.Pp
+The default value is EX_NOUSER.
+EX_NOUSER is 67 on many systems.
+Use 0 to always exit successfully.
+.It Fl c Xo
+.Sm off
+.Ar type,
+.Op Ar log-thold,
+.Ar rej-thold
+.Sm on
+.Xc
+sets logging and "spam" thresholds for checksum
+.Ar type .
+The checksum types are
+.Ar IP ,
+.Ar env_From ,
+.Ar From ,
+.Ar Message-ID ,
+.Ar substitute ,
+.Ar Received ,
+.Ar Body ,
+.Ar Fuz1 ,
+.Ar Fuz2 ,
+.Ar rep-total ,
+and
+.Ar rep .
+The first six,
+.Ar IP
+through
+.Ar substitute ,
+have no effect except when a local DCC server configured with
+.Fl K
+is used.
+The
+.Ar substitute
+thresholds apply to the first substitute heading encountered in the mail
+message.
+The string
+.Ar ALL
+sets thresholds for all types, but is unlikely to be useful except for
+setting logging thresholds.
+The string
+.Ar CMN
+specifies the commonly used checksums
+.Ar Body ,
+.Ar Fuz1 ,
+and
+.Ar Fuz2 .
+.Ar Rej-thold
+and
+.Ar log-thold
+must be numbers, the string
+.Ar NEVER ,
+or the string
+.Ar MANY
+indicating millions of targets.
+Counts from the DCC server as large as the threshold for any single type
+are taken as sufficient evidence
+that the message should be logged or rejected.
+.Pp
+.Ar Log-thold
+is the threshold at which messages are logged.
+It can be handy to log messages at a lower threshold to find
+solicited bulk mail sources such as mailing lists.
+If no logging threshold is set,
+only rejected mail and messages with complicated combinations of white
+and blacklisting are logged.
+Messages that reach at least one of their rejection thresholds are
+logged regardless of logging thresholds.
+.Pp
+.Ar Rej-thold
+is the threshold at which messages are considered "bulk,"
+and so should be rejected or discarded if not whitelisted.
+.Pp
+DCC Reputation thresholds in the commercial version
+of the DCC are controlled by thresholds on checksum types
+.Ar rep
+and
+.Ar rep-total .
+Messages from an IP address that the DCC database says has sent
+more than
+.Fl t Ar rep-total,log-thold
+messages are logged.
+A DCC Reputation is computed for messages received
+from IP addresses that
+have sent more than
+.Fl t Ar rep-total,log-thold
+messages.
+The DCC Reputation of an IP address is the percentage of its messages
+that have been detected as bulk
+or having at least 10 recipients.
+The defaults are equivalent to
+.Fl t Ar rep,never
+and
+.Fl t Ar rep-total,never,20 .
+.Pp
+Bad DCC Reputations do not reject mail unless enabled by an
+.Ar option DCC-rep-on
+line in a
+.Pa whiteclnt
+file.
+.Pp
+The checksums of locally whitelisted messages are not checked with
+the DCC server and so only the number of targets of the current copy of
+a whitelisted message are compared against the thresholds.
+.Pp
+The default is
+.Ar ALL,NEVER ,
+so that nothing is discarded, rejected, or logged.
+A common choice is
+.Ar CMN,25,50
+to reject or discard
+mail with common bodies except as overridden by
+the whitelist of the DCC server, the sendmail
+.Em ${dcc_isspam}
+and
+.Em ${dcc_notspam}
+macros, and
+.Fl g ,
+and
+.Fl w .
+.It Fl g Xo
+.Sm off
+.Op Ar not-
+.Ar type
+.Sm on
+.Xc
+indicates that whitelisted,
+.Ar OK
+or
+.Ar OK2 ,
+counts from the DCC server for a type of checksum are to be believed.
+They should be ignored if prefixed with
+.Ar not- .
+.Ar Type
+is one of the same set of strings as for
+.Fl c .
+Only
+.Ar IP ,
+.Ar env_From ,
+and
+.Ar From
+are likely choices.
+By default all three are honored,
+and hence the need for
+.Ar not- .
+.It Fl S Ar hdr
+adds to the list of substitute or locally chosen headers that
+are checked with the
+.Fl w Ar whiteclnt
+file and sent to the DCC server.
+The checksum of the last header of type
+.Ar hdr
+found in the message is checked.
+As many as 6 different substitute headers can be specified, but only
+the checksum of the first of the 6 will be sent to the DCC server.
+.It Fl i Ar infile
+specifies an input file for the entire message
+instead of standard input.
+If not absolute, the pathname is interpreted relative to the
+directory in which
+.Nm
+was started.
+.It Fl o Ar outfile
+specifies an output file for the entire message including headers
+instead of standard output.
+If not absolute, the pathname is interpreted relative to the
+directory in which
+.Nm
+was started.
+.It Fl l Ar logdir
+specifies a directory for copies of messages whose
+checksum target counts exceed
+.Fl c
+thresholds.
+The format of each file is affected by
+.Fl E .
+.Pp
+See the FILES section below concerning the contents of the files.
+See also the
+.Ar option log-subdirectory-{day,hour,minute}
+lines in
+.Pa whiteclnt
+files described in
+.Xr dcc 8 .
+.Pp
+The directory is relative to the DCC home directory if it is not absolute
+.It Fl B Ar dnsbl-option
+enables DNS blacklist checks of the SMTP client IP address, SMTP envelope
+Mail_From sender domain name, and of host names in URLs in the message body.
+Body URL blacklisting has too many false positives to use on
+abuse mailboxes.
+It is less effective than greylisting with
+.Xr dccm 8
+or
+.Xr dccifd 8
+but can be useful in situations where
+greylisting cannot be used.
+.Pp
+.Ar Dnsbl-option
+is either one of the
+.Fl B Ar set:option
+forms or
+.Bd -literal -compact -offset 4n
+.Fl B Xo
+.Sm off
+.Ar domain Oo Ar ,IPaddr
+.Op Ar /xx Op Ar ,bltype Oc
+.Sm on
+.Xc
+.Ed
+.Ar Domain
+is a DNS blacklist domain such as example.com
+that will be searched.
+.Ar IPaddr Ns Op Ar /xxx
+is the string "any"
+an IP address in the DNS blacklist
+that indicates that the mail message
+should be rejected,
+or a CIDR block covering results from the DNS blacklist.
+"127.0.0.2" is assumed if
+.Ar IPaddr
+is absent.
+IPv6 addresses can be specified with the usual colon (:) notation.
+Names can be used instead of numeric addresses.
+The type of DNS blacklist
+is specified by
+.Ar bltype
+as
+.Ar name ,
+.Ar IPv4 ,
+or
+.Ar IPv6 .
+Given an envelope sender domain name or a domain name in a URL of
+spam.domain.org
+and a blacklist of type
+.Ar name ,
+spam.domain.org.example.com will be tried.
+Blacklist types of
+.Ar IPv4
+and
+.Ar IPv6
+require that the domain name in a URL sender address
+be resolved into an IPv4 or IPv6
+address.
+The address is then written as a reversed string of decimal
+octets to check the DNS blacklist, as in 2.0.0.127.example.com,
+.Pp
+More than one blacklist can be specified and blacklists can be grouped.
+All searching within a group is stopped at the first positive result.
+.Pp
+Unlike
+.Xr dccm 8
+and
+.Xr dccifd 8 ,
+no
+.Ar option\ DNSBL-on
+line is required in the
+.Pa whiteclnt
+file.
+A
+.Fl B
+argument is sufficient to show that DNSBL filtering is wanted by the
+.Nm
+user.
+.Bl -tag -width 3n
+.It Fl B Ar set:no-client
+says that SMTP client IP addresses and reverse DNS domain names should
+not be checked in the following blacklists.
+.br
+.Fl B Ar set:client
+restores the default for the following blacklists.
+.It Fl B Ar set:no-mail_host
+says that SMTP envelope Mail_From sender domain names should
+not be checked in the following blacklists.
+.Fl B Ar set:mail_host
+restores the default.
+.It Fl B Ar set:no-URL
+says that URLs in the message body should not be checked in the
+in the following blacklists.
+.Fl B Ar set:URL
+restores the default.
+.It Fl B Ar set:no-MX
+says MX servers of sender Mail_From domain names and host names in URLs
+should not be checked in the following blacklists.
+.br
+.Fl B Ar set:MX
+restores the default.
+.It Fl B Ar set:no-NS
+says DNS servers of sender Mail_From domain names and host names in URLs
+should not be checked in the following blacklists.
+.Fl B Ar set:NS
+restores the default.
+.It Fl B Ar set:defaults
+is equivalent to all of
+.Fl B Ar set:no-temp-fail
+.Fl B Ar set:client
+.br
+.Fl B Ar set:mail_host
+.Fl B Ar set:URL
+.Fl B Ar set:MX
+and
+.Fl B Ar set:NS
+.It Fl B Ar set:group=X
+adds later DNS blacklists specified with
+.Bd -literal -compact -offset 4n
+.Fl B Xo
+.Sm off
+.Ar domain Oo Ar ,IPaddr
+.Op Ar /xx Op Ar ,bltype Oc
+.Sm on
+.Xc
+.Ed
+to group 1, 2, or 3.
+.It Fl B Ar set:debug=X
+sets the DNS blacklist logging level
+.It Fl B Ar set:msg-secs=S
+limits
+.Nm
+to
+.Ar S
+seconds total for checking all DNS blacklists.
+The default is 25.
+.It Fl B Ar set:URL-secs=S
+limits
+.Nm
+to at most
+.Ar S
+seconds resolving and checking any single URL.
+The default is 11.
+Some spam contains dozens of URLs and that
+some "spamvertised" URLs contain host names that need minutes to
+resolve.
+Busy mail systems cannot afford to spend minutes checking each incoming
+mail message.
+.El
+.It Fl L Ar ltype,facility.level
+specifies how messages should be logged.
+.Ar Ltype
+must be
+.Ar error ,
+.Ar info ,
+or
+.Ar off
+to indicate which of the two types of messages are being controlled or
+to turn off all
+.Xr syslog 3
+messages from
+.Nm .
+.Ar Level
+must be a
+.Xr syslog 3
+level among
+.Ar EMERG ,
+.Ar ALERT ,
+.Ar CRIT , ERR ,
+.Ar WARNING ,
+.Ar NOTICE ,
+.Ar INFO ,
+and
+.Ar DEBUG .
+.Ar Facility
+must be among
+.Ar AUTH ,
+.Ar AUTHPRIV ,
+.Ar CRON ,
+.Ar DAEMON ,
+.Ar FTP ,
+.Ar KERN ,
+.Ar LPR ,
+.Ar MAIL ,
+.Ar NEWS ,
+.Ar USER ,
+.Ar UUCP ,
+and
+.Ar LOCAL0
+through
+.Ar LOCAL7 .
+The default is equivalent to
+.Dl Fl L Ar info,MAIL.NOTICE  Fl L Ar error,MAIL.ERR
+.El
+.Pp
+.Nm
+exits with 0 on success and with the
+.Fl x
+value if the
+.Fl c
+thresholds are reached or the
+.Fl w Ar whiteclnt
+file blacklists the message.
+If at all possible,
+the input mail message is output to standard output or the
+.Fl o Ar outfile
+despite errors.
+If possible, error messages are put into the system log instead of
+being mixed with the output mail message.
+The exit status is zero for errors so that the mail message
+will not be rejected.
+.Pp
+If
+.Nm
+is run more than 500 times in fewer than 5000 seconds,
+.Nm
+tries to start
+.Xr Dccifd 8 .
+The attempt is made at most once per hour.
+Dccifd is significantly more efficient than
+.Nm .
+With luck, mechanisms such as SpamAssassin will notice when dccifd is
+running and switch to dccifd.
+.Sh FILES
+.Bl -tag -width whiteclnt -compact
+.It Pa @prefix@
+DCC home directory in which other files are found.
+.It Pa map
+memory mapped file in the DCC home directory
+of information concerning DCC servers.
+.It Pa whiteclnt
+contains the client whitelist in
+the format described in
+.Xr dcc 8 .
+.It Pa whiteclnt.dccw
+is a memory mapped hash table corresponding to the
+.Pa whiteclnt
+file.
+.It Pa tmpdir
+contains temporary files created and deleted as
+.Nm
+processes the message.
+.It Pa logdir
+is an optional directory specified with
+.Fl l
+and containing marked mail.
+Each file in the directory contains one message, at least one of whose
+checksums reached one of its
+.Fl c
+thresholds.
+The entire body of the SMTP message including its header
+is followed by the checksums for the message.
+.El
+.Sh EXAMPLES
+The following
+.Xr procmailrc 5
+rule adds an X-DCC header to passing mail
+.Bd -literal -offset 4n
+:0 f
+| /usr/local/bin/dccproc -ERw whiteclnt
+.Ed
+.Pp
+This
+.Xr procmailrc 5
+recipe rejects mail with total counts of 10 or larger for
+the commonly used checksums:
+.Bd -literal -offset 4n
+:0 fW
+| /usr/local/bin/dccproc -ERw whiteclnt -ccmn,10
+:0 e
+{
+    EXITCODE=67
+    :0
+    /dev/null
+}
+.Ed
+.Sh SEE ALSO
+.Xr cdcc 8 ,
+.Xr dcc 8 ,
+.Xr dbclean 8 ,
+.Xr dccd 8 ,
+.Xr dblist 8 ,
+.Xr dccifd 8 ,
+.Xr dccm 8 ,
+.Xr dccsight 8 ,
+.Xr mail 1 ,
+.Xr procmail 1 .
+.Sh HISTORY
+Distributed Checksum Clearinghouses are based on an idea of Paul Vixie.
+Implementation of
+.Nm
+was started at Rhyolite Software in 2000.
+This document describes version 1.3.103.
+.Sh BUGS
+.Nm
+uses
+.Fl c
+where
+.Xr dccm 8
+uses
+.Fl t .
diff -r 000000000000 -r c7f6b056b673 dccproc.html.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccproc.html.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,434 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<HTML>
+<HEAD>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <TITLE>dccproc.0.8</TITLE>
+    <META http-equiv="Content-Style-Type" content="text/css">
+    <STYLE type="text/css">
+	BODY {background-color:white; color:black}
+	ADDRESS {font-size:smaller}
+        IMG.logo {width:6em; vertical-align:middle}
+    </STYLE>
+</HEAD>
+<BODY>
+<PRE>
+<!-- Manpage converted by man2html 3.0.1 -->
+<B><A HREF="dccproc.html">dccproc(8)</A></B>            Distributed Checksum Clearinghouse            <B><A HREF="dccproc.html">dccproc(8)</A></B>
+
+
+</PRE>
+<H2><A NAME="NAME">NAME</A></H2><PRE>
+     <B>dccproc</B> -- Distributed Checksum Clearinghouse Procmail Interface
+
+
+</PRE>
+<H2><A NAME="SYNOPSIS">SYNOPSIS</A></H2><PRE>
+     <B>dccproc</B> [<B>-VdAQCHER</B>] [<B>-h</B> <I>homedir</I>] [<B>-m</B> <I>map</I>] [<B>-w</B> <I>whiteclnt</I>] [<B>-T</B> <I>tmpdir</I>]
+             [<B>-a</B> <I>IP-address</I>] [<B>-f</B> <I>env</I><B>_</B><I>from</I>] [<B>-t</B> <I>targets</I>] [<B>-x</B> <I>exitcode</I>]
+             [<B>-c</B> <I>type,</I>[<I>log-thold,</I>]<I>rej-thold</I>] [<B>-g</B> [<I>not-</I>]<I>type</I>] [<B>-S</B> <I>header</I>]
+             [<B>-i</B> <I>infile</I>] [<B>-o</B> <I>outfile</I>] [<B>-l</B> <I>logdir</I>] [<B>-B</B> <I>dnsbl-option</I>]
+             [<B>-L</B> <I>ltype,facility.level</I>]
+
+
+</PRE>
+<H2><A NAME="DESCRIPTION">DESCRIPTION</A></H2><PRE>
+     <B>Dccproc</B> copies a complete SMTP message from standard input or a file to
+     standard output or another file.  As it copies the message, it computes
+     the DCC checksums for the message, reports them to a DCC server, and adds
+     a header line to the message.  Another program such as <B>procmail(1)</B> can
+     use the added header line to filter mail.  Dccproc does not support any
+     thresholds of its own, because equivalent effects can be achieved with
+     regular expressions and you can apply dccproc several times using differ-
+     ent DCC servers and then score mail based what all of the DCC servers
+     say.
+
+     Error messages are sent to stderr as well as the system log.  Connect
+     stderr and stdout to the same file to see errors in context, but direct
+     stderr to /dev/null to keep DCC error messages out of the mail.  The <B>-i</B>
+     option can also be used to separate the error messages.
+
+     <B>Dccproc</B> sends reports of checksums related to mail received by DCC
+     clients and queries about the total number of reports of particular
+     checksums.  A DCC server receives no mail, address, headers, or other
+     information, but only cryptographically secure checksums of such informa-
+     tion.  A DCC server cannot determine the text or other information that
+     corresponds to the checksums it receives.  It only acts as a clearing-
+     house of counts of checksums computed by clients.
+
+     For the sake of privacy for even the checksums of private mail, the
+     checksums of senders of purely internal mail or other mail that is known
+     to not be unsolicited bulk can be listed in a whitelist to not be
+     reported to the DCC server.
+
+     When <B>sendmail(8)</B> is used, <B><A HREF="dccm.html">dccm(8)</A></B> is a better DCC interface.  <B><A HREF="dccifd.html">Dccifd(8)</A></B>
+     is more efficient than <B>dccproc</B> because it is a daemon, but that has costs
+     in complexity.  See <B><A HREF="dccsight.html">dccsight(8)</A></B> for a way to use previously computed
+     checksums.
+
+   <A NAME="OPTIONS"><B>OPTIONS</B></A>
+     The following options are available:
+
+     <A NAME="OPTION-V"><B>-V</B></A>   displays the version of the DCC <B>procmail(1)</B> interface.
+
+     <A NAME="OPTION-d"><B>-d</B></A>   enables debugging output from the DCC client software.  Additional
+          <B>-d</B> options increase the number of messages.  One causes error mes-
+          sages to be sent to STDERR as well as the system log.
+
+     <A NAME="OPTION-A"><B>-A</B></A>   adds to existing X-DCC headers (if any) of the brand of the current
+          server instead of replacing existing headers.
+
+     <A NAME="OPTION-Q"><B>-Q</B></A>   only queries the DCC server about the checksums of messages instead
+          of reporting and then querying.  This is useful when <B>dccproc</B> is used
+          to filter mail that has already been reported to a DCC server by
+          another DCC client such as <B><A HREF="dccm.html">dccm(8)</A></B>.  No single mail message should
+          be reported to a DCC server more than once per recipient.
+
+          It is better to use <I>MXDCC</I> lines in the <B>-w</B> <I>whiteclnt</I> file for your MX
+          mail servers that use DCC than <B>-Q</B>
+
+     <A NAME="OPTION-C"><B>-C</B></A>   outputs only the X-DCC header and the checksums for the message.
+
+     <A NAME="OPTION-H"><B>-H</B></A>   outputs only the X-DCC header.
+
+     <A NAME="OPTION-E"><B>-E</B></A>   adds lines to the start of the log file turned on with <B>-l</B> and <B>-c</B>
+          describing what might have been the envelope of the message.  The
+          information for the inferred envelope comes from arguments including
+          <B>-a</B> and headers in the message when <B>-R</B> is used.  No lines are gener-
+          ated for which no information is available, such as the envelope
+          recipient.
+
+     <A NAME="OPTION-R"><B>-R</B></A>   says the first Received lines have the standard
+          "helo (name [address])..."  format and the address is that of the
+          SMTP client that would otherwise be provided with <B>-a</B>.  The <B>-a</B> option
+          should be used if the local SMTP server adds a Received line with
+          some other format or does not add a Received line.  Received headers
+          specifying IP addresses marked <I>MX</I> or <I>MXDCC</I> in the <B>-w</B> <I>whiteclnt</I> file
+          are skipped.
+
+     <A NAME="OPTION-h"><B>-h</B></A> <I>homedir</I>
+          overrides the default DCC home directory, <I>@prefix@</I>.
+
+     <A NAME="OPTION-m"><B>-m</B></A> <I>map</I>
+          specifies a name or path of the memory mapped parameter file instead
+          of the default <I>map</I> in the DCC home directory.  It should be created
+          with the <B>new map</B> operation of the <B><A HREF="cdcc.html">cdcc(8)</A></B> command.
+
+     <A NAME="OPTION-w"><B>-w</B></A> <I>whiteclnt</I>
+          specifies an optional file containing SMTP client IP addresses and
+          SMTP headers of mail that do not need X-DCC headers and whose check-
+          sums should not be reported to the DCC server.  It can also contain
+          checksums of spam.  If the pathname is not absolute, it is relative
+          to the DCC home directory.  Thus, individual users with private
+          whitelists usually specify them with absolute paths.  Common
+          whitelists shared by users must be in the DCC home directory or one
+          of its subdirectories and owned by the set-UID user of <B>dccproc</B>.  It
+          is useful to <I>include</I> a common or system-wide whitelist in private
+          lists.
+
+          Because the contents of the <I>whiteclnt</I> file are used frequently, a
+          companion file is automatically created and maintained.  It has the
+          same pathname but with an added suffix of <I>.dccw</I>.  It contains a mem-
+          ory mapped hash table of the main file.
+
+          <I>Option</I> lines can be used to modify many aspects of <B>dccproc</B> filter-
+          ing, as described in the main <B><A HREF="dcc.html">dcc(8)</A></B> man page.  For example, an
+          <I>option</I> <I>spam-trap-accept</I> line turns off DCC filtering and reports the
+          message as spam.
+
+     <A NAME="OPTION-T"><B>-T</B></A> <I>tmpdir</I>
+          changes the default directory for temporary files from the system
+          default.  The system default is <I>/tmp</I>.
+
+     <A NAME="OPTION-a"><B>-a</B></A> <I>IP-address</I>
+          specifies the IP address (not the host name) of the immediately pre-
+          vious SMTP client.  It is often not available.  <B>-a</B> <I>0.0.0.0</I> is
+          ignored.  <B>-a</B>.  The <B>-a</B> option should be used instead of <B>-R</B> if the
+          local SMTP server adds a Received line with some other format or
+          does not add a Received line.
+
+     <A NAME="OPTION-f"><B>-f</B></A> <I>env</I><B>_</B><I>from</I>
+          specifies the RFC 821 envelope "Mail From" value with which the mes-
+          sage arrived.  It is often not available.  If <B>-f</B> is not present, the
+          contents of the first Return-Path: or UNIX style From_ header is
+          used.  The <I>env</I><B>_</B><I>from</I> string is often but need not be bracketed with
+          "&lt;&gt;".
+
+     <A NAME="OPTION-t"><B>-t</B></A> <I>targets</I>
+          specifies the number of addressees of the message if other than 1.
+          The string <I>many</I> instead of a number asserts that there were too many
+          addressees and that the message is unsolicited bulk email.
+
+     <A NAME="OPTION-x"><B>-x</B></A> <I>exitcode</I>
+          specifies the code or status with which <B>dccproc</B> exits if the <B>-c</B>
+          thresholds are reached or the <B>-w</B> <I>whiteclnt</I> file blacklists the mes-
+          sage.
+
+          The default value is EX_NOUSER.  EX_NOUSER is 67 on many systems.
+          Use 0 to always exit successfully.
+
+     <A NAME="OPTION-c"><B>-c</B></A> <I>type,</I>[<I>log-thold,</I>]<I>rej-thold</I>
+          sets logging and "spam" thresholds for checksum <I>type</I>.  The checksum
+          types are <I>IP</I>, <I>env</I><B>_</B><I>From</I>, <I>From</I>, <I>Message-ID</I>, <I>substitute</I>, <I>Received</I>,
+          <I>Body</I>, <I>Fuz1</I>, <I>Fuz2</I>, <I>rep-total</I>, and <I>rep</I>.  The first six, <I>IP</I> through
+          <I>substitute</I>, have no effect except when a local DCC server configured
+          with <B>-K</B> is used.  The <I>substitute</I> thresholds apply to the first sub-
+          stitute heading encountered in the mail message.  The string <I>ALL</I>
+          sets thresholds for all types, but is unlikely to be useful except
+          for setting logging thresholds.  The string <I>CMN</I> specifies the com-
+          monly used checksums <I>Body</I>, <I>Fuz1</I>, and <I>Fuz2</I>.  <I>Rej-thold</I> and <I>log-thold</I>
+          must be numbers, the string <I>NEVER</I>, or the string <I>MANY</I> indicating
+          millions of targets.  Counts from the DCC server as large as the
+          threshold for any single type are taken as sufficient evidence that
+          the message should be logged or rejected.
+
+          <I>Log-thold</I> is the threshold at which messages are logged.  It can be
+          handy to log messages at a lower threshold to find solicited bulk
+          mail sources such as mailing lists.  If no logging threshold is set,
+          only rejected mail and messages with complicated combinations of
+          white and blacklisting are logged.  Messages that reach at least one
+          of their rejection thresholds are logged regardless of logging
+          thresholds.
+
+          <I>Rej-thold</I> is the threshold at which messages are considered "bulk,"
+          and so should be rejected or discarded if not whitelisted.
+
+          DCC Reputation thresholds in the commercial version of the DCC are
+          controlled by thresholds on checksum types <I>rep</I> and <I>rep-total</I>.  Mes-
+          sages from an IP address that the DCC database says has sent more
+          than <B>-t</B> <I>rep-total,log-thold</I> messages are logged.  A DCC Reputation
+          is computed for messages received from IP addresses that have sent
+          more than <B>-t</B> <I>rep-total,log-thold</I> messages.  The DCC Reputation of an
+          IP address is the percentage of its messages that have been detected
+          as bulk or having at least 10 recipients.  The defaults are equiva-
+          lent to <B>-t</B> <I>rep,never</I> and <B>-t</B> <I>rep-total,never,20</I>.
+
+          Bad DCC Reputations do not reject mail unless enabled by an <I>option</I>
+          <I>DCC-rep-on</I> line in a <I>whiteclnt</I> file.
+
+          The checksums of locally whitelisted messages are not checked with
+          the DCC server and so only the number of targets of the current copy
+          of a whitelisted message are compared against the thresholds.
+
+          The default is <I>ALL,NEVER</I>, so that nothing is discarded, rejected, or
+          logged.  A common choice is <I>CMN,25,50</I> to reject or discard mail with
+          common bodies except as overridden by the whitelist of the DCC
+          server, the sendmail <I>${dcc</I><B>_</B><I>isspam}</I> and <I>${dcc</I><B>_</B><I>notspam}</I> macros, and
+          <B>-g</B>, and <B>-w</B>.
+
+     <A NAME="OPTION-g"><B>-g</B></A> [<I>not-</I>]<I>type</I>
+          indicates that whitelisted, <I>OK</I> or <I>OK2</I>, counts from the DCC server
+          for a type of checksum are to be believed.  They should be ignored
+          if prefixed with <I>not-</I>.  <I>Type</I> is one of the same set of strings as
+          for <B>-c</B>.  Only <I>IP</I>, <I>env</I><B>_</B><I>From</I>, and <I>From</I> are likely choices.  By default
+          all three are honored, and hence the need for <I>not-</I>.
+
+     <A NAME="OPTION-S"><B>-S</B></A> <I>hdr</I>
+          adds to the list of substitute or locally chosen headers that are
+          checked with the <B>-w</B> <I>whiteclnt</I> file and sent to the DCC server.  The
+          checksum of the last header of type <I>hdr</I> found in the message is
+          checked.  As many as 6 different substitute headers can be speci-
+          fied, but only the checksum of the first of the 6 will be sent to
+          the DCC server.
+
+     <A NAME="OPTION-i"><B>-i</B></A> <I>infile</I>
+          specifies an input file for the entire message instead of standard
+          input.  If not absolute, the pathname is interpreted relative to the
+          directory in which <B>dccproc</B> was started.
+
+     <A NAME="OPTION-o"><B>-o</B></A> <I>outfile</I>
+          specifies an output file for the entire message including headers
+          instead of standard output.  If not absolute, the pathname is inter-
+          preted relative to the directory in which <B>dccproc</B> was started.
+
+     <A NAME="OPTION-l"><B>-l</B></A> <I>logdir</I>
+          specifies a directory for copies of messages whose checksum target
+          counts exceed <B>-c</B> thresholds.  The format of each file is affected by
+          <B>-E</B>.
+
+          See the FILES section below concerning the contents of the files.
+          See also the <I>option</I> <I>log-subdirectory-{day,hour,minute}</I> lines in
+          <I>whiteclnt</I> files described in <B><A HREF="dcc.html">dcc(8)</A></B>.
+
+          The directory is relative to the DCC home directory if it is not
+          absolute
+
+     <A NAME="OPTION-B"><B>-B</B></A> <I>dnsbl-option</I>
+          enables DNS blacklist checks of the SMTP client IP address, SMTP
+          envelope Mail_From sender domain name, and of host names in URLs in
+          the message body.  Body URL blacklisting has too many false posi-
+          tives to use on abuse mailboxes.  It is less effective than
+          greylisting with <B><A HREF="dccm.html">dccm(8)</A></B> or <B><A HREF="dccifd.html">dccifd(8)</A></B> but can be useful in situa-
+          tions where greylisting cannot be used.
+
+          <I>Dnsbl-option</I> is either one of the <B>-B</B> <I>set:option</I> forms or
+              <B>-B</B> <I>domain</I>[<I>,IPaddr</I>[<I>/xx</I>[<I>,bltype</I>]]]
+          <I>Domain</I> is a DNS blacklist domain such as example.com that will be
+          searched.  <I>IPaddr</I>[<I>/xxx</I>] is the string "any" an IP address in the DNS
+          blacklist that indicates that the mail message should be rejected,
+          or a CIDR block covering results from the DNS blacklist.
+          "127.0.0.2" is assumed if <I>IPaddr</I> is absent.  IPv6 addresses can be
+          specified with the usual colon (:) notation.  Names can be used
+          instead of numeric addresses.  The type of DNS blacklist is speci-
+          fied by <I>bltype</I> as <I>name</I>, <I>IPv4</I>, or <I>IPv6</I>.  Given an envelope sender
+          domain name or a domain name in a URL of spam.domain.org and a
+          blacklist of type <I>name</I>, spam.domain.org.example.com will be tried.
+          Blacklist types of <I>IPv4</I> and <I>IPv6</I> require that the domain name in a
+          URL sender address be resolved into an IPv4 or IPv6 address.  The
+          address is then written as a reversed string of decimal octets to
+          check the DNS blacklist, as in 2.0.0.127.example.com,
+
+          More than one blacklist can be specified and blacklists can be
+          grouped.  All searching within a group is stopped at the first posi-
+          tive result.
+
+          Unlike <B><A HREF="dccm.html">dccm(8)</A></B> and <B><A HREF="dccifd.html">dccifd(8)</A></B>, no <I>option</I> <I>DNSBL-on</I> line is required in
+          the <I>whiteclnt</I> file.  A <B>-B</B> argument is sufficient to show that DNSBL
+          filtering is wanted by the <B>dccproc</B> user.
+
+          <B>-B</B> <I>set:no-client</I>
+               says that SMTP client IP addresses and reverse DNS domain names
+               should not be checked in the following blacklists.
+               <B>-B</B> <I>set:client</I> restores the default for the following black-
+               lists.
+
+          <B>-B</B> <I>set:no-mail</I><B>_</B><I>host</I>
+               says that SMTP envelope Mail_From sender domain names should
+               not be checked in the following blacklists.  <B>-B</B> <I>set:mail</I><B>_</B><I>host</I>
+               restores the default.
+
+          <B>-B</B> <I>set:no-URL</I>
+               says that URLs in the message body should not be checked in the
+               in the following blacklists.  <B>-B</B> <I>set:URL</I> restores the default.
+
+          <B>-B</B> <I>set:no-MX</I>
+               says MX servers of sender Mail_From domain names and host names
+               in URLs should not be checked in the following blacklists.
+               <B>-B</B> <I>set:MX</I> restores the default.
+
+          <B>-B</B> <I>set:no-NS</I>
+               says DNS servers of sender Mail_From domain names and host
+               names in URLs should not be checked in the following black-
+               lists.  <B>-B</B> <I>set:NS</I> restores the default.
+
+          <B>-B</B> <I>set:defaults</I>
+               is equivalent to all of <B>-B</B> <I>set:no-temp-fail</I> <B>-B</B> <I>set:client</I>
+               <B>-B</B> <I>set:mail</I><B>_</B><I>host</I> <B>-B</B> <I>set:URL</I> <B>-B</B> <I>set:MX</I> and <B>-B</B> <I>set:NS</I>
+
+          <B>-B</B> <I>set:group=X</I>
+               adds later DNS blacklists specified with
+                   <B>-B</B> <I>domain</I>[<I>,IPaddr</I>[<I>/xx</I>[<I>,bltype</I>]]]
+               to group 1, 2, or 3.
+
+          <B>-B</B> <I>set:debug=X</I>
+               sets the DNS blacklist logging level
+
+          <B>-B</B> <I>set:msg-secs=S</I>
+               limits <B>dccproc</B> to <I>S</I> seconds total for checking all DNS black-
+               lists.  The default is 25.
+
+          <B>-B</B> <I>set:URL-secs=S</I>
+               limits <B>dccproc</B> to at most <I>S</I> seconds resolving and checking any
+               single URL.  The default is 11.  Some spam contains dozens of
+               URLs and that some "spamvertised" URLs contain host names that
+               need minutes to resolve.  Busy mail systems cannot afford to
+               spend minutes checking each incoming mail message.
+
+     <A NAME="OPTION-L"><B>-L</B></A> <I>ltype,facility.level</I>
+          specifies how messages should be logged.  <I>Ltype</I> must be <I>error</I>, <I>info</I>,
+          or <I>off</I> to indicate which of the two types of messages are being con-
+          trolled or to turn off all <B>syslog(3)</B> messages from <B>dccproc</B>.  <I>Level</I>
+          must be a <B>syslog(3)</B> level among <I>EMERG</I>, <I>ALERT</I>, <I>CRIT</I>, <I>ERR</I>, <I>WARNING</I>,
+          <I>NOTICE</I>, <I>INFO</I>, and <I>DEBUG</I>.  <I>Facility</I> must be among <I>AUTH</I>, <I>AUTHPRIV</I>,
+          <I>CRON</I>, <I>DAEMON</I>, <I>FTP</I>, <I>KERN</I>, <I>LPR</I>, <I>MAIL</I>, <I>NEWS</I>, <I>USER</I>, <I>UUCP</I>, and <I>LOCAL0</I>
+          through <I>LOCAL7</I>.  The default is equivalent to
+                <B>-L</B> <I>info,MAIL.NOTICE</I> <B>-L</B> <I>error,MAIL.ERR</I>
+
+     <B>dccproc</B> exits with 0 on success and with the <B>-x</B> value if the <B>-c</B> thresh-
+     olds are reached or the <B>-w</B> <I>whiteclnt</I> file blacklists the message.  If at
+     all possible, the input mail message is output to standard output or the
+     <A NAME="OPTION-o"><B>-o</B></A> <I>outfile</I> despite errors.  If possible, error messages are put into the
+     system log instead of being mixed with the output mail message.  The exit
+     status is zero for errors so that the mail message will not be rejected.
+
+     If <B>dccproc</B> is run more than 500 times in fewer than 5000 seconds, <B>dccproc</B>
+     tries to start <B><A HREF="dccifd.html">Dccifd(8)</A></B>.  The attempt is made at most once per hour.
+     Dccifd is significantly more efficient than <B>dccproc</B>.  With luck, mecha-
+     nisms such as SpamAssassin will notice when dccifd is running and switch
+     to dccifd.
+
+
+</PRE>
+<H2><A NAME="FILES">FILES</A></H2><PRE>
+     <A NAME="FILE-@prefix@">@prefix@</A>   DCC home directory in which other files are found.
+     <A NAME="FILE-map">map</A>        memory mapped file in the DCC home directory of information
+                concerning DCC servers.
+     <A NAME="FILE-whiteclnt">whiteclnt</A>  contains the client whitelist in the format described in
+                <B><A HREF="dcc.html">dcc(8)</A></B>.
+     <A NAME="FILE-whiteclnt.dccw">whiteclnt.dccw</A>
+                is a memory mapped hash table corresponding to the <I>whiteclnt</I>
+                file.
+     <A NAME="FILE-tmpdir">tmpdir</A>     contains temporary files created and deleted as <B>dccproc</B> pro-
+                cesses the message.
+     <A NAME="FILE-logdir">logdir</A>     is an optional directory specified with <B>-l</B> and containing
+                marked mail.  Each file in the directory contains one message,
+                at least one of whose checksums reached one of its <B>-c</B> thresh-
+                olds.  The entire body of the SMTP message including its
+                header is followed by the checksums for the message.
+
+
+</PRE>
+<H2><A NAME="EXAMPLES">EXAMPLES</A></H2><PRE>
+     The following <B>procmailrc(5)</B> rule adds an X-DCC header to passing mail
+
+         :0 f
+         | /usr/local/bin/dccproc -ERw whiteclnt
+
+     This <B>procmailrc(5)</B> recipe rejects mail with total counts of 10 or larger
+     for the commonly used checksums:
+
+         :0 fW
+         | /usr/local/bin/dccproc -ERw whiteclnt -ccmn,10
+         :0 e
+         {
+             EXITCODE=67
+             :0
+             /dev/null
+         }
+
+
+</PRE>
+<H2><A NAME="SEE-ALSO">SEE ALSO</A></H2><PRE>
+     <B><A HREF="cdcc.html">cdcc(8)</A></B>, <B><A HREF="dcc.html">dcc(8)</A></B>, <B><A HREF="dbclean.html">dbclean(8)</A></B>, <B><A HREF="dccd.html">dccd(8)</A></B>, <B><A HREF="dblist.html">dblist(8)</A></B>, <B><A HREF="dccifd.html">dccifd(8)</A></B>, <B><A HREF="dccm.html">dccm(8)</A></B>,
+     <B><A HREF="dccsight.html">dccsight(8)</A></B>, <B>mail(1)</B>, <B>procmail(1)</B>.
+
+
+</PRE>
+<H2><A NAME="HISTORY">HISTORY</A></H2><PRE>
+     Distributed Checksum Clearinghouses are based on an idea of Paul Vixie.
+     Implementation of <B>dccproc</B> was started at Rhyolite Software in 2000.  This
+     document describes version 1.3.103.
+
+
+</PRE>
+<H2><A NAME="BUGS">BUGS</A></H2><PRE>
+     <B>dccproc</B> uses <B>-c</B> where <B><A HREF="dccm.html">dccm(8)</A></B> uses <B>-t</B>.
+
+                               February 26, 2009
+</PRE>
+<HR>
+<ADDRESS>
+Man(1) output converted with
+<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
+modified for the DCC $Date 2001/04/29 03:22:18 $
+<BR>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG SRC="http://logos.dcc-servers.net/border.png"
+            class=logo ALT="DCC logo">
+    </A>
+<A HREF="http://validator.w3.org/check?uri=referer">
+    <IMG class=logo ALT="Valid HTML 4.01 Strict"
+        SRC="http://www.w3.org/Icons/valid-html401">
+    </A>
+</ADDRESS>
+</BODY>
+</HTML>
diff -r 000000000000 -r c7f6b056b673 dccproc/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccproc/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,4 @@
+Makefile.in
+dccproc.c
+win32.mak
+.manifest
diff -r 000000000000 -r c7f6b056b673 dccproc/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccproc/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,51 @@
+# make the Distributed Checksum Clearinghouse `dccproc` program
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.13 $Revision$
+# @configure_input@
+
+DEPTH	=..
+PROG	=dccproc
+SRCS	=$(PROG).c
+
+@MAKE_PROG@
+
+@MAKE_DOT@ifndef NO_SUID
+# dccproc needs to be SUID to read the server passwords
+BINMODE	=4$(DCC_MODE)
+BINOWN	=@DCCSUID@
+@MAKE_DOT@endif
+@MAKE_INC2@
diff -r 000000000000 -r c7f6b056b673 dccproc/dccproc.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccproc/dccproc.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,1349 @@
+/* Distributed Checksum Clearinghouse server
+ *
+ * report a message for such as procmail
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.189 $Revision$
+ */
+
+#include "dcc_ck.h"
+#include "dcc_xhdr.h"
+#include "dcc_heap_debug.h"
+#include <signal.h>			/* for Linux and SunOS*/
+#ifndef DCC_WIN32
+#include <arpa/inet.h>
+#endif
+
+
+static DCC_EMSG dcc_emsg;
+
+static const char *mapfile_nm = DCC_MAP_NM_DEF;
+
+static u_char priv_logdir;
+static DCC_PATH log_path;
+static int lfd = -1;
+static struct timeval ldate;
+
+static u_char logging = 1;		/* 0=no log, 1=have file, 2=used it */
+static size_t log_size;
+
+static char id[DCC_MSG_ID_LEN+1];
+static DCC_PATH tmp_nm;
+static int tmp_fd = -1;
+static u_char tmp_rewound;
+static int hdrs_len, body_len;
+static u_char seen_hdr;
+
+static int exit_code = EX_NOUSER;
+static DCC_TGTS local_tgts;
+static u_char local_tgts_spam, local_tgts_set;
+static int total_hdrs, cr_hdrs;
+
+static const char* white_nm;
+static const char *ifile_nm = "stdin", *ofile_nm = "stdout";
+static FILE *ifile, *ofile;
+
+static DCC_CLNT_CTXT *ctxt;
+static char xhdr_fname[sizeof(DCC_XHDR_START)+sizeof(DCC_BRAND)+1];
+static int xhdr_fname_len;
+static u_char add_xhdr;			/* add instead of replace header */
+static u_char cksums_only;		/* output only checksums */
+static u_char x_dcc_only;		/* output only the X-DCC header */
+static u_char fake_envelope;		/* fake envelope log lines */
+static int std_received;		/* Received: line is standard */
+
+static ASK_ST ask_st;
+static FLTR_SWS rcpt_sws;
+static DCC_GOT_CKS cks;
+static DCC_CKS_WTGTS wtgts;
+static char helo[DCC_HELO_MAX];
+static char sender_name[DCC_MAXDOMAINLEN];
+static char sender_str[INET6_ADDRSTRLEN];
+static struct in6_addr clnt_addr;
+
+static char env_from_buf[DCC_HDR_CK_MAX+1];
+static const char *env_from = 0;
+
+static char mail_host[DCC_MAXDOMAINLEN];
+
+static DCC_HEADER_BUF header;
+
+static EARLY_LOG early_log;
+
+static void start_dccifd(void);
+static u_char check_mx_listing(void);
+static int get_hdr(char *, int);
+static void add_hdr(void *, const char *, u_int);
+static void tmp_write(const void *, int);
+static void tmp_close(void);
+static void scopy(int, u_char);
+static void log_write(const void *, int);
+static void log_body_write(const char *, u_int);
+static void thr_log_write(void *, const char *, u_int);
+static void log_late(void);
+static int log_print(u_char, const char *, ...) PATTRIB(2,3);
+#define LOG_CAPTION(s) log_write((s), LITZ(s))
+#define LOG_EOL() LOG_CAPTION("\n")
+static void log_fin(void);
+static void log_ck(void *, const char *, u_int);
+static void dccproc_error_msg(const char *, ...) PATTRIB(1,2);
+static void sigterm(int);
+
+
+static const char *usage_str =
+"[-VdAQCHER]  [-h homedir] [-m map] [-w whiteclnt] [-T tmpdir]\n"
+"   [-a IP-address] [-f env_from] [-t targets] [-x exitcode]\n"
+"   [-c type,[log-thold,][spam-thold]] [-g [not-]type] [-S header]\n"
+"   [-i infile] [-o outfile] [-l logdir] [-B dnsbl-option]\n"
+"   [-L ltype,facility.level]";
+
+static void NRATTRIB
+usage(const char* barg)
+{
+	if (barg) {
+		dcc_logbad(EX_USAGE, "unrecognized \"%s\"\nusage: %s\n",
+			   barg, usage_str);
+	} else {
+		dcc_logbad(EX_USAGE, "%s\n", usage_str);
+	}
+}
+
+
+
+int NRATTRIB
+main(int argc, char **argv)
+{
+	char buf[20*DCC_HDR_CK_MAX];	/* at least DCC_HDR_CK_MAX*3 */
+	u_char log_tgts_set = 0;
+	const char *homedir = 0;
+	const char *logdir = 0;
+	const char *tmpdir = 0;
+	u_char ask_result;
+	char *p;
+	const char *p2;
+	u_long l;
+	int error, blen, i;
+
+	/* because stderr is often mixed with stdout and effectively
+	 * invisible, also complain to syslog */
+	dcc_syslog_init(1, argv[0], 0);
+	dcc_clear_tholds();
+
+	/* get ready for the IP and From header checksums */
+	dcc_cks_init(&cks);
+
+	/* we must be SUID to read and write the system's common connection
+	 * parameter memory mapped file.  We also need to read the common
+	 * local white list and write the mmap()'ed hash file */
+	dcc_init_priv();
+
+	ofile = stdout;
+	ifile = stdin;
+	opterr = 0;
+	while ((i = getopt(argc, argv, "VdAQCHER"
+			   "r:h:m:w:T:a:f:g:S:t:x:c:i:o:l:B:L:")) != -1) {
+		switch (i) {
+		case 'V':
+			fprintf(stderr, DCC_VERSION"\n");
+			exit(EX_OK);
+			break;
+
+		case 'd':
+			++dcc_clnt_debug;
+			break;
+
+		case 'A':
+			add_xhdr = 1;
+			break;
+
+		case 'Q':
+			ask_st |= ASK_ST_QUERY;
+			break;
+
+		case 'C':
+			cksums_only = 1;
+			break;
+
+		case 'H':
+			x_dcc_only = 1;
+			break;
+
+		case 'E':
+			fake_envelope = 1;
+			break;
+
+		case 'R':
+			if (!std_received)
+				std_received = 1;
+			break;
+
+		case 'r':		/* a bad idea replacment for -R */
+			std_received = strtoul(optarg, &p, 10);
+			if (*p != '\0' || i == 0) {
+				dccproc_error_msg("invalid count"
+						  " \"-e %s\"", optarg);
+				std_received = 1;
+			}
+			break;
+
+		case 'h':
+			homedir = optarg;
+			break;
+
+		case 'm':
+			mapfile_nm = optarg;
+			break;
+
+		case 'w':
+			white_nm = optarg;
+			break;
+
+		case 'T':
+			tmpdir = optarg;
+			break;
+
+		case 'a':
+			/* ignore SpamAssassin noise */
+			if (!strcmp("0.0.0.0", optarg))
+				break;
+			dcc_host_lock();
+			if (!dcc_get_host(optarg, 2, &error)) {
+				dccproc_error_msg("\"-a %s\": %s",
+						  optarg, DCC_HSTRERROR(error));
+			} else {
+				if (dcc_hostaddrs[0].sa.sa_family == AF_INET)
+					dcc_ipv4toipv6(&clnt_addr,
+						       dcc_hostaddrs[0]
+						       .ipv4.sin_addr);
+				else
+					clnt_addr = (dcc_hostaddrs[0].ipv6
+						     .sin6_addr);
+				dcc_get_ipv6_ck(&cks, &clnt_addr);
+				dcc_ipv6tostr(sender_str, sizeof(sender_str),
+					      &clnt_addr);
+			}
+			dcc_host_unlock();
+			break;
+
+		case 'f':
+			env_from = optarg;
+			break;
+
+		case 'g':		/* honor not-spam "counts" */
+			dcc_parse_honor(optarg);
+			break;
+
+		case 'S':
+			if (!dcc_add_sub_hdr(dcc_emsg, optarg))
+				dcc_logbad(EX_USAGE, "%s", dcc_emsg);
+			break;
+
+		case 't':
+			if (!strcasecmp(optarg, "many")) {
+				local_tgts = 1;
+				local_tgts_spam = 1;
+				local_tgts_set = 1;
+			} else {
+				l = strtoul(optarg, &p, 10);
+				if (*p != '\0' || l > DCC_TGTS_RPT_MAX) {
+					dccproc_error_msg("invalid count"
+							" \"-t %s\"", optarg);
+				} else {
+					local_tgts = l;
+					local_tgts_spam = 0;
+					local_tgts_set = 1;
+				}
+			}
+			break;
+
+		case 'x':
+			l = strtoul(optarg, &p, 10);
+			if (*p != '\0') {
+				dccproc_error_msg("invalid exit code \"-x %s\"",
+						  optarg);
+			} else {
+				exit_code = l;
+			}
+			break;
+
+		case 'c':
+			if (dcc_parse_tholds("-c ", optarg))
+				log_tgts_set = 1;
+			break;
+
+		case 'i':
+			/* open the input file now, before changing to the
+			 * home DCC directory */
+			ifile_nm = optarg;
+			ifile = fopen(ifile_nm, "r");
+			if (!ifile)
+				dcc_logbad(EX_USAGE,
+					   "bad input file \"%s\": %s",
+					   ifile_nm, ERROR_STR());
+			break;
+
+		case 'o':
+			/* open the output file now, before changing to the
+			 * home DCC directory */
+			ofile_nm = optarg;
+			ofile = fopen(ofile_nm, "w");
+			if (!ofile)
+				dcc_logbad(EX_USAGE,
+					   "bad output file \"%s\": %s",
+					   ofile_nm, ERROR_STR());
+			break;
+
+		case 'l':
+			logdir = optarg;
+			break;
+
+		case 'B':
+			if (!dcc_parse_dnsbl(dcc_emsg, optarg, 0, 1))
+				dccproc_error_msg("%s", dcc_emsg);
+			break;
+
+#ifndef DCC_WIN32
+		case 'L':
+			dcc_parse_log_opt(optarg);
+			break;
+#endif
+
+		default:
+			usage(optopt2str(optopt));
+		}
+	}
+	if (argc != optind)
+		usage(argv[optind]);
+
+#ifdef SIGPIPE
+	signal(SIGPIPE, SIG_IGN);
+#endif
+#ifdef SIGHUP
+	signal(SIGHUP, sigterm);
+#endif
+	signal(SIGTERM, sigterm);
+	signal(SIGINT, sigterm);
+#ifdef SIGXFSZ
+	signal(SIGXFSZ, SIG_IGN);
+#endif
+
+	/* Close STDERR to keep it from being mixed with the message,
+	 * unless we are not going to output the message to STDOUT.
+	 * Ensure that stderr and file descriptor 2 are open to something
+	 * to prevent surprises from busybody libraries. */
+	if (!dcc_clnt_debug && !cksums_only && !x_dcc_only && !ofile_nm) {
+		close(STDERR_FILENO);
+		clean_stdio();
+	}
+
+	dcc_clnt_unthread_init();
+	dcc_cdhome(0, homedir, 0);
+	if (!dcc_main_logdir_init(dcc_emsg, logdir)) {
+		dcc_error_msg("%s", dcc_emsg);
+		/* dccproc will not be around as a daemon
+		 * when and if the log directory is created
+		 * so forget about a directory that might
+		 * someday be ok */
+		dcc_main_logdir[0] = '\0';
+	}
+	tmp_path_init(tmpdir, logdir);
+
+	if (dcc_main_logdir[0] == '\0') {
+		if (log_tgts_set)
+			dccproc_error_msg("log thresholds set with -c"
+					  " but no -l directory");
+		logging = 0;
+	} else {
+#ifndef DCC_WIN32
+		/* use privileges to make log files in the built-in home
+		 * directory */
+		if (!homedir
+		    && 0 > access(dcc_main_logdir, R_OK|W_OK|X_OK)) {
+			priv_logdir = 1;
+			dcc_get_priv_home(dcc_main_logdir);
+		}
+#endif
+		lfd = dcc_main_log_open(dcc_emsg, log_path, id, sizeof(id));
+		if (priv_logdir)
+			dcc_rel_priv();
+		if (lfd < 0) {
+			dccproc_error_msg("%s", dcc_emsg);
+			logging = 0;
+		}
+	}
+
+	if (fake_envelope && lfd >= 0) {
+		char date_buf[40];
+
+		gettimeofday(&ldate, 0);
+		log_print(0, DCC_LOG_DATE_PAT"\n",
+			  dcc_time2str(date_buf, sizeof(date_buf),
+				       DCC_LOG_DATE_FMT,
+				       ldate.tv_sec));
+	}
+
+	if (!local_tgts_set) {
+		local_tgts = (ask_st & ASK_ST_QUERY) ? 0 : 1;
+		local_tgts_spam = 0;
+	} else if (local_tgts == 0) {
+		ask_st |= ASK_ST_QUERY;
+		local_tgts_spam = 0;
+	} else if (ask_st & ASK_ST_QUERY) {
+		dcc_error_msg("\"-t %s\" is incompatible with \"-Q\"",
+			      local_tgts_spam
+			      ? "many"
+			      : dcc_tgts2str(buf, sizeof(buf), local_tgts, 0));
+		local_tgts = 0;
+		local_tgts_spam = 0;
+	}
+	if (local_tgts == DCC_TGTS_TOO_MANY) {
+		local_tgts = 1;
+		local_tgts_spam = 1;
+	}
+
+	if (logging
+	    || (!cksums_only && !x_dcc_only)) {
+		tmp_fd = dcc_mkstemp(dcc_emsg, tmp_nm, sizeof(tmp_nm),
+				     id, sizeof(id), 0,
+				     0, DCC_TMP_LOG_PREFIX, 1, 0);
+		if (tmp_fd < 0)
+			dcc_logbad(EX_IOERR, "%s", dcc_emsg);
+	}
+
+	/* start a connection to a DCC server
+	 *	we need the server's name for the X-DCC header. */
+	ctxt = dcc_clnt_start(dcc_emsg, 0, mapfile_nm,
+			      DCC_CLNT_FG_BAD_SRVR_OK
+			      | DCC_CLNT_FG_NO_PICK_SRVR
+			      | DCC_CLNT_FG_NO_FAIL);
+	if (ctxt) {
+		if (!homedir)
+			start_dccifd();
+		dcc_emsg[0] = '\0';
+		ctxt = dcc_clnt_start_fin(dcc_emsg, ctxt);
+	}
+	if (!ctxt) {
+		dccproc_error_msg("%s", dcc_emsg);
+	} else {
+		xhdr_fname_len = get_xhdr_fname(xhdr_fname, sizeof(xhdr_fname),
+						dcc_clnt_info);
+	}
+
+	/* get the local whitelist ready */
+	dcc_wf_init(&cmn_wf, DCC_WF_EITHER);
+	if (white_nm
+	    && !dcc_new_white_nm(dcc_emsg, &cmn_wf, white_nm)) {
+		dccproc_error_msg("%s", dcc_emsg);
+		white_nm = 0;
+	}
+	/* look past the SMTP client if it is a listed MX server */
+	if (sender_str[0] != '\0' && white_nm)
+		check_mx_listing();
+
+	dcc_dnsbl_init(&cks, ctxt, 0, id);
+
+	/* get the headers */
+	for (;;) {
+		int hlen;
+
+		hlen = get_hdr(buf, sizeof(buf));
+		if (hlen <= 2
+		    && (buf[0] == '\n'
+			|| (buf[0] == '\r' && buf[1] == '\n'))) {
+			/* stop at the separator between the body and headers */
+			if (!seen_hdr)
+				dcc_logbad(EX_DATAERR,
+					   "missing SMTP header lines");
+			hdrs_len -= hlen;
+			body_len = hlen;
+			break;
+		}
+
+#define GET_HDR_CK(h,t) {						\
+			if (!CLITCMP(buf, h)) {				\
+				dcc_get_cks(&cks,DCC_CK_##t, &buf[LITZ(h)], 1);\
+				seen_hdr = 1;				\
+				continue;}}
+		GET_HDR_CK(DCC_XHDR_TYPE_FROM":", FROM);
+		GET_HDR_CK(DCC_XHDR_TYPE_MESSAGE_ID":", MESSAGE_ID);
+#undef GET_HDR_CK
+
+		/* notice UNIX From_ line */
+		if (!seen_hdr
+		    && !env_from
+		    && parse_unix_from(buf, env_from_buf,
+				       sizeof(env_from_buf))) {
+			env_from = env_from_buf;
+			seen_hdr = 1;
+			continue;
+		}
+
+		if (!env_from && parse_return_path(buf, env_from_buf,
+						   sizeof(env_from_buf))) {
+			env_from = env_from_buf;
+			seen_hdr = 1;
+			continue;
+		}
+
+		if (!CLITCMP(buf, DCC_XHDR_TYPE_RECEIVED":")) {
+			seen_hdr = 1;
+
+			p2 = &buf[LITZ(DCC_XHDR_TYPE_RECEIVED":")];
+
+			/* compute checksum of the last Received: header */
+			dcc_get_cks(&cks, DCC_CK_RECEIVED, p2, 1);
+
+			/* pick IP address out of Nth Received: header
+			 * unless we had a good -a value */
+			if (sender_str[0] != '\0')
+				std_received = 0;
+			if (!std_received)
+				continue;
+			if (--std_received > 0)
+				continue;
+
+			p2 = parse_received(p2, &cks, helo, sizeof(helo),
+					    sender_str, sizeof(sender_str),
+					    sender_name, sizeof(sender_name));
+			if (p2 == 0) {
+				/* to avoid being fooled by forged Received:
+				 * fields, do not skip unrecognized forms */
+				std_received = 0;
+			} else if (*p2 != '\0') {
+				log_print(1, "skip %s Received: header\n", p2);
+				std_received = 1;
+			} else {
+				std_received = check_mx_listing();
+			}
+			continue;
+		}
+
+		/* Notice MIME multipart boundary definitions */
+		dcc_ck_mime_hdr(&cks, buf, 0);
+
+		if (dcc_ck_get_sub(&cks, buf, 0))
+			seen_hdr = 1;
+
+		/* notice any sort of header */
+		if (!seen_hdr) {
+			for (p = buf; ; ++p) {
+				if (*p == ':') {
+					seen_hdr = 1;
+					break;
+				}
+				if (*p <= ' ' || *p >= 0x7f)
+					break;
+			}
+		}
+	}
+	/* Create a checksum for a null Message-ID header if there
+	 * was no Message-ID header.  */
+	if (cks.sums[DCC_CK_MESSAGE_ID].type != DCC_CK_MESSAGE_ID)
+		dcc_get_cks(&cks, DCC_CK_MESSAGE_ID, "", 0);
+
+	/* Check DNS blacklists for STMP client and envelope sender
+	 * before collecting the body to avoid wasting time DNS resolving
+	 * URLs if the envelope answers the question.  Much of the DNS
+	 * work for the envelope has probably already been done. */
+	if (cks.sums[DCC_CK_IP].type == DCC_CK_IP)
+		dcc_client_dnsbl(cks.dnsbl, &cks.ip_addr, sender_name);
+
+	if (env_from) {
+		dcc_get_cks(&cks, DCC_CK_ENV_FROM, env_from, 1);
+		if (parse_mail_host(env_from, mail_host, sizeof(mail_host))) {
+			dcc_ck_get_sub(&cks, "mail_host", mail_host);
+			dcc_mail_host_dnsbl(cks.dnsbl, mail_host);
+		}
+	}
+
+	/* collect the body */
+	do {
+		blen = fread(buf, 1, sizeof(buf), ifile);
+		if (blen != sizeof(buf)) {
+			if (ferror(ifile))
+				dcc_logbad(EX_DATAERR, "fgets(%s): %s",
+					   ifile_nm, ERROR_STR());
+			if (!blen)
+				break;
+		}
+
+		tmp_write(buf, blen);
+		body_len += blen;
+		dcc_ck_body(&cks, buf, blen);
+	} while (!feof(ifile));
+	fclose(ifile);
+
+	dcc_cks_fin(&cks);
+
+	if (!unthr_ask_white(dcc_emsg, &ask_st, &rcpt_sws,
+			     white_nm, &cks, wtgts))
+		dccproc_error_msg("%s", dcc_emsg);
+
+	dcc_dnsbl_result(&ask_st, cks.dnsbl);
+
+	/* Unlike dccm and dccifd, no "option DNSBL-on" line is required in
+	 * the whiteclnt file.  A -B argument is sufficient to show that
+	 * DNSBL filtering is wanted. */
+	if (ask_st & ASK_ST_DNSBL_HIT_M)
+		ask_st |= (ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT);
+
+	if (ctxt) {
+		if (ask_st & ASK_ST_QUERY) {
+			local_tgts_spam = 0;
+			local_tgts = 0;
+		}
+		if (local_tgts != 0
+		    && (ask_st & ASK_ST_CLNT_ISSPAM))
+			local_tgts_spam = 1;
+
+		ask_result = unthr_ask_dcc(dcc_emsg, ctxt, &header, &ask_st,
+					   &cks, local_tgts_spam, local_tgts);
+		if (!ask_result)
+			dccproc_error_msg("%s", dcc_emsg);
+	}
+
+	if (fake_envelope && lfd >= 0) {
+		if (sender_str[0] != '\0') {
+			LOG_CAPTION(DCC_XHDR_TYPE_IP": ");
+			log_write(sender_name, strlen(sender_name));
+			LOG_CAPTION(" ");
+			log_write(sender_str, strlen(sender_str));
+			LOG_EOL();
+		}
+		if (helo[0] != '\0') {
+			LOG_CAPTION("HELO: ");
+			log_write(helo, strlen(helo));
+			LOG_EOL();
+			dcc_ck_get_sub(&cks, "helo", helo);
+		}
+		if (env_from) {
+			LOG_CAPTION(DCC_XHDR_TYPE_ENV_FROM": ");
+			log_write(env_from, strlen(env_from));
+			log_print(0, "  mail_host=%s", mail_host);
+			LOG_EOL();
+		}
+		LOG_EOL();
+	}
+
+	/* copy the headers to the log file and the output */
+	scopy(hdrs_len, 1);
+
+	/* emit the X-DCC and external filter headers
+	 *	End them with "\r\n" if at least half of the header lines
+	 *	ended that way.  Otherwise use "\n" */
+	if (header.buf[0] != '\0')
+		xhdr_write(add_hdr, 0, header.buf, header.used,
+			   cr_hdrs > total_hdrs/2);
+
+	/* emit body */
+	scopy(body_len, 1);
+
+	LOG_CAPTION(DCC_LOG_MSG_SEP);
+
+	log_late();
+
+	/* make the log file look like a dccm or dccifd log file */
+	if (fake_envelope) {
+		DCC_PATH abs_nm;
+
+		if (ask_st & ASK_ST_WLIST_NOTSPAM)
+			log_print(0, "%s"DCC_XHDR_ISOK"\n",
+				  fnm2abs_err(abs_nm, white_nm));
+		else if (ask_st & ASK_ST_WLIST_ISSPAM)
+			log_print(0, "%s%s\n",
+				  fnm2abs_err(abs_nm, white_nm),
+				  (rcpt_sws & FLTR_SW_TRAP_ACC)
+				  ? "-->"DCC_XHDR_TRAP_ACC
+				  : (rcpt_sws & FLTR_SW_TRAP_REJ)
+				  ? "-->"DCC_XHDR_TRAP_REJ
+				  : DCC_XHDR_ISSPAM);
+		log_ask_st(thr_log_write, 0, ask_st, rcpt_sws, 0, &header);
+	}
+
+	/* log error message for most prematurely closed output pipes before
+	 * logging the checksums or sending them to the pipe for -C */
+	if (EOF == fflush(ofile)) {
+		dcc_error_msg("fflush(%s): %s", ofile_nm, ERROR_STR());
+		fclose(ofile);
+		ofile = 0;
+	}
+
+	dcc_print_cks(log_ck, 0, local_tgts_spam, local_tgts, &cks, wtgts, 0);
+
+	if (ofile) {
+		if (fclose(ofile))
+			dcc_logbad(EX_IOERR, "fclose(%s): %s",
+				   ofile_nm, ERROR_STR());
+		ofile = 0;
+	}
+
+	/* Exit saying it was spam unless this is an accepting spam trap.
+	 * Spam traps report all mail as spam, but expect to receive it for
+	 * logging or analysis. */
+	if (((ask_st & ASK_ST_CLNT_ISSPAM)
+	     || ((ask_st & ASK_ST_SRVR_ISSPAM)
+		 && !(rcpt_sws & FLTR_SW_DCC_OFF))
+	     || ((ask_st & ASK_ST_REP_ISSPAM)
+		 && (rcpt_sws & FLTR_SW_REP_ON)))
+	    && !(rcpt_sws & FLTR_SW_TRAP_ACC)) {
+		p2 = "\n"DCC_XHDR_RESULT DCC_XHDR_RESULT_REJECT"\n";
+
+	} else {
+		p2 = "\n"DCC_XHDR_RESULT DCC_XHDR_RESULT_ACCEPT"\n";
+		exit_code = EX_OK;
+	}
+	if (fake_envelope)
+		log_write(p2, strlen(p2));
+
+	log_fin();
+	exit(exit_code);
+
+#ifdef DCC_WIN32
+	return 0;
+#endif
+}
+
+
+
+static void
+start_dccifd(void)
+{
+#ifndef DCC_WIN32
+	time_t t;
+	int c;
+	pid_t pid;
+
+	assert_info_locked();
+
+	/* once an hour,
+	 * start dccifd if dccproc is run more often than
+	 * DCCPROC_MAX_CREDITS times at an average rate of at least
+	 * DCCPROC_COST times per second */
+
+	t = (ctxt->start.tv_sec/DCCPROC_COST
+	     - dcc_clnt_info->dccproc_last/DCCPROC_COST);
+	if (t > DCCPROC_MAX_CREDITS*2)	/* don't overflow */
+		t = DCCPROC_MAX_CREDITS*2;
+	else if (t < 0)
+		t = 0;
+	c = t + dcc_clnt_info->dccproc_c;
+	if (c > DCCPROC_MAX_CREDITS)
+		c = DCCPROC_MAX_CREDITS;
+	--c;
+	if (c < -DCCPROC_MAX_CREDITS)
+		c = -DCCPROC_MAX_CREDITS;
+	dcc_clnt_info->dccproc_c = c;
+	dcc_clnt_info->dccproc_last = ctxt->start.tv_sec;
+
+	if (dcc_clnt_info->dccproc_c >= 0)
+		return;
+
+	if (!DCC_IS_TIME(ctxt->start.tv_sec,
+			 dcc_clnt_info->dccproc_dccifd_try,
+			 DCCPROC_TRY_DCCIFD))
+		return;
+	dcc_clnt_info->dccproc_dccifd_try = (ctxt->start.tv_sec
+					     + DCCPROC_TRY_DCCIFD);
+	pid = fork();
+	if (pid) {
+		if (pid < 0)
+			dccproc_error_msg("fork(): %s", ERROR_STR());
+		return;
+	}
+
+	close(STDIN_FILENO);
+	close(STDOUT_FILENO);
+	close(STDERR_FILENO);
+	clean_stdio();
+
+	dcc_get_priv();
+	setuid(dcc_effective_uid);
+	setgid(dcc_effective_gid);
+
+	dcc_trace_msg("try to start dccifd");
+	execl(DCC_LIBEXECDIR"/start-dccifd",
+	      "start-dccifd", "-A", (const char *)0);
+	dcc_trace_msg("exec("DCC_LIBEXECDIR"/start-dccifd): %s", ERROR_STR());
+	exit(0);
+#endif /* DCC_WIN32 */
+}
+
+
+
+/* If the immediate SMTP client because it is a listed MX server,
+ *	then we must ignore its IP address and keep looking for the
+ *	real SMTP client. */
+static u_char				/* 1=listed MX server */
+check_mx_listing(void)
+{
+	DCC_TGTS tgts;
+
+	if (!dcc_white_mx(dcc_emsg, &tgts, &cks))
+		dccproc_error_msg("%s", dcc_emsg);
+
+	if (tgts == DCC_TGTS_OK) {
+		return 0;
+	}
+
+	if (tgts == DCC_TGTS_SUBMIT_CLIENT) {
+		log_print(1, "%s is a listed 'submit' client\n",
+			  dcc_trim_ffff(sender_str));
+		return 0;
+	}
+
+	if (tgts == DCC_TGTS_OK_MXDCC) {
+		log_print(1, "%s is a whitelisted MX server with DCC client\n",
+			  dcc_trim_ffff(sender_str));
+		ask_st |= ASK_ST_QUERY;
+	} else if (tgts == DCC_TGTS_OK_MX) {
+		log_print(1, "%s is a whitelisted MX server\n",
+			  dcc_trim_ffff(sender_str));
+	} else {
+		return 0;
+	}
+
+	sender_str[0] = '\0';
+	dcc_unget_ip_ck(&cks);
+
+	/* tell caller to look at the next Received: header */
+	return 1;
+}
+
+
+
+/* send a new header to the output and the log */
+static void
+add_hdr(void *wp0 UATTRIB, const char *buf, u_int buf_len)
+{
+	u_int i;
+	const char *estr;
+
+	log_body_write(buf, buf_len);
+	if (ofile) {
+		i = fwrite(buf, 1, buf_len, ofile);
+		if (i != buf_len) {
+			estr = ERROR_STR();
+			fclose(ofile);
+			ofile = 0;
+			dcc_logbad(EX_IOERR, "fwrite(add_hdr %s,%d)=%d: %s",
+				   ofile_nm, buf_len, i, estr);
+		}
+	}
+}
+
+
+
+/* get the next header line */
+static int				/* header length */
+get_hdr(char *buf,
+	int buflen)			/* >DCC_HDR_CK_MAX*3 */
+{
+	u_char no_copy;
+	int hlen, wpos;
+	const char *line;
+	char c;
+	int llen, i;
+
+	no_copy = 0;
+	hlen = wpos = 0;
+	for (;;) {
+		line = fgets(&buf[hlen], buflen-hlen, ifile);
+		if (!line) {
+			if (ferror(ifile))
+				dcc_logbad(EX_DATAERR, "fgets(%s): %s",
+					   ifile_nm, ERROR_STR());
+			else
+				dcc_logbad(EX_DATAERR, "missing message body");
+		}
+		llen = strlen(line);
+
+		/* delete our X-DCC header at the start of a field */
+		if (hlen == 0 && !add_xhdr
+		    && is_xhdr(buf, llen)) {
+			seen_hdr = 1;
+			no_copy = 1;
+		}
+
+		/* do not crash on too-long headers */
+		hlen += llen;
+		if (hlen > DCC_HDR_CK_MAX*2) {
+			/* truncate headers too big for our buffer */
+			if (!no_copy
+			    && ((i = (hlen - wpos)) > 0)) {
+				tmp_write(&buf[wpos], i);
+				hdrs_len += i;
+			}
+			c = buf[hlen-1];
+			hlen = DCC_HDR_CK_MAX;
+			buf[hlen++] = '\r';
+			buf[hlen++] = '\n';
+			wpos = hlen;
+			if (c != '\n')
+				continue;
+		}
+
+		/* get the next character after the end-of-line to see if
+		 * the next line is a continuation */
+		if (hlen > 2) {
+			i = getc(ifile);
+			if (i != EOF)
+				ungetc(i, ifile);
+			if (i == ' ' || i == '\t')
+				continue;
+		}
+
+		/* not a continuation, so stop reading the field */
+		++total_hdrs;
+		/* notice if this line ended with "\r\n" */
+		if (hlen > 1 && buf[hlen-2] == '\r')
+			++cr_hdrs;
+
+		if (!no_copy) {
+			i = hlen - wpos;
+			if (i > 0) {
+				tmp_write(&buf[wpos], hlen-wpos);
+				hdrs_len += i;
+			}
+			return hlen;
+		}
+
+		/* at the end of our X-DCC header, look for another */
+		no_copy = 0;
+		hlen = wpos = 0;
+	}
+}
+
+
+
+static void
+tmp_write(const void *buf, int len)
+{
+	int i;
+
+	if (tmp_fd < 0)
+		return;
+
+	if (tmp_rewound)
+		dcc_logbad(EX_SOFTWARE, "writing to rewound temp file");
+
+	i = write(tmp_fd, buf, len);
+	if (i != len) {
+		if (i < 0)
+			dcc_logbad(EX_IOERR, "write(%s,%d): %s",
+				   tmp_nm, len, ERROR_STR());
+		else
+			dcc_logbad(EX_IOERR, "write(%s,%d)=%d",
+				   tmp_nm, len, i);
+	}
+}
+
+
+
+static void
+tmp_close(void)
+{
+	if (tmp_fd >= 0) {
+		if (0 < close(tmp_fd))
+			dcc_error_msg("close(%s): %s", tmp_nm, ERROR_STR());
+		tmp_fd = -1;
+	}
+}
+
+
+
+/* copy some of the temporary file to the output */
+static void
+scopy(int total_len,			/* copy this much of temporary file */
+      u_char complain)			/* 1=ok to complain about problems */
+{
+	char buf[BUFSIZ];
+	const char *estr;
+	int buf_len, data_len, i;
+
+	if (tmp_fd < 0)
+		return;
+
+	/* if the temporary file has not been rewound,
+	 * then rewind it now */
+	if (!tmp_rewound) {
+		tmp_rewound = 1;
+		if (0 > lseek(tmp_fd, 0, SEEK_SET)) {
+			estr = ERROR_STR();
+			tmp_close();
+			if (complain)
+				dcc_logbad(EX_IOERR, "rewind(%s): %s",
+					   tmp_nm, estr);
+		}
+	}
+
+	while (total_len > 0) {
+		buf_len = sizeof(buf);
+		if (buf_len > total_len) {
+			buf_len = total_len;
+		}
+
+		data_len = read(tmp_fd, buf, buf_len);
+		if (data_len <= 0) {
+			estr = ERROR_STR();
+			tmp_close();
+			if (data_len < 0)
+				dcc_logbad(EX_IOERR, "read(%s, %d): %s",
+					   tmp_nm, data_len, estr);
+			if (complain)
+				dcc_logbad(EX_IOERR, "premature end of %s",
+					   tmp_nm);
+			return;
+		}
+
+		log_body_write(buf, data_len);
+
+		if (ofile && (!cksums_only && !x_dcc_only)) {
+			i = fwrite(buf, 1, data_len, ofile);
+			if (i != data_len) {
+				estr = ERROR_STR();
+				fclose(ofile);
+				ofile = 0;
+				if (complain)
+					dcc_logbad(EX_IOERR,
+						   "fwrite(scopy %s, %d)=%d:"
+						   " %s",
+						   ofile_nm, data_len, i, estr);
+				tmp_close();
+				return;
+			}
+		}
+
+		total_len -= data_len;
+	}
+}
+
+
+
+static void
+log_write(const void *buf, int len)
+{
+	int i;
+
+	if (lfd < 0)
+		return;
+
+	i = write(lfd, buf, len);
+	if (i == len) {
+		logging = 2;
+		log_size += len;
+	} else {
+		dcc_error_msg("write(log %s): %s", log_path, ERROR_STR());
+		dcc_log_close(0, log_path, lfd, &ldate);
+		lfd = -1;
+		logging = 0;
+		log_path[0] = '\0';
+	}
+}
+
+
+
+static void
+log_body_write(const char *buf, u_int buflen)
+{
+	int trimlen;
+	const char *p, *lim;
+
+	if (lfd < 0)
+		return;
+
+	/* just write if there is room */
+	trimlen = MAX_LOG_KBYTE*1024 - log_size;
+	if (trimlen >= (int)buflen) {
+		log_write(buf, buflen);
+		return;
+	}
+
+	/* do nothing if too much already written */
+	if (trimlen < 0)
+		return;
+
+	/* look for and end-of-line near the end of the buffer
+	 * so that we can make the truncation pretty */
+	lim = buf;
+	p = lim+trimlen;
+	if (trimlen > 90)
+		lim += trimlen-90;
+	while (--p > lim) {
+		if (*p == '\n') {
+			trimlen = p-buf+1;
+			break;
+		}
+	}
+	log_write(buf, trimlen);
+	if (buf[trimlen-1] != '\n')
+		LOG_EOL();
+	LOG_CAPTION(DCC_LOG_TRN_MSG_CR);
+	log_size = MAX_LOG_KBYTE*1024+1;
+}
+
+
+
+static void
+thr_log_write(void *context UATTRIB, const char *buf, u_int len)
+{
+	log_write(buf, len);
+}
+
+
+
+/* does not append '\n' */
+static int
+vlog_print(u_char error, const char *p, va_list args)
+{
+	char logbuf[LOGBUF_SIZE];
+	int i;
+
+	/* buffer the message if we cannot write to the log file */
+	if (error &&  (lfd < 0 || !tmp_rewound))
+		return dcc_vearly_log(&early_log, p, args);
+
+	if (lfd < 0)
+		return 0;
+	i = vsnprintf(logbuf, sizeof(logbuf), p, args);
+	if (i >= ISZ(logbuf))
+		i = sizeof(logbuf)-1;
+	log_write(logbuf, i);
+	return i;
+}
+
+
+
+static void
+log_late(void)
+{
+	if (early_log.len) {
+		log_write(early_log.buf, early_log.len);
+		early_log.len = 0;
+	}
+}
+
+
+
+/* does not append '\n' */
+static int PATTRIB(2,3)
+log_print(u_char error, const char *p, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, p);
+	i = vlog_print(error, p, args);
+	va_end(args);
+	return i;
+}
+
+
+
+/* does not append '\n' */
+int
+thr_log_print(void *cp UATTRIB, u_char error, const char *p, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, p);
+	i = vlog_print(error, p, args);
+	va_end(args);
+	return i;
+}
+
+
+
+static void
+log_fin(void)
+{
+	if (log_path[0] == '\0')
+		return;
+
+	/* Close before renaming to accomodate WIN32 foolishness.
+	 * Assuming dcc_mkstemp() works properly, there is no race */
+	dcc_log_close(0, log_path, lfd, &ldate);
+	lfd = -1;
+#ifndef DCC_WIN32
+	if (priv_logdir)
+		dcc_get_priv_home(dcc_main_logdir);
+#endif
+	if (!(ask_st & ASK_ST_LOGIT)
+	    || !dcc_log_keep(0, log_path)) {
+		if (0 > unlink(log_path))
+			dccproc_error_msg("unlink(%s): %s",
+					  log_path, ERROR_STR());
+		log_path[0] = '\0';
+	}
+	if (priv_logdir)
+		dcc_rel_priv();
+}
+
+
+
+static void
+log_ck(void *arg UATTRIB, const char *buf, u_int buf_len)
+{
+	if (cksums_only && ofile)
+		fputs(buf, ofile);
+	log_write(buf, buf_len);
+}
+
+
+
+/* try to send error message to dccproc log file as well as sendmail */
+static int
+dccproc_verror_msg(const char *p, va_list args)
+{
+	char logbuf[LOGBUF_SIZE];
+
+	/* Some systems including Linux with gcc 3.4.2 on AMD 64 processors
+	 * do not allow two uses of a va_list but requires va_copy()
+	 * Other systems do not have any notion of va_copy(). */
+	if (vsnprintf(logbuf, sizeof(logbuf), p, args) >= ISZ(logbuf))
+		strcpy(&logbuf[ISZ(logbuf)-sizeof("...")], "...");
+
+	dcc_error_msg(logbuf);
+
+	ask_st |= ASK_ST_LOGIT;
+	return log_print(1, "%s\n", logbuf);
+}
+
+
+
+/* try to send error message to dccproc log file as well as sendmail */
+static void PATTRIB(1,2)
+dccproc_error_msg(const char *p, ...)
+{
+	va_list args;
+
+	va_start(args, p);
+	dccproc_verror_msg(p, args);
+	va_end(args);
+}
+
+
+
+int
+thr_error_msg(void *cp UATTRIB, const char *p, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, p);
+	i = dccproc_verror_msg(p, args);
+	va_end(args);
+
+	return i;
+}
+
+
+
+void
+thr_trace_msg(void *cp UATTRIB, const char *p, ...)
+{
+	va_list args;
+
+	va_start(args, p);
+	dccproc_verror_msg(p, args);
+	va_end(args);
+}
+
+
+
+/* things are so sick that we must bail out */
+void NRATTRIB
+dcc_logbad(int ex_code, const char *p, ...)
+{
+	char buf[BUFSIZ];
+	va_list args;
+	size_t len;
+
+	log_late();
+	if (*p >= ' ' && !tmp_rewound) {
+		va_start(args, p);
+		dcc_vfatal_msg(p, args);
+		va_end(args);
+
+		ask_st |= ASK_ST_LOGIT;
+		if (logging > 1)
+			log_write("\n", 1);
+		va_start(args, p);
+		vlog_print(0, p, args);
+		va_end(args);
+		log_write("\n\n", 2);
+		p = 0;
+	}
+
+	/* copy first from the temporary file and then the input
+	 * to try to ensure that we don't lose mail */
+	scopy(INT_MAX, 0);
+	if (ifile && ofile && !cksums_only && !x_dcc_only) {
+		do {
+			len = fread(buf, 1, sizeof(buf), ifile);
+			if (!len)
+				break;
+			log_write(buf, len);
+		} while (len == fwrite(buf, 1, len, ofile));
+	}
+
+	if (p && *p >= ' ') {
+		va_start(args, p);
+		dcc_vfatal_msg(p, args);
+		va_end(args);
+
+		log_write("\n\n", 2);
+		va_start(args, p);
+		vlog_print(0,p, args);
+		va_end(args);
+		log_write("\n", 1);
+	}
+	log_fin();
+
+	if (ex_code == EX_SOFTWARE)
+		abort();
+	exit(EX_OK);			/* don't tell procmail to reject mail */
+}
+
+
+
+/* watch for fatal signals */
+static void NRATTRIB
+sigterm(int sig)
+{
+	log_fin();
+	exit(-sig);
+}
diff -r 000000000000 -r c7f6b056b673 dccproc/win32.mak
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccproc/win32.mak	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,53 @@
+# Makefile for dccproc for WIN32.
+
+#   This assumes Borland's free command line tools FreeCommandLineTools.exe 
+#   available in 2004 at
+#	http://www.borland.com/products/downloads/download_cbuilder.html
+#   and elsewhere
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.7 $Revision$
+
+!include "../win32.makinc1"
+
+TARGET	=dccproc
+SRCS	=$(TARGET).c
+OBJS	=$(SRCS:.c=.obj)
+
+dccproc: $(OBJS) ../dcclib/dcclib.lib
+	$(CC) $(LFLAGS) $?
+
+!include "../win32.makinc2"
diff -r 000000000000 -r c7f6b056b673 dccsight.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccsight.0	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,99 @@
+dccsight(8)           Distributed Checksum Clearinghouse           dccsight(8)
+
+NNAAMMEE
+     ddccccssiigghhtt -- Distributed Checksum Clearinghouse raw checksum interface
+
+SSYYNNOOPPSSIISS
+     ddccccssiigghhtt [--VVddQQCC] [--hh _h_o_m_e_d_i_r] [--mm _m_a_p] [--ww _w_h_i_t_e_c_l_n_t] [--tt _t_a_r_g_e_t_s]
+              [--ii _i_n_f_i_l_e] [--LL _l_t_y_p_e_,_f_a_c_i_l_i_t_y_._l_e_v_e_l]
+
+DDEESSCCRRIIPPTTIIOONN
+     DDccccssiigghhtt reads one or more lines containing DCC checksums, reports them
+     to a DCC server, and writes a X-DCC header line.  It can be used to
+     report checksums obtained from dccproc(8) using --CC.
+
+   OOPPTTIIOONNSS
+     The following options are available:
+
+     --VV   displays the version of the DCC raw checksum interface.
+
+     --dd   enables debugging output from the DCC client software.  Additional
+          --dd options increase the number of messages.
+
+     --QQ   only queries the DCC server about the checksums instead of reporting
+          and then querying.  This is useful when ddccccssiigghhtt is used to filter
+          mail that has already been reported to a DCC server by another DCC
+          client such as dccm(8).  This can also be useful when applying a
+          private white or black list to mail that has already been reported
+          to a DCC server.  No single mail message should be reported to a DCC
+          server more than once per recipient, such as would happen if
+          ddccccssiigghhtt is not given --QQ when processing a stream of mail that has
+          already been seen by a DCC client.  Additional reports of a message
+          increase its apparent "bulkness."
+
+     --CC   outputs the checksums for the message as well as the X-DCC header.
+
+     --hh _h_o_m_e_d_i_r
+          overrides the default DCC home directory, _/_v_a_r_/_d_c_c.
+
+     --mm _m_a_p
+          specifies a name or path of the memory mapped parameter file instead
+          of the default _m_a_p in the DCC home directory.  It should be created
+          with the cdcc(8) command.
+
+     --ww _w_h_i_t_e_c_l_n_t
+          specifies an optional file containing SMTP client IP addresses and
+          SMTP headers of mail that do not need X-DCC headers and whose check-
+          sums should not be reported to the DCC server.  It can also contain
+          checksums of spam.  If the pathname is not absolute, it is relative
+          to the DCC home directory.  Thus, individual users with private
+          whitelists usually specify them with absolute paths.  It is useful
+          to _i_n_c_l_u_d_e a common or system-wide whitelist in private lists.
+
+          The format of the ddccccssiigghhtt whiteclnt file is the same as the
+          _w_h_i_t_e_l_i_s_t file required by dbclean(8) and dccsight(8).  Because this
+          list is used frequently, a companion file is used.  It has the same
+          pathname but with an added suffix of _._d_c_c_w.  After being created
+          empty, it will contain an automatic memory mapped hash table of the
+          main file.
+
+     --tt _t_a_r_g_e_t_s
+          specifies the number of addressees of the message if other than 1.
+          The string _m_a_n_y instead of a number asserts that there were too many
+          addressees and that the message is unsolicited bulk email.
+
+     --ii _i_n_f_i_l_e
+          specifies an input file instead of standard input.  If not absolute,
+          the pathname is interpreted relative to the directory in which
+          ddccccssiigghhtt was started.
+
+     --LL _l_t_y_p_e_,_f_a_c_i_l_i_t_y_._l_e_v_e_l
+          specifies how messages should be logged.  _L_t_y_p_e must be _e_r_r_o_r, _i_n_f_o,
+          or _o_f_f to indicate which of the two types of messages are being con-
+          trolled or to turn off all syslog(3) messages from ddccccssiigghhtt.  _L_e_v_e_l
+          must be a syslog(3) level among _E_M_E_R_G, _A_L_E_R_T, _C_R_I_T, _E_R_R, _W_A_R_N_I_N_G,
+          _N_O_T_I_C_E, _I_N_F_O, and _D_E_B_U_G.  _F_a_c_i_l_i_t_y must be among _A_U_T_H, _A_U_T_H_P_R_I_V,
+          _C_R_O_N, _D_A_E_M_O_N, _F_T_P, _K_E_R_N, _L_P_R, _M_A_I_L, _N_E_W_S, _U_S_E_R, _U_U_C_P, and _L_O_C_A_L_0
+          through _L_O_C_A_L_7.  The default is equivalent to
+                --LL _i_n_f_o_,_M_A_I_L_._N_O_T_I_C_E --LL _e_r_r_o_r_,_M_A_I_L_._E_R_R
+
+     ddccccssiigghhtt exits 0 on success, and >0 if an error occurs.
+
+FFIILLEESS
+     /var/dcc   DCC home directory in which other files are found.
+     map        memory mapped file in the DCC home directory of information
+                concerning DCC servers.
+     whiteclnt  contains the client whitelist in the format described in
+                dcc(8).
+     whiteclnt.dccw
+                memory mapped hash table of the _w_h_i_t_e_c_l_n_t file.
+
+SSEEEE AALLSSOO
+     cdcc(8), dcc(8), dbclean(8), dccd(8), dblist(8), dccproc(8), dccm(8),
+     dccifd(8), mail(1), procmail(1).
+
+HHIISSTTOORRYY
+     Implementation of ddccccssiigghhtt was started at Rhyolite Software in 2000.
+     This document describes version 1.3.103.
+
+                               February 26, 2009
diff -r 000000000000 -r c7f6b056b673 dccsight.8.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccsight.8.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,223 @@
+.\" Copyright (c) 2008 by Rhyolite Software, LLC
+.\"
+.\" This agreement is not applicable to any entity which sells anti-spam
+.\" solutions to others or provides an anti-spam solution as part of a
+.\" security solution sold to other entities, or to a private network
+.\" which employs the DCC or uses data provided by operation of the DCC
+.\" but does not provide corresponding data to other users.
+.\"
+.\" Permission to use, copy, modify, and distribute this software without
+.\" changes for any purpose with or without fee is hereby granted, provided
+.\" that the above copyright notice and this permission notice appear in all
+.\" copies and any distributed versions or copies are either unchanged
+.\" or not called anything similar to "DCC" or "Distributed Checksum
+.\" Clearinghouse".
+.\"
+.\" Parties not eligible to receive a license under this agreement can
+.\" obtain a commercial license to use DCC by contacting Rhyolite Software
+.\" at sales@rhyolite.com.
+.\"
+.\" A commercial license would be for Distributed Checksum and Reputation
+.\" Clearinghouse software.  That software includes additional features.  This
+.\" free license for Distributed ChecksumClearinghouse Software does not in any
+.\" way grant permision to use Distributed Checksum and Reputation Clearinghouse
+.\" software
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+.\" 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.
+.\"
+.\" Rhyolite Software DCC 1.3.103-1.28 $Revision$
+.\"
+.Dd February 26, 2009
+.ds volume-ds-DCC Distributed Checksum Clearinghouse
+.Dt dccsight 8 DCC
+.Os " "
+.Sh NAME
+.Nm dccsight
+.Nd Distributed Checksum Clearinghouse raw checksum interface
+.Sh SYNOPSIS
+.Nm dccsight
+.Bk -words
+.Op Fl VdQC
+.Op Fl h Ar homedir
+.Op Fl m Ar map
+.Op Fl w Ar whiteclnt
+.Op Fl t Ar targets
+.Op Fl i Ar infile
+.Op Fl L Ar ltype,facility.level
+.Ek
+.Sh DESCRIPTION
+.Nm Dccsight
+reads one or more lines containing DCC checksums,
+reports them to a DCC server,
+and writes a X-DCC header line.
+It can be used to report checksums obtained from
+.Xr dccproc 8
+using
+.Fl C .
+.Ss OPTIONS
+The following options are available:
+.Bl -tag -width 3n
+.It Fl V
+displays the version of the DCC raw checksum interface.
+.It Fl d
+enables debugging output from the DCC client software.
+Additional
+.Fl d
+options increase the number of messages.
+.It Fl Q
+only queries the DCC server about the checksums
+instead of reporting and then querying.
+This is useful when
+.Nm
+is used to filter mail that has already been reported to a DCC
+server by another DCC client such as
+.Xr dccm 8 .
+This can also be useful when applying a private white or black list to
+mail that has already been reported to a DCC server.
+No single mail message should be reported to a DCC
+server more than once per recipient, such as would happen if
+.Nm
+is not given
+.Fl Q
+when processing a stream of mail that has already been seen by a DCC client.
+Additional reports of a message
+increase its apparent "bulkness."
+.It Fl C
+outputs the checksums for the message as well as the X-DCC header.
+.It Fl h Ar homedir
+overrides the default DCC home directory,
+.Pa @prefix@ .
+.It Fl m Ar map
+specifies a name or path of the memory mapped parameter file instead
+of the default
+.Pa map
+in the DCC home directory.
+It should be created with the
+.Xr cdcc 8
+command.
+.It Fl w Ar whiteclnt
+specifies an optional file containing SMTP client IP addresses and
+SMTP headers
+of mail that do not need X-DCC headers and whose checksums should not
+be reported to the DCC server.
+It can also contain checksums of spam.
+If the pathname is not absolute, it is relative to the DCC home directory.
+Thus, individual users with private whitelists usually specify them
+with absolute paths.
+It is useful to
+.Ar include
+a common or system-wide whitelist in private lists.
+.Pp
+The format of the
+.Nm
+whiteclnt file is the same as the
+.Pa whitelist
+file required by
+.Xr dbclean 8
+and
+.Xr dccsight 8 .
+Because this list is used frequently, a companion file is used.
+It has the same pathname but with an added suffix of
+.Ar .dccw .
+After being created empty,
+it will contain an automatic memory mapped hash table of the main file.
+.It Fl t Ar targets
+specifies the number of addressees of the message if other than 1.
+The string
+.Ar many
+instead of a number asserts that there were too many addressees
+and that the message is unsolicited bulk email.
+.It Fl i Ar infile
+specifies an input file
+instead of standard input.
+If not absolute, the pathname is interpreted relative to the
+directory in which
+.Nm
+was started.
+.It Fl L Ar ltype,facility.level
+specifies how messages should be logged.
+.Ar Ltype
+must be
+.Ar error ,
+.Ar info ,
+or
+.Ar off
+to indicate which of the two types of messages are being controlled or
+to turn off all
+.Xr syslog 3
+messages from
+.Nm .
+.Ar Level
+must be a
+.Xr syslog 3
+level among
+.Ar EMERG ,
+.Ar ALERT ,
+.Ar CRIT , ERR ,
+.Ar WARNING ,
+.Ar NOTICE ,
+.Ar INFO ,
+and
+.Ar DEBUG .
+.Ar Facility
+must be among
+.Ar AUTH ,
+.Ar AUTHPRIV ,
+.Ar CRON ,
+.Ar DAEMON ,
+.Ar FTP ,
+.Ar KERN ,
+.Ar LPR ,
+.Ar MAIL ,
+.Ar NEWS ,
+.Ar USER ,
+.Ar UUCP ,
+and
+.Ar LOCAL0
+through
+.Ar LOCAL7 .
+The default is equivalent to
+.Dl Fl L Ar info,MAIL.NOTICE  Fl L Ar error,MAIL.ERR
+.El
+.Pp
+.Nm
+exits 0 on success, and >0 if an error occurs.
+.Sh FILES
+.Bl -tag -width whiteclnt -compact
+.It Pa @prefix@
+DCC home directory in which other files are found.
+.It Pa map
+memory mapped file in the DCC home directory
+of information concerning DCC servers.
+.It Pa whiteclnt
+contains the client whitelist in
+the format described in
+.Xr dcc 8 .
+.It Pa whiteclnt.dccw
+memory mapped hash table of the
+.Pa whiteclnt
+file.
+.El
+.Sh SEE ALSO
+.Xr cdcc 8 ,
+.Xr dcc 8 ,
+.Xr dbclean 8 ,
+.Xr dccd 8 ,
+.Xr dblist 8 ,
+.Xr dccproc 8 ,
+.Xr dccm 8 ,
+.Xr dccifd 8 ,
+.Xr mail 1 ,
+.Xr procmail 1 .
+.Sh HISTORY
+Implementation of
+.Nm
+was started at Rhyolite Software in 2000.
+This document describes version 1.3.103.
diff -r 000000000000 -r c7f6b056b673 dccsight.html.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccsight.html.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,144 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<HTML>
+<HEAD>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <TITLE>dccsight.0.8</TITLE>
+    <META http-equiv="Content-Style-Type" content="text/css">
+    <STYLE type="text/css">
+	BODY {background-color:white; color:black}
+	ADDRESS {font-size:smaller}
+        IMG.logo {width:6em; vertical-align:middle}
+    </STYLE>
+</HEAD>
+<BODY>
+<PRE>
+<!-- Manpage converted by man2html 3.0.1 -->
+<B><A HREF="dccsight.html">dccsight(8)</A></B>           Distributed Checksum Clearinghouse           <B><A HREF="dccsight.html">dccsight(8)</A></B>
+
+
+</PRE>
+<H2><A NAME="NAME">NAME</A></H2><PRE>
+     <B>dccsight</B> -- Distributed Checksum Clearinghouse raw checksum interface
+
+
+</PRE>
+<H2><A NAME="SYNOPSIS">SYNOPSIS</A></H2><PRE>
+     <B>dccsight</B> [<B>-VdQC</B>] [<B>-h</B> <I>homedir</I>] [<B>-m</B> <I>map</I>] [<B>-w</B> <I>whiteclnt</I>] [<B>-t</B> <I>targets</I>]
+              [<B>-i</B> <I>infile</I>] [<B>-L</B> <I>ltype,facility.level</I>]
+
+
+</PRE>
+<H2><A NAME="DESCRIPTION">DESCRIPTION</A></H2><PRE>
+     <B>Dccsight</B> reads one or more lines containing DCC checksums, reports them
+     to a DCC server, and writes a X-DCC header line.  It can be used to
+     report checksums obtained from <B><A HREF="dccproc.html">dccproc(8)</A></B> using <B>-C</B>.
+
+   <A NAME="OPTIONS"><B>OPTIONS</B></A>
+     The following options are available:
+
+     <A NAME="OPTION-V"><B>-V</B></A>   displays the version of the DCC raw checksum interface.
+
+     <A NAME="OPTION-d"><B>-d</B></A>   enables debugging output from the DCC client software.  Additional
+          <B>-d</B> options increase the number of messages.
+
+     <A NAME="OPTION-Q"><B>-Q</B></A>   only queries the DCC server about the checksums instead of reporting
+          and then querying.  This is useful when <B>dccsight</B> is used to filter
+          mail that has already been reported to a DCC server by another DCC
+          client such as <B><A HREF="dccm.html">dccm(8)</A></B>.  This can also be useful when applying a
+          private white or black list to mail that has already been reported
+          to a DCC server.  No single mail message should be reported to a DCC
+          server more than once per recipient, such as would happen if
+          <B>dccsight</B> is not given <B>-Q</B> when processing a stream of mail that has
+          already been seen by a DCC client.  Additional reports of a message
+          increase its apparent "bulkness."
+
+     <A NAME="OPTION-C"><B>-C</B></A>   outputs the checksums for the message as well as the X-DCC header.
+
+     <A NAME="OPTION-h"><B>-h</B></A> <I>homedir</I>
+          overrides the default DCC home directory, <I>@prefix@</I>.
+
+     <A NAME="OPTION-m"><B>-m</B></A> <I>map</I>
+          specifies a name or path of the memory mapped parameter file instead
+          of the default <I>map</I> in the DCC home directory.  It should be created
+          with the <B><A HREF="cdcc.html">cdcc(8)</A></B> command.
+
+     <A NAME="OPTION-w"><B>-w</B></A> <I>whiteclnt</I>
+          specifies an optional file containing SMTP client IP addresses and
+          SMTP headers of mail that do not need X-DCC headers and whose check-
+          sums should not be reported to the DCC server.  It can also contain
+          checksums of spam.  If the pathname is not absolute, it is relative
+          to the DCC home directory.  Thus, individual users with private
+          whitelists usually specify them with absolute paths.  It is useful
+          to <I>include</I> a common or system-wide whitelist in private lists.
+
+          The format of the <B>dccsight</B> whiteclnt file is the same as the
+          <I>whitelist</I> file required by <B><A HREF="dbclean.html">dbclean(8)</A></B> and <B><A HREF="dccsight.html">dccsight(8)</A></B>.  Because this
+          list is used frequently, a companion file is used.  It has the same
+          pathname but with an added suffix of <I>.dccw</I>.  After being created
+          empty, it will contain an automatic memory mapped hash table of the
+          main file.
+
+     <A NAME="OPTION-t"><B>-t</B></A> <I>targets</I>
+          specifies the number of addressees of the message if other than 1.
+          The string <I>many</I> instead of a number asserts that there were too many
+          addressees and that the message is unsolicited bulk email.
+
+     <A NAME="OPTION-i"><B>-i</B></A> <I>infile</I>
+          specifies an input file instead of standard input.  If not absolute,
+          the pathname is interpreted relative to the directory in which
+          <B>dccsight</B> was started.
+
+     <A NAME="OPTION-L"><B>-L</B></A> <I>ltype,facility.level</I>
+          specifies how messages should be logged.  <I>Ltype</I> must be <I>error</I>, <I>info</I>,
+          or <I>off</I> to indicate which of the two types of messages are being con-
+          trolled or to turn off all <B>syslog(3)</B> messages from <B>dccsight</B>.  <I>Level</I>
+          must be a <B>syslog(3)</B> level among <I>EMERG</I>, <I>ALERT</I>, <I>CRIT</I>, <I>ERR</I>, <I>WARNING</I>,
+          <I>NOTICE</I>, <I>INFO</I>, and <I>DEBUG</I>.  <I>Facility</I> must be among <I>AUTH</I>, <I>AUTHPRIV</I>,
+          <I>CRON</I>, <I>DAEMON</I>, <I>FTP</I>, <I>KERN</I>, <I>LPR</I>, <I>MAIL</I>, <I>NEWS</I>, <I>USER</I>, <I>UUCP</I>, and <I>LOCAL0</I>
+          through <I>LOCAL7</I>.  The default is equivalent to
+                <B>-L</B> <I>info,MAIL.NOTICE</I> <B>-L</B> <I>error,MAIL.ERR</I>
+
+     <B>dccsight</B> exits 0 on success, and &gt;0 if an error occurs.
+
+
+</PRE>
+<H2><A NAME="FILES">FILES</A></H2><PRE>
+     <A NAME="FILE-@prefix@">@prefix@</A>   DCC home directory in which other files are found.
+     <A NAME="FILE-map">map</A>        memory mapped file in the DCC home directory of information
+                concerning DCC servers.
+     <A NAME="FILE-whiteclnt">whiteclnt</A>  contains the client whitelist in the format described in
+                <B><A HREF="dcc.html">dcc(8)</A></B>.
+     <A NAME="FILE-whiteclnt.dccw">whiteclnt.dccw</A>
+                memory mapped hash table of the <I>whiteclnt</I> file.
+
+
+</PRE>
+<H2><A NAME="SEE-ALSO">SEE ALSO</A></H2><PRE>
+     <B><A HREF="cdcc.html">cdcc(8)</A></B>, <B><A HREF="dcc.html">dcc(8)</A></B>, <B><A HREF="dbclean.html">dbclean(8)</A></B>, <B><A HREF="dccd.html">dccd(8)</A></B>, <B><A HREF="dblist.html">dblist(8)</A></B>, <B><A HREF="dccproc.html">dccproc(8)</A></B>, <B><A HREF="dccm.html">dccm(8)</A></B>,
+     <B><A HREF="dccifd.html">dccifd(8)</A></B>, <B>mail(1)</B>, <B>procmail(1)</B>.
+
+
+</PRE>
+<H2><A NAME="HISTORY">HISTORY</A></H2><PRE>
+     Implementation of <B>dccsight</B> was started at Rhyolite Software in 2000.
+     This document describes version 1.3.103.
+
+                               February 26, 2009
+</PRE>
+<HR>
+<ADDRESS>
+Man(1) output converted with
+<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
+modified for the DCC $Date 2001/04/29 03:22:18 $
+<BR>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG SRC="http://logos.dcc-servers.net/border.png"
+            class=logo ALT="DCC logo">
+    </A>
+<A HREF="http://validator.w3.org/check?uri=referer">
+    <IMG class=logo ALT="Valid HTML 4.01 Strict"
+        SRC="http://www.w3.org/Icons/valid-html401">
+    </A>
+</ADDRESS>
+</BODY>
+</HTML>
diff -r 000000000000 -r c7f6b056b673 dccsight/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccsight/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,3 @@
+Makefile.in
+dccsight.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 dccsight/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccsight/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,52 @@
+# make the Distributed Checksum Clearinghouse `dccsight` program
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.12 $Revision$
+# @configure_input@
+
+DEPTH	=..
+PROG	=dccsight
+SRCS	=$(PROG).c
+
+DCC_BINDIR=@installroot@@libexecdir@
+@MAKE_PROG@
+
+@MAKE_DOT@ifndef NO_SUID
+# dccsight needs to be SUID to read the server passwords
+BINMODE	=4$(DCC_MODE)
+BINOWN	=@DCCSUID@
+@MAKE_DOT@endif
+@MAKE_INC2@
diff -r 000000000000 -r c7f6b056b673 dccsight/dccsight.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dccsight/dccsight.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,417 @@
+/* Distributed Checksum Clearinghouse server
+ *
+ * report the previously computed checksums of a message
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.70 $Revision$
+ */
+
+#include "dcc_ck.h"
+#include "dcc_xhdr.h"
+
+
+static DCC_EMSG dcc_emsg;
+
+static const char *homedir;
+static const char *mapfile_nm = DCC_MAP_NM_DEF;
+
+static char *local_tgts_str;
+static DCC_TGTS local_tgts = 1;
+static u_char local_tgts_spam, local_tgts_set;
+
+static const char* white_nm;
+static const char *ifile_nm;
+static FILE *ifile;
+static const char *grey_sum;
+
+static DCC_CLNT_CTXT *ctxt;
+static u_char print_cksums;
+
+static ASK_ST ask_st;
+static FLTR_SWS rcpt_sws;
+
+static DCC_GOT_CKS cks;
+static DCC_CKS_WTGTS wtgts;
+static u_char have_sum;
+
+static DCC_HEADER_BUF header;
+
+/* kludges to avoid linking with dnsbl.c */
+DNSBL *dnsbls;
+u_char have_dnsbl_groups;
+
+static void do_grey(void);
+static int add_cksum(DCC_EMSG, DCC_WF *, DCC_CK_TYPES, DCC_SUM, DCC_TGTS);
+static void print_ck(void *arg, const char *, u_int);
+
+
+static void NRATTRIB
+usage(void)
+{
+	dcc_logbad(EX_USAGE,
+		   "usage: [-VdCQ] [-h homedir] [-m map] [-w whiteclnt]"
+		   " [-t targets]\n"
+		   "   [-c type,[log-thold,][spam-thold]] [-g [not-]type]\n"
+		   "   [-i infile] [-G grey-cksum] [-L ltype,facility.level]");
+}
+
+
+
+int NRATTRIB
+main(int argc, char **argv)
+{
+	char buf[200];
+	const char *bufp;
+	char type_str[DCC_XHDR_MAX_TYPE_LEN+1];
+	DCC_CK_TYPES type;
+	u_long l;
+	u_char skip_heading, result;
+	char c1, c2, *p;
+	int i;
+
+	dcc_syslog_init(0, argv[0], 0);
+	dcc_clear_tholds();
+
+	/* we must be SUID to read and write the system's common connection
+	 * parameter memory mapped file.  We also need to read the common
+	 * local white list and write the mmap()'ed hash file */
+	dcc_init_priv();
+
+	ifile = stdin;
+	while ((i = getopt(argc, argv, "VdCQh:m:w:t:c:g:i:G:L:")) != -1) {
+		switch (i) {
+		case 'V':
+			fprintf(stderr, DCC_VERSION"\n");
+			exit(EX_OK);
+			break;
+
+		case 'd':
+			++dcc_clnt_debug;
+			break;
+
+		case 'Q':
+			ask_st |= ASK_ST_QUERY;
+			break;
+
+		case 'C':
+			print_cksums = 1;
+			break;
+
+		case 'h':
+			homedir = optarg;
+			break;
+
+		case 'm':
+			mapfile_nm = optarg;
+			break;
+
+		case 'w':
+			white_nm = optarg;
+			break;
+
+		case 't':
+			local_tgts_str = optarg;
+			if (!strcasecmp(optarg, "many")) {
+				local_tgts_spam = 1;
+				local_tgts = 1;
+				local_tgts_set = 1;
+			} else {
+				l = strtoul(optarg, &p, 10);
+				if (*p != '\0' || l > 1000)
+					dcc_error_msg("invalid count \"%s\"",
+						      optarg);
+				else
+					local_tgts = l;
+					local_tgts_spam = 0;
+					local_tgts_set = 1;
+			}
+			break;
+
+		case 'c':
+			dcc_parse_tholds("-c ", optarg);
+			break;
+
+		case 'g':		/* honor not-spam "counts" */
+			dcc_parse_honor(optarg);
+			break;
+
+		case 'i':
+			/* open the input file now, before changing to the
+			 * home DCC directory */
+			ifile_nm = optarg;
+			ifile = fopen(ifile_nm, "r");
+			if (!ifile)
+				dcc_logbad(EX_USAGE,
+					   "bad input file \"%s\": %s",
+					   ifile_nm, ERROR_STR());
+			break;
+
+		case 'G':
+			grey_sum = optarg;
+			break;
+
+		case 'L':
+			dcc_parse_log_opt(optarg);
+			break;
+
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	if (argc != 0)
+		usage();
+
+	dcc_clnt_unthread_init();
+	dcc_cdhome(0, homedir, 1);
+
+	/* open /var/dcc/map and start a connection to a DCC server */
+	ctxt = dcc_clnt_init(dcc_emsg, 0, mapfile_nm, DCC_CLNT_FG_NONE);
+	if (!ctxt)
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+
+	if (grey_sum) {
+		if (ifile_nm)
+			dcc_logbad(EX_USAGE, "-i and -G incompatible");
+		do_grey();
+		exit(EX_OK);
+	}
+
+	if (!ifile_nm)
+		ifile_nm = "stdin";
+
+	/* get the checksums */
+	skip_heading = 0;
+	for (;;) {
+		bufp = fgets(buf, sizeof(buf), ifile);
+		if (!bufp) {
+			if (ferror(ifile))
+				dcc_logbad(EX_DATAERR, "fgets(%s): %s",
+					   ifile_nm, ERROR_STR());
+			break;
+		}
+
+		/* ignore blank lines */
+		i = strlen(buf);
+		if (!i) {
+			skip_heading = 0;
+			continue;
+		}
+
+		/* trim leading and trailing whitespace */
+		p = &buf[i-1];
+		bufp += strspn(bufp, DCC_WHITESPACE);
+		c2 = *p;		/* should be '\n' */
+		while (p > bufp) {
+			c1 = *(p-1);
+			if (c1 != '\r' && c1 != ' ' && c1 != '\t')
+				break;
+			*--p = c2;
+		}
+		if (*bufp == '\n' || *bufp == '\0') {
+			skip_heading = 0;
+			continue;
+		}
+
+		/* ignore DCC header lines such as in the output
+		 * of `dccproc -C` */
+		if (skip_heading == 0
+		    && !CLITCMP(bufp, DCC_XHDR_START)) {
+			skip_heading = 1;
+			continue;
+		}
+		/* skip headings for the checksums */
+		if (skip_heading <= 1
+		    && !CLITCMP(bufp, DCC_XHDR_REPORTED)) {
+			skip_heading = 2;
+			continue;
+		}
+
+		/* handle the next checksum */
+		bufp = dcc_parse_word(dcc_emsg, type_str, sizeof(type_str),
+				      bufp, "checksum type", 0, 0);
+		if (!bufp)
+			dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+		type = dcc_str2type_wf(type_str, -1);
+		if (type != DCC_CK_ENV_TO
+		    && 0 >= dcc_parse_hex_ck(dcc_emsg, &cmn_wf,
+					     type_str, type,
+					     bufp, 1, add_cksum))
+			dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+	}
+	fclose(ifile);
+
+	if (!have_sum)
+		dcc_logbad(EX_DATAERR, "no reportable checksums");
+
+	dcc_wf_init(&cmn_wf, 0);
+	if (!unthr_ask_white(dcc_emsg, &ask_st, &rcpt_sws,
+			     white_nm, &cks, wtgts))
+		dcc_error_msg("%s", dcc_emsg);
+
+	if (!local_tgts_set) {
+		local_tgts = (ask_st & ASK_ST_QUERY) ? 0 : 1;
+		local_tgts_spam = 0;
+	} else if (local_tgts == 0) {
+		ask_st |= ASK_ST_QUERY;
+		local_tgts_spam = 0;
+	} else if (ask_st & ASK_ST_QUERY) {
+		dcc_error_msg("\"-t %s\" is incompatible with \"-Q\"",
+			      local_tgts_str);
+		local_tgts = 0;
+		local_tgts_spam = 0;
+	}
+	if (local_tgts == DCC_TGTS_TOO_MANY) {
+		local_tgts = 1;
+		local_tgts_spam = 1;
+	}
+	if (local_tgts != 0
+	    && (ask_st & ASK_ST_CLNT_ISSPAM))
+		local_tgts_spam = 1;
+
+	result = unthr_ask_dcc(dcc_emsg, ctxt, &header, &ask_st,
+			       &cks, local_tgts_spam, local_tgts);
+	if (!result) {
+		dcc_error_msg("%s", dcc_emsg);
+	} else if (header.buf[0] != '\0') {
+		printf("%s", header.buf);
+	} else if (dcc_clnt_debug) {
+		if (ask_st & ASK_ST_WLIST_NOTSPAM)
+			printf("no header; checksums are locally whitelisted");
+		else
+			printf("no header");
+	}
+
+	if (print_cksums)
+		dcc_print_cks(print_ck, 0, local_tgts_spam, local_tgts,
+			      &cks, wtgts, 0);
+
+	exit(EX_OK);
+}
+
+
+
+static void NRATTRIB
+do_grey(void)
+{
+	union {
+	    u_int32_t n[4];
+	    DCC_SUM sum;
+	} u;
+	DCC_REPORT rpt;
+	DCC_OP_RESP resp;
+
+	if (4 != sscanf(grey_sum, DCC_CKSUM_HEX_PAT,
+			&u.n[0], &u.n[1], &u.n[2], &u.n[3]))
+		dcc_logbad(EX_USAGE,
+			   "unrecognized greylist checksum");
+	u.n[0] = htonl(u.n[0]);
+	u.n[1] = htonl(u.n[1]);
+	u.n[2] = htonl(u.n[2]);
+	u.n[3] = htonl(u.n[3]);
+
+	memset(&rpt, 0, sizeof(rpt));
+	memcpy(rpt.cks[0].sum, u.sum, sizeof(rpt.cks[0].sum));
+	rpt.cks[0].type = DCC_CK_GREY3;
+	rpt.cks[0].len = sizeof(rpt.cks[0]);
+	if (!dcc_clnt_op(dcc_emsg, ctxt, DCC_CLNT_FG_GREY, 0, 0, 0,
+			 &rpt.hdr, (sizeof(rpt) - sizeof(rpt.cks)
+				    + sizeof(rpt.cks[0])),
+			 (ask_st & ASK_ST_QUERY)
+			 ? DCC_OP_GREY_QUERY : DCC_OP_GREY_WHITE,
+			 &resp, sizeof(resp)))
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+
+	if (!dcc_ck_grey_answer(dcc_emsg, &resp))
+		dcc_logbad(dcc_ex_code, "%s", dcc_emsg);
+
+	switch (ntohl(resp.gans.triple)) {
+	case DCC_TGTS_OK:		/* embargo ended just now */
+		printf(DCC_XHDR_EMBARGO_ENDED"\n");
+		break;
+	case DCC_TGTS_TOO_MANY:		/* no current embargo */
+		printf(DCC_XHDR_EMBARGO_PASS"\n");
+		break;
+	case DCC_TGTS_GREY_WHITE:	/* whitelisted for greylisting */
+		printf(DCC_XHDR_EMBARGO_OK"\n");
+		break;
+	default:			/* embargoed */
+		printf(DCC_XHDR_EMBARGO"\n");
+		break;
+	}
+	exit(EX_OK);
+}
+
+
+
+static int
+add_cksum(DCC_EMSG emsg, DCC_WF *wf UATTRIB,
+	  DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts)
+{
+	static DCC_SUM zero;
+
+	if (cks.sums[type].type != DCC_CK_INVALID
+	    && type != DCC_CK_SUB) {
+		dcc_pemsg(EX_DATAERR, emsg, "duplicate %s checksum",
+			  dcc_type2str_err(type, 0, 0, 0));
+	}
+
+	/* envelope Rcpt_To values are never sent to the server */
+	if (type == DCC_CK_ENV_TO)
+		return 1;
+
+	/* do not send FUZ2 missing checksum */
+	if (type == DCC_CK_FUZ2
+	    && !memcmp(sum, zero, sizeof(DCC_SUM)))
+		return 1;
+
+	memcpy(cks.sums[type].sum, sum, sizeof(cks.sums[type].sum));
+	cks.sums[type].type = type;
+	cks.sums[type].rpt2srvr = 1;
+	cks.sums[type].tgts = DCC_TGTS_INVALID;
+	if (tgts)
+		have_sum = 1;
+	return 1;
+}
+
+
+
+static void
+print_ck(void *arg UATTRIB, const char *buf, u_int buf_len)
+{
+	fwrite(buf, buf_len, 1, stdout);
+}
diff -r 000000000000 -r c7f6b056b673 dns-helper/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dns-helper/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,3 @@
+Makefile.in
+dns-helper.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 dns-helper/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dns-helper/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,46 @@
+# make the Distributed Checksum Clearinghouse DNS helper process
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.2 $Revision$
+# @configure_input@
+
+DEPTH	=..
+PROG	=dns-helper
+SRCS	=$(PROG).c
+
+DCC_BINDIR=@installroot@@libexecdir@
+@MAKE_PROG@
+@MAKE_INC2@
diff -r 000000000000 -r c7f6b056b673 dns-helper/dns-helper.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dns-helper/dns-helper.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,125 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * talk to DNS resolver libraries that are not thread safe
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.7 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include "helper.h"
+
+
+static DCC_EMSG dcc_emsg;
+
+static void NRATTRIB
+usage(void)
+{
+	dcc_logbad(EX_USAGE, "usage: [-V] -B...");
+}
+
+
+
+int NRATTRIB
+main(int argc, char **argv)
+{
+	int i;
+
+	dcc_syslog_init(1, argv[0], 0);
+
+	while ((i = getopt(argc, argv, "VB:L:")) != -1) {
+		switch (i) {
+		case 'V':
+			fprintf(stderr, DCC_VERSION"\n");
+			exit(EX_OK);
+			break;
+
+		case 'B':
+			if (!dcc_parse_dnsbl(dcc_emsg, optarg, 0, 0))
+				dcc_error_msg("%s", dcc_emsg);
+			break;
+
+		case 'L':
+			dcc_parse_log_opt(optarg);
+			break;
+
+		default:
+			usage();
+		}
+	}
+	usage();
+}
+
+
+
+int
+thr_error_msg(void *cp UATTRIB, const char *p, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, p);
+	i = dcc_verror_msg(p, args);
+	va_end(args);
+	return i;
+}
+
+
+
+void
+thr_trace_msg(void *cp UATTRIB, const char *p, ...)
+{
+	va_list args;
+
+	va_start(args, p);
+	dcc_verror_msg(p, args);
+	va_end(args);
+}
+
+
+
+/* DOES append '\n', but since it is ony for errors, do not worry about it */
+int
+thr_log_print(void *cp UATTRIB, u_char error UATTRIB, const char *p, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, p);
+	i = dcc_verror_msg(p, args);
+	va_end(args);
+	return i;
+}
diff -r 000000000000 -r c7f6b056b673 gmake.inc.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gmake.inc.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,144 @@
+# help make the Distributed Checksum Clearinghouse on Linux
+#	without a common library of rules
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.42 $Revision$
+# @configure_input@
+
+GMAKE_QUIET=--no-print-directory
+
+ifdef SUBDIR
+ifeq (,$(findstring k,$(MAKEFLAGS)))
+SUBK	=set -e
+endif
+all install clean cleandir:
+	@$(SUBK) $(foreach DIR,$(SUBDIR),echo "==> $(DIR)";	\
+	    $(MAKE) $(GMAKE_QUIET) -C $(DIR) $@;)
+endif
+
+include $(DEPTH)/Makefile.inc
+
+OBJS	=$(SRCS:.c=.o)
+
+ifdef LIB
+all:	lib$(LIB).a
+
+lib$(LIB).a:$(OBJS)
+	@rm -f lib${LIB}.a
+	${AR} qcs lib${LIB}.a ${OBJS}
+	@RANLIB@ lib$(LIB).a
+
+CLEANFILES+=lib$(LIB).a
+endif
+
+
+ifdef PROG
+all:   $(PROG)
+
+$(PROG):$(OBJS)
+	$(CC) $(LDFLAGS) $(OBJS) ${LDADD} -o $@
+
+$(PROG):$(DPADD)
+endif
+
+# do not let clean be the default target
+all:
+
+CLEANFILES+=$(OBJS) $(SRCS:.c=.d)
+ifdef PROG
+CLEANFILES+=$(PROG)
+endif
+
+clean cleandir:locclean
+locclean:
+ifeq "$(CLEANFILES)" " "
+	@:
+else
+	rm -f $(CLEANFILES)
+endif
+
+install:beforeinstall locinstall
+locinstall:maninstall proginstall
+
+proginstall:beforeinstall
+ifdef PROG
+	$(BININSTALL) $(PROG) $(BINDIR)
+endif
+
+# gmake with gcc or IRIX compilers do not need the depend target
+depend:
+	@:
+
+# Don't get excited about missing header files named in old .d files
+#   Genuinely needed header files that have disappeared will break compiles.
+%.h:
+	@:
+
+%.d:	%.c
+	@$(CC) -M $(CFLAGS) $< \
+	    | sed "s!^\([^ :]\{1,\}\)[ :]\{1,\}!\1 $@: !g" > $@
+
+# include the .d file if we have any C source and we are not deleting things
+@HAVE_CC_M@
+ifndef SRCS
+HAVE_CC_M=no
+endif
+ifeq ($(MAKECMDGOALS),cleandir)
+HAVE_CC_M=no
+endif
+ifeq ($(MAKECMDGOALS),clean)
+HAVE_CC_M=no
+endif
+ifeq ($(MAKECMDGOALS),deinstall)
+HAVE_CC_M=no
+endif
+ifndef HAVE_CC_M
+ifeq ($(shell sh -c "$(CC) -v 2>&1 | sed -n -e 's/^gcc version .*/gcc/p'"),gcc)
+HAVE_CC_M=yes
+endif
+endif
+ifeq ($(HAVE_CC_M),yes)
+-include $(SRCS:.c=.d)
+endif
+
+# Try to rebuild the .0 man pages from the shipped .8 files on systems
+# with gmake and groff so that they will have local directory names
+@USE_GROFF@
+ifeq ($(USE_GROFF),yes)
+.SUFFIXES:.0 .8 .html
+.8.0::
+	groff -Tascii -mtty-char -mdoc $*.8 > $@
+endif
diff -r 000000000000 -r c7f6b056b673 homedir/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,14 @@
+Makefile.in
+README
+dcc_conf.in
+fix-map
+flod
+grey_flod
+grey_whitelist
+ids
+make-dcc_conf
+map.txt
+whiteclnt
+whitecommon
+whitelist
+.manifest
diff -r 000000000000 -r c7f6b056b673 homedir/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,110 @@
+# make the Distributed Checksum Clearinghouse prototype home directory files
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.57 $Revision$
+# @configure_input@
+
+DEPTH	=..
+
+@MAKE_PROG@
+@MAKE_INC2@
+
+HINST	=$(INSTALL) -c $(SET_DCCOWN)
+HD	=@installroot@$(DCC_HOMEDIR)
+MAP	=$(HD)/map@configsuffix@
+MAPTXT	=$(HD)/map.txt@configsuffix@
+IDS	=$(HD)/ids@configsuffix@
+CDCC	=$(DEPTH)/cdcc/cdcc
+
+# this should not be the ./configure name
+UPFILE	=.updatedcc_pfile
+
+SIMPLE	=flod grey_flod whitelist grey_whitelist whiteclnt whitecommon
+ALL	=$(SIMPLE) ids map map.txt dcc_conf
+
+all:
+	@:
+
+# /dev/random is overkill for the security needed here, but use it if it exists
+RND=(ps; dd if=/dev/random count=1 2>&1) | cksum | tr '	 ' xy
+
+install:
+	if test ! -d $(HD); then\
+	    $(HINST) -m 755 -d $(HD); fi
+	if test ! -d $(HD)/log; then\
+	    $(HINST) -m 710 -d $(HD)/log; fi
+	if test -z '@configsuffix@' -a -s $(HD)/dcc_conf; then\
+	    sh make-dcc_conf -h $(HD) -s '@configsuffix@';\
+	else\
+	    $(HINST) -m 644 dcc_conf $(HD)/dcc_conf@configsuffix@;\
+	fi
+	for nm in $(SIMPLE); do\
+	    if test -n '@configsuffix@' -o ! -f $(HD)/$$nm; then\
+		$(HINST) -m 644 $$nm $(HD)/$${nm}@configsuffix@; fi; done
+	if test -s $(UPFILE); then\
+	    $(HINST) -m 600 $(UPFILE) $(HD)/$(UPFILE);\
+	    set +e; chown @DCCSUID@ $(HD)/$(UPFILE); fi
+	if test -n '@configsuffix@' || test ! -f $(HD)/ids -a ! -f $(HD)/map\
+				 -a ! -f $(HD)/map.txt; then\
+	    umask 077; PASSWD=`$(RND)`;\
+	    sed -e "s/secret1/$$PASSWD/" -e "s/secret2/`$(RND)`/" ids >$(IDS);\
+	    rm -f $(MAP) || true; sed -e "s/secret1/$$PASSWD/" map.txt |\
+		$(CDCC) -qh$(HD) "new map $(MAP); load -";\
+	    sh fix-map -c $(CDCC) -m $(MAP) -h $(HD);\
+	    echo '# map.txt is merely the output of `cdcc info`' >$(MAPTXT);\
+	    $(CDCC) -qh$(HD) "file $(MAP); info" >>$(MAPTXT);\
+	    set +e; chown @DCCSUID@ $(MAPTXT) $(MAP) $(IDS);\
+	else\
+	    sh fix-map -c $(CDCC) -m $(MAP) -h $(HD);\
+	fi
+	if test -n '@configsuffix@'; then\
+	    for nm in $(ALL); do\
+		if test ! -f $(HD)/$$nm; then\
+		    cp -p $(HD)/$${nm}@configsuffix@ $(HD)/$${nm}; fi; done; fi
+
+deinstall:
+	for NM in dcc_conf flod grey_flod whiteclnt whitecommon\
+		whitelist grey_whitelist; do\
+	    if test ! -f $(HD)/$$NM; then continue; fi;\
+	    if test `diff $$NM $(HD)/$$NM\
+		    | sed -e '/^---$$/d' -e '/^[^<>]/d'\
+			 -e '/^[<>][	]*#/d' -e '/^[<>][	]*$$/d'\
+		    | wc -l` -eq 0; then rm $(HD)/$$NM; fi; done;
+	-rm -f $(HD)/dcc_conf-new
+	-if test -d $(HD); then\
+	    find $(HD) -name '*.dcc[xw]' | @DCC_XARGS@ rm -f ;\
+	    rmdir $(HD)/log;\
+	    fi
diff -r 000000000000 -r c7f6b056b673 homedir/README
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/README	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,23 @@
+These sample files can be modified and installed in the DCC home directory
+
+The following are used by DCC clients:
+    whiteclnt
+    map.txt		must be loaded into the map file with
+				`cdcc "load map.ascii"`
+			    That is done by `make install`
+
+The following are used by DCC servers:
+    whitelist		must be added to a new database with `dbclean -NS`
+    flod
+    ids
+
+Among the other files
+    whitecommon		is used by both the sample whiteclnt and whitelist files
+    dcc_conf		prototype configuration file for libexec/rcDCC and
+			    libexec/cron-dccd
+			    dcc_conf does not exist until it has been built
+			    with the `configure` script from the dcc_conf.in
+			    prototype.
+
+
+	Rhyolite Software DCC 1.3.103-1.9 $Revision$
diff -r 000000000000 -r c7f6b056b673 homedir/dcc_conf.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/dcc_conf.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,152 @@
+#! /bin/sh
+
+# set parameters for DCC start, stop, and cron scripts
+
+# This file is parsed by /bin/sh, and so parameters must be appropriately quoted
+#   Start your own comment lines with something other than "#" such as "##"
+#   so that they will be preserved when installing a new version.
+
+
+# from Rhyolite Software DCC 1.3.103-1.77 $Revision$
+DCC_CONF_VERSION=4
+
+# don't set DCC_HOMEDIR since if we got here, it must be set
+DCC_LIBEXEC=@libexecdir@
+DCC_RUNDIR=@dcc_rundir@
+
+# DCC user name
+DCCUID=@DCCSUID@
+
+
+DCCD_ENABLE=off
+# DCC server-IDs must be globally unique.
+SRVR_ID=
+# BRAND can be any short alphanumeric string that suggests the identity
+#   of the server.
+BRAND=
+# args used to start dccd such as -6
+DCCD_ARGS=
+
+
+# GREY_CLIENT_ARGS contains "on", "-GnoIP", etc. to turn on greylisting
+#	in the dccm and dccifd DCC clients.
+#   Also turns on the local greylist dccd server unless GREY_ENABLE=off
+GREY_CLIENT_ARGS=
+# GREY_ENABLE turns local greylist server 'on' or 'off',
+#	but does not effect dccm, dccifd
+GREY_ENABLE=
+
+# GREY_SRVR_ID DCC server-IDs must be globally unique, but greylisting dccd
+#   servers are usually isolated.  If you have more than one greylist server,
+#   ensure that they use distinct server-IDs and that they flood each other
+#   with entries in @prefix@/flod
+GREY_SRVR_ID=$SRVR_ID
+# Start dccd for grey listing or set server options such as -Gweak-IP.
+#   See also GREY_ENABLE.
+GREY_DCCD_ARGS=
+
+# dccm and dccifd client reputation parameters such as -tREP,20
+REP_ARGS=-tREP,20
+
+# DNS blacklist -B parameters for dccifd and dccm
+#   For example, this checks SMTP client IP addresses for any value Spamhaus'
+#   ZEN.  It also looks for for values between 127.0.0.0 and 127.0.0.8 for SMTP
+#   envelope sender mail_host or mail_host domain names, URLs in mail message
+#   bodies, MX servers, DNS (NS) servers.  The second and third references to
+#   zen.spamhaus.org are recognized and only one for each IP address is sent to
+#   the DNSBL server for each IP address.
+# DNSBL_ARGS="'-Bset:rej-msg=5.7.1 550 %ID %BTYPE http://www.spamhaus.org/query/bl?ip=%BTGT' \
+#     -Bzen.spamhaus.org,127.0.0.0/29 -Bzen.spamhaus.org,127.0.0.8 \
+#     -Bset:no-NS  -Bset:no-mail_host -Bset:no-URL -Bzen.spamhaus.org"
+DNSBL_ARGS=
+
+
+DCCM_ENABLE=off
+#
+# used to start dccm
+#   a common value is
+#	DCCM_ARGS="-SHELO -Smail_host -SSender -SList-ID"
+#   Note the use of single quotes in
+#	DCCM_ARGS="-SHELO '-r5.7.1 550 mail %s from %s rejected with DCC'"
+DCCM_ARGS="-SHELO -Smail_host -SSender -SList-ID"
+# The log directories should be cleaned with the @libexecdir@/cron-dccd nightly
+#   cron job.  Set DCCM_LOGDIR to the empty or null string to disable logging.
+DCCM_LOGDIR="log"
+DCCM_WHITECLNT=whiteclnt
+DCCM_USERDIRS=userdirs
+# set DCCM_LOG_AT to a number that determines "bulk mail" for your situation.
+#   50 is a typical value.
+# Leave DCCM_REJECT_AT blank until you are confident that most sources of
+#   solicited bulk mail have been whitelisted.  Then set it to the number
+#   that defines "bulk mail" for your site.  This rejection or "bulk" threshold
+#   does not affect the blacklisting of the DCCM_WHITECLNT whitelist file.
+# Add '-aIGNORE' to DCCM_ARGS to ignore the bulkiness of mail except to
+#   add X-DCC headers.
+DCCM_LOG_AT=5
+DCCM_REJECT_AT=
+# override basic list of DCC server checksums controlling rejections or logging
+DCCM_CKSUMS=
+# additional DCC server checksums worthy of rejections or logging
+DCCM_XTRA_CKSUMS=
+
+
+DCCIFD_ENABLE=off
+#
+# used to start dccifd
+#   a common value is
+#   DCCIFD_ARGS="-SHELO -Smail_host -SSender -SList-ID"
+DCCIFD_ARGS="-SHELO -Smail_host -SSender -SList-ID"
+DCCIFD_LOGDIR="$DCCM_LOGDIR"
+DCCIFD_WHITECLNT="$DCCM_WHITECLNT"
+#   When both dccm and dccifd are used it may be necessary to set
+#   DCCIFD_USERDIRS="$DCCM_USERDIRS/local"
+DCCIFD_USERDIRS="$DCCM_USERDIRS"
+DCCIFD_LOG_AT="$DCCM_LOG_AT"
+DCCIFD_REJECT_AT="$DCCM_REJECT_AT"
+# override basic list of checksums controlling rejections or logging
+DCCIFD_CKSUMS="$DCCM_CKSUMS"
+# additional DCC server checksums worthy of rejections or logging
+DCCIFD_XTRA_CKSUMS="$DCCM_XTRA_CKSUMS"
+
+# days to keep files in DCC log directories
+DBCLEAN_LOGDAYS=14
+# used to start dbclean, including -e and -E
+DBCLEAN_ARGS=
+
+
+# optionally set to something like "local5" or "local5.notice" for
+#   dccd, dbclean, dccifd, and dccm.  The defaults are equivalent to
+#   DCC_INFO_LOG_FACILITY=MAIL.NOTICE and DCC_ERROR_LOG_FACILITY=MAIL.ERR
+DCC_INFO_LOG_FACILITY=
+DCC_ERROR_LOG_FACILITY=
+
+
+# ensure that the log facilities include levels and that $DCC_LOGGER
+#   has a default.
+if test -n "$DCC_INFO_LOG_FACILITY"; then
+    if expr "X$DCC_INFO_LOG_FACILITY" : 'X.*\..*' >/dev/null; then
+	:
+    else
+	DCC_INFO_LOG_FACILITY="$DCC_INFO_LOG_FACILITY.notice"
+    fi
+    DCC_LOG_ARGS="$DCC_LOG_ARGS -Linfo,$DCC_INFO_LOG_FACILITY"
+fi
+if test -z "$DCC_ERROR_LOG_FACILITY"; then
+    # for $DCC_LOGGER
+    DCC_ERROR_LOG_FACILITY=mail.err
+else
+    if expr "X$DCC_ERROR_LOG_FACILITY" : 'X.*\..*' >/dev/null; then
+	:
+    else
+	DCC_ERROR_LOG_FACILITY="$DCC_ERROR_LOG_FACILITY.err"
+    fi
+    DCC_LOG_ARGS="$DCC_LOG_ARGS -Lerror,$DCC_ERROR_LOG_FACILITY"
+fi
+DCC_LOGGER="@DCC_LOGGER@"
+
+
+# capture ./configure values for make-dcc_conf
+Configure_DCC_LIBEXEC=@libexecdir@
+Configure_DCC_RUNDIR=@dcc_rundir@
+Configure_DCCUID=@DCCSUID@
+Configure_DCC_LOGGER="@DCC_LOGGER@"
diff -r 000000000000 -r c7f6b056b673 homedir/fix-map
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/fix-map	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,118 @@
+#! /bin/sh -e
+
+# correct map file when switching between DCC and DCC Reputation code.
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+#	Rhyolite Software DCC 1.3.103-1.11 $Revision$
+
+
+set -e
+
+SUFFIX=`date +%y%m%d`
+DCC_HOMEDIR=
+CDCC=
+MAP=map
+
+USAGE="$0: [-x] [-m map] -c cdcc -h homedir"
+while getopts "xm:c:h:" c; do
+    case $c in
+	x) set -x;;
+	m) MAP="$OPTARG";;
+	c) CDCC="$OPTARG";;
+	h) DCC_HOMEDIR="$OPTARG";;
+	*) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0 -o -z "$CDCC" -o -z "$DCC_HOMEDIR"			\
+	    -o ! -d "$DCC_HOMEDIR/."; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+if expr "$MAP" : '/.*' >/dev/null; then :
+else
+    MAP=$DCC_HOMEDIR/$MAP
+fi
+if test ! -f "$MAP"; then
+    echo "$MAP does not exist" 1>&2
+    exit 1
+fi
+if test ! -w "$MAP" -o ! -r "$MAP"; then
+    echo "$MAP not readable and writable" 1>&2
+    exit 1
+fi
+
+if test ! -x "$CDCC"; then
+    # the cdcc command has usually not yet been installed, so use the local
+    #	version
+    echo "$CDCC does not seem to have been built" 1>&2
+    exit 1
+fi
+CDCC_CMD="$CDCC -qh $DCC_HOMEDIR file=$MAP"
+$CDCC_CMD >/dev/null </dev/null			# see that it works
+
+RSRVR=com-dcc.rhyolite.com
+RID=
+RPASSWD=
+if test -r $DCC_HOMEDIR/.updatedcc_pfile				\
+	    -a -s $DCC_HOMEDIR/.updatedcc_pfile; then
+    . $DCC_HOMEDIR/.updatedcc_pfile
+fi
+if test -r .updatedcc_pfile -a -s .updatedcc_pfile; then
+    . ./.updatedcc_pfile
+fi
+
+USING_DCC=`$CDCC_CMD info						\
+    | sed -n -e 's/^\([-a-z0-9]*\.dcc-servers\.net\),.*/\1/p'`
+USING_RHYOLITE=`$CDCC_CMD info						\
+    | sed -n -e 's/^\([-a-z0-9]*\.rhyolite\.com\),.*/\1/p'`
+
+
+# Replace references to the common DCC Reputation servers when installing the
+#   free DCC client software because the common DCC Reputation servers have
+#   probably forgotten this client's ID and so will refuse to answer.
+#   When we remove the common DCC Reputation servers, add the public servers
+#   as low priority backups to avoid leaving things broken.
+if test -n "$USING_RHYOLITE"; then
+    $CDCC_CMD "add dcc1.dcc-servers.net RTT+2000 ms"			\
+	"add dcc2.dcc-servers.net RTT+2000 ms"				\
+	"add dcc3.dcc-servers.net RTT+2000 ms"				\
+	"add dcc4.dcc-servers.net RTT+2000 ms"				\
+	"add dcc5.dcc-servers.net RTT+2000 ms"
+    for s in $USING_RHYOLITE; do
+	$CDCC_CMD "delete $s"
+    done
+fi
diff -r 000000000000 -r c7f6b056b673 homedir/flod
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/flod	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,19 @@
+# Sample DCC server peers
+#   This file contains peers to which the local DCC server should flood
+#   reports of checksums of possible spam.  See dccd(8).
+
+# In this example the two servers, dcc1.domain.com and dcc2.domain.com
+#   authenticate their floods of checksums with the passwords of the
+#   passwd-ID 1000.  See the sample ids file.
+
+# hostname,port	    rem-id [passwd-id] [out-opts] [in-opts]
+#dcc1.domain.com    101	1000
+#dcc2.domain.com    102	1000
+
+# This example floods to a local server using a private IDs.  This is
+#   NOT recommend.  See the description of server-ID remapping in the
+#   flod file description in the dccd man page.
+#temp.domain.com    32700 1000 - 32700-32767->self
+#   The first "-" in that line is significant.
+
+#	Rhyolite Software DCC 1.3.103-1.13 $Revision$
diff -r 000000000000 -r c7f6b056b673 homedir/grey_flod
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/grey_flod	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,12 @@
+# Sample Greylist server peers
+#   This file contains peers to which the local greylist server should flood
+#   reports of checksums of familiar correspondents and embargoed messages.
+#   See dccd(8).
+
+# In this example the two servers  See the sample ids file.
+#   hostname,port	    rem-id [passwd-id] [out-opts] [in-opts]
+#grey1.domain.com    32702
+#grey2.domain.com    32703
+
+
+#	Rhyolite Software DCC 1.3.103-1.2 $Revision$
diff -r 000000000000 -r c7f6b056b673 homedir/grey_whitelist
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/grey_whitelist	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,17 @@
+# whitelist required by greylist DCC server
+#   See the dcc man page for the format of DCC white and blacklists.
+#   Each line must be in one of the forms
+#count [hex] type value
+#      [hex] type value
+#include pathname
+
+#Changes to this file or the files it includes have no effect until `dbclean -G`
+#   is run, perhaps by /var/dcc/libexec/cron-dccd
+
+#	See http://www.rhyolite.com/dcc/ about the DCC.
+
+#	2004/01/09 18:46:08
+
+
+# some mailing lists and abuse auto-responders
+include whitecommon
diff -r 000000000000 -r c7f6b056b673 homedir/ids
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/ids	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,54 @@
+# Sample DCC server IDs and passwords as well as client IDs and passwords
+#  See dccd(8)
+
+# Each ID must have at least one password.  A second password can be present
+#   to ease changing passwords.  Either password is always acceptable.
+#   One password can be the secret currently used and the other can be the
+#   previous or future secret.
+
+# Every DCC server must have a unique server-ID.  A server ID is a number
+#   between 100 and 32767.  A DCC server is a system running dccd.
+# Clients use client-IDs, which are the anonymous client-ID 1 and numbers
+#   starting at 32768.  DCC clients are systems that process mail and
+
+# Server passwords or the passwords associated with server-IDs are used in
+#   two areas.  First, the cdcc program uses them to authenticate its commands
+#   to a server.  Passwords and IDs used for this purpose should be guarded
+#   carefully. Second, passwords are used to are used to authenticate floods
+#   of checksums among DCC servers.
+#
+# Client-IDs are used by servers to identify and authenticate clients.
+#   Servers can be configured to give better service to clients not
+#   using the anonymous client-ID or to give reduced or no service
+#   to anonymous clients.
+
+# It is handy to reserve a server-ID for inter-server flooding, and call it
+#   a "passwd-ID."  That way the people running the DCC server on
+#   dcc1.domain.com do not need to know the password that the people running
+#   the dcc2.example.com server use to control that server, and vice versa.
+#   If they knew each other's secret passwords, then each could control each
+#   other's server with `cdcc`.
+
+######################################################
+# Every DCC client organization should have a unique client-ID.
+#   Client programs and computers within an organization can share client-IDs.
+
+
+# sample client-ID
+#   This password is generated by the installation script, and so may not be
+#   as secret as necessary.  This password must be the same as the password
+#   in the map file.  `make install` replaces "secret1" with a suitable
+#   password here and in the map file.
+32768	secret1
+
+# server-IDs
+1000
+
+# auto local greylist server-ID
+#	The preceding line is used by /var/dcc/libexec/start-grey
+#	and /var/dcc/libexec/stop-dccd to find the following line containing
+#	the password generated by the installation script:
+32702	secret2
+
+
+#	Rhyolite Software DCC 1.3.103-1.26 $Revision$
diff -r 000000000000 -r c7f6b056b673 homedir/make-dcc_conf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/make-dcc_conf	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,183 @@
+#! /bin/sh -e
+
+# generate a new dcc_conf file from an existing file
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+#	Rhyolite Software DCC 1.3.103-1.23 $Revision$
+
+FNAME=dcc_conf
+
+CUR=
+PROTOTYPE=$FNAME
+SUFFIX=new
+NEW=
+DCC_HOMEDIR=
+
+USAGE="$0: [-x] [-i cur] [-s new-suffix] [-p prototype] [-o new] -h homedir"
+while getopts "xi:s:p:o:h:" c; do
+    case $c in
+	x) set -x;;
+	i) CUR="$OPTARG";;
+	s) if test -n "$OPTARG"; then
+	    SUFFIX="$OPTARG"
+	    fi
+	    ;;
+	p) PROTOTYPE="$OPTARG";;
+	o) NEW="$OPTARG";;
+	h) DCC_HOMEDIR="$OPTARG";;
+	*) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0 -o ! -d "$DCC_HOMEDIR"; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+
+if test ! -s "$PROTOTYPE"; then
+    echo "prototype $PROTOTYPE does not exist" 1>&2
+    exit 1
+fi
+
+if test -z "$CUR"; then
+    CUR="$DCC_HOMEDIR/$FNAME"
+fi
+if test ! -f "$CUR"; then
+    echo "$CUR does not exist" 1>&2
+    exit 1
+fi
+
+if test -z "$NEW"; then
+    NEW="$CUR-$SUFFIX"
+fi
+
+# Use /^[ABCDEFGHIJKLMNOPQRSTUVWXYZ]/ patterns instead of easier to read,
+# probably faster /^[A-Z/ patterns because someone has decided that
+# GNU Awk 3.1.3 (at least on x86_64) should have /^[A-Z]/ match "fi"
+# with IGNORECASE=0 and even in traditional mode.
+
+rm -f $NEW
+awk '(first_file == "") {
+	    first_file = FILENAME;
+	}
+	/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ]/ {
+	    # deal with continuation lines
+	    line = $0;
+	    while (substr(line,length(line)) == "\\") {
+		if (getline <= 0) {
+		    break;
+		}
+		line = line "\n" $0;
+	    }
+	    # divide the line into the variable name or key and its value
+	    inx = index(line, "=");
+	    if (inx == 0) {
+		inx = length(line)+1;
+	    }
+	    key = substr(line, 1, inx-1);
+	    val = substr(line, inx+1);
+	    # only record things in the first file
+	    if (first_file == FILENAME) {
+		defined[key] = 1;
+		mem[key] = val;
+		if (cur_comment) {
+		    comments[key] = cur_comment;
+		    cur_comment = "";
+		}
+	    } else {
+		# on first line of second file, make compatibility adjustments
+		if (!adjusted) {
+		    adjusted = 1;
+		    if (mem["DCCD_ENABLE"] == "") {
+			defined["DCCD_ENABLE"] = 1;
+			if (mem["SRVR_ID"] == "") {
+			    mem["DCCD_ENABLE"] = "off";
+			} else {
+			    mem["DCCD_ENABLE"] = "on";
+			}
+		    }
+		    # fix DCC_RUNDIR=@dcc_rundir@ bug in 1.2.14 and preceding
+		    if (defined["DCC_RUNDIR"]) {
+			if (mem["DCC_RUNDIR"] == "@dcc_rundir@") {
+			    defined["DCC_RUNDIR"] = 0;
+			}
+		    }
+		    # Use new values of some variables if their old values were
+		    #	defaults.  This makes ./configure changes effective.
+		    if (mem["Configure_DCC_LIBEXEC"] == mem["DCC_LIBEXEC"]) {
+			defined["DCC_LIBEXEC"] = 0;
+		    }
+		    if (mem["Configure_DCC_RUNDIR"] == mem["DCC_RUNDIR"]) {
+			defined["DCC_RUNDIR"] = 0;
+		    }
+		    if (mem["Configure_DCCSUID"] == mem["DCCSUID"]) {
+			defined["DCCSUID"] = 0;
+		    }
+		    if (mem["Configure_DCC_LOGGER"] == mem["DCC_LOGGER"]) {
+			defined["DCC_LOGGER"] = 0;
+		    }
+		    # use new, configured values of some variables
+		    if (defined["DCC_CONF_VERSION"])
+			old_version = mem["DCC_CONF_VERSION"];
+		    defined["DCC_CONF_VERSION"] = 0;
+		    defined["Configure_DCC_LIBEXEC"] = 0;
+		    defined["Configure_DCC_RUNDIR"] = 0;
+		    defined["Configure_DCCSUID"] = 0;
+		    defined["Configure_DCC_LOGGER"] = 0;
+		}
+		if (defined[key]) {
+		    if (comments[key] && old_version > 3) {
+			print comments[key];
+		    }
+		    print key "=" mem[key];
+		} else {
+		    print line;
+		}
+	    }
+	}
+	!/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ]/ {
+	    if (FILENAME != first_file) {
+		print $0;
+	    } else {
+		if ($0 ~ /^ *#/ && $1 != "#") {
+		    if (cur_comment) {
+			cur_comment = cur_comment "\n" $0;
+		    } else {
+			cur_comment = $0;
+		    }
+		}
+	    }
+	}' $CUR $PROTOTYPE >$NEW
diff -r 000000000000 -r c7f6b056b673 homedir/map.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/map.txt	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,32 @@
+# Sample DCC client information
+#   See cdcc(8) about loading this file into the memory mapped client file,
+#   /var/dcc/map, with the "load" command.  When used for that purpose,
+#   it must not be publicly readable.
+
+#   This file is otherwise only documentation for the mapped client file.
+#   It is handy to regularly update it with a command like
+#	`cdcc -q info > map.txt` and archive it with RCS or SCCS.
+#   The cdcc command must be run as the dcc user or the passwords will not be
+#   captured.
+
+# public DCC servers
+#	REMOVE THESE LINES from any spam appliance or filter that you sell.
+#	It is wrong to take and sell the human system administration work
+#	of the operators of the public DCC servers.
+dcc1.dcc-servers.net	RTT+1000 ms	anon
+dcc2.dcc-servers.net	RTT+1000 ms	anon
+dcc3.dcc-servers.net	RTT+1000 ms	anon
+dcc4.dcc-servers.net	RTT+1000 ms	anon
+dcc5.dcc-servers.net	RTT+1000 ms	anon
+
+#   `make install` replaces "secret1" with a unique password.  Extreme
+#   security is not needed for these passwords.
+
+# local DCC server
+127.0.0.1   RTT-1000 ms    32768 secret1
+
+# local greylist server
+127.0.0.1   GREYLIST	   32768 secret1
+
+
+#	Rhyolite Software DCC 1.3.103-1.24 $Revision$  2007/10/25 14:18:15
diff -r 000000000000 -r c7f6b056b673 homedir/whiteclnt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/whiteclnt	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,126 @@
+# sample whitelist for DCC clients
+
+# Changes in this file and the files it includes are noticed automatically
+#   within a few minutes.
+
+
+# See the dcc man page for the format of DCC whitelists.
+#   Each line must be in one of the forms:
+#count [hex] type value
+#      [hex] type value
+
+# Hexadecimal checksum values can be obtained from dccm, dccifd, and
+#   dccproc log files.
+
+
+#include pathname
+
+
+#option log-all
+#option	log-normal
+#option	option log-subdirectory-day
+#option	option log-subdirectory-hour
+#option	option log-subdirectory-minute
+#option	dcc-on
+#option	dcc-off
+#option	greylist-on
+#option	greylist-off
+#option	greylist-log-on
+#option	greylist-log-off
+#option	DCC-reps-off
+#option	DCC-reps-on
+#option	DNSBL-off
+#option	DNSBL-on
+#option	MTA-first
+#option	MTA-last
+#option	forced-discard-ok
+#option	no-forced-discard
+#option	threshold cksum_type,targets
+#option spam-trap-accept
+#option spam-trap-reject
+
+
+# Do not tell the DCC servers about purely local mail.
+#   If you don't delete this line, you should probably add lines listing
+#   all of the host names of this system.
+ok	ip	127.0.0.1
+
+# List statically allocated IP addresses that you trust to never send
+#   or forward unsolicited bulk email
+#ok	ip	10.1.2.0/24
+
+# List secondary MX relays or any MX relays that might forward spam to this 
+#	system.  
+#mx	ip	10.3.4.5
+#mx	ip	10.6.7.0/28
+#   Use mxdcc instead of mx if the relay does DCC checks
+#mxdcc	ip	10.8.9.10
+
+# List SMTP submission clients such as web browsers that cannot tolerate
+#   4yz temporary rejections but that cannot be trusted to never send spam
+#submit	ip	10.4.5.0/24
+
+
+# Do not filter postmaster to avoid rejecting reports of spam.
+#   As with all header checksums, all valid forms of the address must
+#   be listed.
+#ok	env_to	postmaster
+#ok	env_to	postmaster@example.com
+#ok	env_to	postmaster@host.example.com
+
+# See http://www.iecc.com/dcc-testmsg-whitelist.txt for list of checksums
+#   of practically blank messages.  Such checksums can be usefully whitelisted.
+#   See /var/dcc/libexec/fetch-testmsg-whitelist for a cron script to
+#   fetch them.
+#include testmsg-whitelist
+
+# If dccm or dccifd is run with "-S mail_host" by adding that to DCCM_ARGS
+#   or DCCIFD_ARGS in /var/dcc/dcc_conf, then uncommenting the following
+#   line would white-list all mail from the SMTP client at example.com.
+#ok	substitute mail_host example.com
+
+# If dccm, dccifd, or dccproc is run with "-S Mailing-list", then this line
+#   would white-list all mail with either of two Mailing-List header
+#   values:
+#ok	hex substitute Mailing-List e78e0f7f b0d5212c 8a1a433a 769ad0fd
+#ok	substitute Mailing-List host.example.com
+
+# A lot of bulk mail lacks message-ID header lines.
+#   If you receive much mail from lame mailing lists (often involving
+#	qmail) or solicited bulk mail you probably do not want to use
+#	this blacklist entry.
+# many	message-id <>
+
+# This rejects messages with substantial text but few words.
+#   If you receive binary or non-text email, you probably
+#   do not want to use this blacklist entry
+#many	hex	FUZ2: 00000000 00000000 00000000 00000000
+
+# Mail with SMTP HELO values commonly used seen in spam can be rejected
+#   and reported to the DCC server as spam by running dccproc, dccifd, or dccm
+#   with "-S HELO" and uncommenting the following lines:
+#many	substitute helo localhost
+#many	substitute helo EmailSender
+#many	substitute helo Super
+#many	substitute helo Testsmtp
+#many	substitute helo laptop
+#many	substitute helo localhost.com
+#many	substitute helo localhost.localdomain
+#many	substitute helo newsserver
+#many	substitute helo oemcomputer
+#many	substitute helo proxy
+#many	substitute helo server
+#many	substitute helo smtp.localhost.localdomain
+#many	substitute helo test.com
+#many	substitute helo unknown
+#many	substitute helo webhome
+#many	substitute helo webserver
+#many	substitute helo whatever-your-domain-name-might-be.com
+#many	substitute helo www
+#many	substitute helo yourwebsite.com
+#
+#many	substitute helo 10.0.0.1
+#many	substitute helo IP-addresses-of-your-DNS-HTTP-and-SMTP-servers
+
+# whitelist values common to the server and client
+include whitecommon
diff -r 000000000000 -r c7f6b056b673 homedir/whitecommon
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/whitecommon	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,47 @@
+# This list contains a few mailing lists to show how the file might be used.
+#	There is no guarantee that they are in any sense valid or
+#	certain to not send spam
+
+#	The list is in the form of a Distributed Checksum Clearinghouse
+#	whitelist or blacklist used by a DCC client such as dccproc or dccm
+#	or by a DCC server such as dccd.
+#	Something like it is commonly used by both clients and servers on
+#	a system by using "include" in individual DCC whitelists.
+
+# This file is often included in whiteclnt, whitelist, and grey_whitelist files.
+
+# This file is copied to /var/dcc/whitecommon by the official Makefiles
+#   only only if /var/dcc/whitecommon does not exist.  New versions are not
+#   installed over existing /var/dcc/whitecommon by the updatedcc script.
+
+#   Each line must be in one of the forms
+#count type value
+#      type value
+
+# See http://www.rhyolite.com/dcc/ about the DCC.
+
+
+################
+# example mailing lists
+#	entries of type "substitute mail_host" require `dccm -S mail_host`,
+#	    ir or `dccproc -S mail_host`
+#	entries of type "substitute Sender" require `-S Sender`
+#	entries of type "substitute List-ID" require `-S List-ID`
+
+# sendmail.org
+#ok	env_from	owner-sendmail-announce@lists.Sendmail.ORG
+#	env_from	sendmail-announce-request@lists.sendmail.org
+#	env_from	sendmail-beta-request@knecht.Neophilic.COM
+#	substitute mail_host sendmail.org
+
+# Rhyolite.com DCC
+ok  substitute List-ID: Distributed Checksum Clearinghouse <dcc.rhyolite.com>
+
+# BUGTRAQ
+#ok	substitute mail_host securityfocus.com
+
+# news.admin.net-abuse.sightings confirmations
+#ok	from		Bob the NANAS ModBot <nanas-req@cybernothing.org>
+
+
+#	2008/06/01 01:38:42
diff -r 000000000000 -r c7f6b056b673 homedir/whitelist
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homedir/whitelist	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,25 @@
+# whitelist file required by DCC server
+#   Unless you are running a DCC server, this file will not be used.  Even
+#   if you are running a DCC server, it is almost always better to put entries
+#   in the client whiteclnt files.
+
+#   See the dcc man page for the format of DCC white and blacklists.
+#   Each line must be in one of the forms
+#count [hex] type value
+#      [hex] type value
+#include pathname
+
+# Whitelists for DCC servers are usually not a good idea.  It is usually
+#   better to use whitelists only with DCC clients such as dccproc, dccifd,
+#   and dccm.
+
+#Changes to this file or the files it includes have no effect until dbclean
+#   is run, perhaps by /var/dcc/libexec/cron-dccd
+
+#	See http://www.rhyolite.com/dcc/ about the DCC.
+
+#	2007/09/21 20:03:38
+
+
+# the whitecommon file is commonly included in client DCC whitelists
+#include whitecommon
diff -r 000000000000 -r c7f6b056b673 include/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,17 @@
+dcc_ck.h
+dcc_clnt.h
+dcc_config.h
+dcc_config.h.in
+dcc_defs.h
+dcc_heap_debug.h
+dcc_ids.h
+dcc_ifaddrs.h
+dcc_md5.h
+dcc_paths.h
+dcc_proto.h
+dcc_xhdr.h
+dccif.h
+helper.h
+kludge.h.in
+sendmail-sysexits.h
+.manifest
diff -r 000000000000 -r c7f6b056b673 include/dcc_ck.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dcc_ck.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,779 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * checksum routines
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.182 $Revision$
+ */
+
+#ifndef DCC_CK_H
+#define DCC_CK_H
+
+#include "dcc_clnt.h"
+#include "dcc_md5.h"
+
+
+typedef DCC_TGTS DCC_CKSUM_THOLDS[DCC_DIM_CKS];
+#define DCC_THOLD_NEVER	    (DCC_TGTS_TOO_MANY+1)
+#define DCC_THOLD_UNSET	    (DCC_TGTS_INVALID)
+
+extern DCC_CKSUM_THOLDS dcc_tholds_rej;
+
+
+/* MIME boundary
+ * RFC 1341 says boundaries can be 70 bytes, but handle non-conformant spam */
+#define DCC_CK_BND_MAX	    94
+#define DCC_CK_BND_DIM	(DCC_CK_BND_MAX+2)  /* including leading -- */
+#define DCC_CK_BND_MISS	(DCC_CK_BND_DIM+1)
+typedef struct {
+    u_char	bnd_len;		/* length including leading "--" */
+    u_char	cmp_len;		/* compared so far */
+    char	bnd[DCC_CK_BND_DIM];	/* "--"boundary */
+} DCC_CK_BND;
+
+
+typedef u_char DCC_CK_FC[256];
+extern const DCC_CK_FC dcc_cset_1, dcc_cset_2;
+
+/* state machine for ignoring parts of URLs */
+typedef struct {
+    enum {
+	DCC_URL_ST_IDLE,		/* waiting for H or = */
+	DCC_URL_ST_QUOTE,		/*    "     " '"' or 'H' after = */
+	DCC_URL_ST_QH,			/*    "     "  H after quote */
+	DCC_URL_ST_T1,			/*    "     "  T */
+	DCC_URL_ST_T2,			/*    "     "  T */
+	DCC_URL_ST_P,			/*    "     "  P */
+	DCC_URL_ST_S,			/*    "     "  [S] */
+	DCC_URL_ST_COLON,		/*    "     "  : */
+	DCC_URL_ST_SLASH1,		/*    "     "  / */
+	DCC_URL_ST_SLASH2,		/*    "     "  / */
+	DCC_URL_ST_SLASH3_START,
+	DCC_URL_ST_SLASH3,		/*    "     "  third / */
+	DCC_URL_ST_SKIP			/* skipping rest of URL */
+    } st;
+    char	*start;			/* start of hostname in URL */
+    char	*dot;			/* last '.' in hostname */
+    int		total;			/* length of URL */
+    u_char	flags;
+#    define	 DCC_URL_QUOTED	    0x01
+#    define	 DCC_URL_DEL_DOMAIN 0x02    /* waiting for domain to delete */
+#    define	 DCC_URL_PERCENT1   0x04    /* waiting for 1st digit after % */
+#    define	 DCC_URL_PERCENT2   0x08    /* waiting for 2nd digit after % */
+#    define	 DCC_URL_SQUOTED    0x10    /* single quoted (') URL */
+#    define	 DCC_URL_DQUOTED    0x20    /* double quoted (") URL */
+#    define	 DCC_URL_QUOTES (DCC_URL_DQUOTED | DCC_URL_SQUOTED)
+#    define	 DCC_URL_SIMPLE (DCC_URL_DEL_DOMAIN	\
+				 | DCC_URL_PERCENT1	\
+				 | DCC_URL_PERCENT2)
+    u_char	percent;
+} DCC_URL_SKIP;
+#define DCC_URL_MAX	    65		/* 63 is RFC 1034 limit on labels */
+#define DCC_URL_FAILSAFE    2000	/* big bogus "username:password@" */
+
+
+/* a template for generating a reply to an SMTP command */
+typedef enum {
+    REPLY_TPLT_NULL,
+    REPLY_TPLT_ID,	/* queue ID for sendmail, fake for dccifd */
+    REPLY_TPLT_CIP,	/* SMTP client IP address */
+    REPLY_TPLT_BTYPE,	/* type of DNSBL hit */
+    REPLY_TPLT_BTGT,	/* DNSBL hit IP address or name */
+    REPLY_TPLT_BPROBE,	/* DNSBL probe domain name */
+    REPLY_TPLT_BRESULT	/* DNSBL probe result */
+} REPLY_TPLT_ARGS;
+typedef struct {
+    const char *log_result;		/* "reject" etc. for log */
+#    define NUM_REPLY_TPLT_ARGS 6
+    REPLY_TPLT_ARGS args[NUM_REPLY_TPLT_ARGS];
+    char    rcode[sizeof("5yz")];
+    char    xcode[sizeof("1.2.3")];
+    u_char  is_pat;			/* template has %s references */
+#    define  REPLY_BUF_LEN 256
+    char    pat[REPLY_BUF_LEN];		/* pattern for SMTP message */
+}  REPLY_TPLT;
+
+
+/* specify a DNS blacklist */
+typedef struct {
+    char    c[DCC_MAXDOMAINLEN];	/* null terminated */
+} DNSBL_DOM;
+
+typedef u_char DNSBL_FGS;
+#define	DNSBL_FG_DUP	    0x01	/* duplicate blacklist */
+#define DNSBL_FG_TIMEO	    0x02	/* suffered a timeout */
+#define	DNSBL_FG_TFAIL	    0x04	/* want SMTP failure on timeout */
+#define	DNSBL_FG_CLIENT	    0x08	/* check client IP address */
+#define	DNSBL_FG_MAIL_HOST  0x10	/* check env_from domain */
+#define	DNSBL_FG_URL	    0x20	/* check URLs in message body */
+#define	DNSBL_FG_MX	    0x40	/* MX for _URL & _MAIL_HOST */
+#define	DNSBL_FG_NS	    0x80	/* NS for _URL & _MAIL_HOST */
+# define DNSBL_FG_HITS	   (DNSBL_FG_CLIENT | DNSBL_FG_MAIL_HOST | DNSBL_FG_URL)
+# define DNSBL_FG_TYPES	   (DNSBL_FG_HITS | DNSBL_FG_MX | DNSBL_FG_NS)
+# define DNSBL_FG_DEFAULT   DNSBL_FG_TYPES
+
+typedef enum DNSBL_TYPE {
+    DNSBL_TYPE_IPV4,
+    DNSBL_TYPE_IPV6,
+    DNSBL_TYPE_NAME
+} DNSBL_TYPE;
+
+#define	MAX_DNSBL_GROUPS    3
+typedef u_char	DNSBL_GBITS;
+#define DNSBL_G2B(n) ((1<<(n)) & ((2<<MAX_DNSBL_GROUPS)-1))
+
+typedef struct dnsbl {
+    struct dnsbl *fwd;
+    struct dnsbl *dup;			/* same except result */
+    u_char	group;			/* 1-indexed */
+    struct in6_addr tgt;		/* hit if DNSBL matches this block */
+    struct in6_addr tgt_mask;
+    u_char	tgt_use_ipv6;		/* ipv4 vs. ipv6 result weighting */
+    DNSBL_TYPE	bl_type;
+    int		bl_num;
+    int		bl_dom_len;
+    DNSBL_DOM	bl_dom;
+    const REPLY_TPLT *reply;
+    DNSBL_FGS	fgs;
+} DNSBL;
+
+/* working storage for one set of DNSBLs */
+typedef struct {
+    DNSBL_FGS	fgs;			/* kind of DNSBL hit */
+    const char	*btype;			/* type of hit string */
+    const DNSBL	*dnsbl;			/* hit DNSBL */
+    char	btype_buf[64];
+    char	result[DCC_SU2STR_SIZE];    /* IP address found in DNSBL */
+    DNSBL_DOM	tgt;			/* IP address or name sought in DNSBL */
+    DNSBL_DOM	probe;			/* what was actually looked up */
+} DNSBL_GROUP;
+
+/* working storage for all DNSBLs */
+typedef struct {
+    DNSBL_DOM	dom;			/* domain name */
+    struct in6_addr addr;		/* and/or IP address sought in DNSBL */
+} DNSBL_TGT;
+typedef struct {
+    DNSBL_GBITS all;
+    DNSBL_GBITS	client;
+    DNSBL_GBITS	mail_host;
+    DNSBL_GBITS	mail_host_ns;
+    DNSBL_GBITS	mail_host_mx;
+    DNSBL_GBITS	url;
+    DNSBL_GBITS	url_ns;
+    DNSBL_GBITS	url_mx;
+} DNSBL_UNHIT;
+typedef struct {
+    DCC_CLNT_CTXT *dcc_ctxt;
+    void	*log_ctxt;
+    const char	*id;
+    time_t	msg_us;			/* microseconds remaining for msg */
+    struct timeval url_start;
+    time_t	url_us;			/* microseconds to look up URL */
+    time_t	url_us_used;
+    DNSBL_UNHIT	unhit;			/* groups of DNSBLs not yet hit */
+    u_short	tgt_dom_len;
+    DNSBL_TGT	tgt;
+    struct {
+	DNSBL_DOM   dom;
+    } tgt_cache[8];
+    int		tgt_cache_pos;
+    DNSBL_GROUP	groups[MAX_DNSBL_GROUPS];
+} DNSBL_WORK;
+
+extern DNSBL *dnsbls;
+extern u_char have_dnsbl_groups;
+extern u_char have_helpers;
+extern DCC_PATH dnsbl_progpath;
+
+
+
+/* accumulated checksums for an SMTP message */
+typedef struct {
+    const char *hdr_nm;			/* name if substitute checksum */
+    DCC_TGTS	tgts;			/* server's answer or previous total */
+    DCC_CK_TYPE_B type;
+    u_char	rpt2srvr;		/* 1=report to server */
+    DCC_SUM	sum;
+} DCC_GOT_SUM;
+#define CLR_GOT_SUM(g) ((g)->hdr_nm = 0, (g)->tgts = DCC_TGTS_INVALID,	\
+			(g)->type = DCC_CK_INVALID, (g)->rpt2srvr = 0)
+
+typedef union {
+    u_int32_t	w32[4];
+    u_char	b[16];
+} DCC_FUZ2_WORD;
+#define DCC_FUZ2_WORD_CLEAR(_w) ((_w)->w32[0]=0, (_w)->w32[1]=0,	\
+				 (_w)->w32[2]=0, (_w)->w32[3]=0)
+
+typedef struct {
+    DCC_URL_SKIP url;
+    u_int	total;			/* bytes */
+    char	*cp;			/* end of data in buffer */
+    char	*eol;			/* most recent eol */
+#    define	 DCC_FUZ1_MAX_LINE  78
+#    define	 DCC_HTTPS_STR	    "https"
+#    define	 DCC_HTTPS_LEN	    LITZ(DCC_HTTPS_STR)
+    char	buf[DCC_FUZ1_MAX_LINE*4
+		    +DCC_HTTPS_LEN];
+    MD5_CTX	md5;
+} DCC_CTX_FUZ1;
+
+
+typedef struct {			/* per-language counts */
+    u_int	wsummed;		/* bytes of words summed */
+    u_int	wtotal;			/* words summed */
+    MD5_CTX	md5;
+} FUZ2_LANG;
+
+typedef struct {
+    u_int	btotal;			/* total non-whitespace bytes seen */
+    u_int	xsummed;		/* non-word bytes checksummed */
+    u_int	wlen;			/* length of current word */
+    u_int	cref_cnt;		/* fancy character length */
+    u_int	tag_len;		/* HTML tag length */
+    DCC_FUZ2_WORD w;
+    DCC_FUZ2_WORD cref_w;
+    DCC_FUZ2_WORD tag;
+    int		urls;			/* # of URLs seen */
+    char	*url_cp;		/* accumulated URLs */
+#    define	 DCC_FUZ2_URL_MAX   (1+DCC_URL_MAX)
+    char	url_buf[DCC_FUZ2_URL_MAX*2];
+    DCC_URL_SKIP url;			/* state machine for skipping URLs */
+    enum {
+	DCC_CREF_ST_IDLE = 0,
+	DCC_CREF_ST_START,		/* get HTML character reference */
+	DCC_CREF_ST_NUM,		/*	hex or decimal */
+	DCC_CREF_ST_DEC,
+	DCC_CREF_ST_HEX,
+	DCC_CREF_ST_NAME
+    } cref_st;
+    enum {
+	DCC_FUZ2_ST_WORD = 0,		/* gathering word */
+	DCC_FUZ2_ST_START_TAG,		/* gathering start of HTML tag */
+	DCC_FUZ2_ST_SKIP_TAG,		/* skipping HTML tag */
+	DCC_FUZ2_ST_SKIP_COMMENT	/* skipping HTML comment */
+    } st;
+#    define FUZ2_LAN_NUM 3
+    FUZ2_LANG	lang[FUZ2_LAN_NUM];
+} DCC_CTX_FUZ2;
+
+/* The maximum length of a checksumed HELO value and the continuation string
+ * need to be the same for all users of the local white-/blacklist */
+#define DCC_HELO_MAX	(DCC_MAXDOMAINLEN+8)
+#define	DCC_HELO_CONT	"..."
+
+/* local-part maximum in RFC 2821 */
+#define DCC_LOCAL_PART_MAX  64
+#define DCC_ENV_FROM_MAX    (DCC_LOCAL_PART_MAX+DCC_MAXDOMAINLEN)
+
+#define DCC_MSG_ID_LEN	    24
+
+/* do not checksum more than this much of a header line */
+#define DCC_HDR_CK_MAX	1024
+
+/* substitute or locally configured checksums */
+#define DCC_MAX_SUB_CKS 6
+
+#define DCC_WSUMS (DCC_DIM_CKS+DCC_MAX_SUB_CKS)	/* max whitelist checksums */
+typedef struct {
+    DCC_GOT_SUM	sums[DCC_WSUMS];
+    struct in6_addr ip_addr;		/* SMTP client IP address */
+    struct {				/*  quoted-printable state machine */
+	int	    x;
+	int	    y;
+	u_char	    n;
+	enum {
+	    DCC_CK_QP_IDLE,		/* waiting for '=' */
+	    DCC_CK_QP_EQ,		/* seen "=" */
+	    DCC_CK_QP_1,		/* seen "=X" of "=XY" */
+	    DCC_CK_QP_FAIL1,		/* have output '=' after bad =X */
+	    DCC_CK_QP_FAIL2,		/* have output '=' after bad =XY */
+	    DCC_CK_QP_FAIL3		/* have output 'X' after bad =XY */
+	} state;
+    } qp;
+    struct {				/* base64 state machine */
+	u_int32_t   quantum;
+	int	    quantum_cnt;
+    } b64;
+    enum {
+	CK_MP_ST_PREAMBLE,		/* preamble before first boundary */
+	CK_MP_ST_BND,			/* MIME boundary */
+	CK_MP_ST_HDRS,			/* headers after a boundary */
+	CK_MP_ST_TEXT,			/* body or entity */
+	CK_MP_ST_EPILOGUE		/* text after last boundary */
+    } mp_st;
+    enum CK_MHDR_ST {			/* parse entity fields */
+	CK_MHDR_ST_IDLE,
+	CK_MHDR_ST_CE_CT,		/* matching "Content-T" */
+	CK_MHDR_ST_CE,			/*	"ransfer-Encoding:" */
+	CK_MHDR_ST_CE_WS,		/* white space after "encoding:" */
+	CK_MHDR_ST_CT,			/*	"ype:" */
+	CK_MHDR_ST_CT_WS,		/* white space after "type:" */
+	CK_MHDR_ST_QP,			/*	"quoted-printable" */
+	CK_MHDR_ST_B64,			/*	"base64" */
+	CK_MHDR_ST_TEXT,		/*	"text" */
+	CK_MHDR_ST_HTML,		/*	"/html" */
+	CK_MHDR_ST_CSET_SKIP_PARAM,	/*	skip to ";" after "text" */
+	CK_MHDR_ST_CSET_SPAN_WS,	/*	skip blanks before "charset" */
+	CK_MHDR_ST_CSET,		/* match "charset=" */
+	CK_MHDR_ST_CSET_ISO_8859,	/*	"ISO-8859-" */
+	CK_MHDR_ST_CSET_ISO_X,		/* find the 8859 type number */
+	CK_MHDR_ST_MULTIPART,		/* match "multipart" */
+	CK_MHDR_ST_BND_SKIP_PARAM,	/* skip "/alternative;" or similar */
+	CK_MHDR_ST_BND_SPAN_WS,		/* skip whitespace before "boundary" */
+	CK_MHDR_ST_BND,			/* match "boundary=" */
+	CK_MHDR_ST_BND_VALUE		/* collecting boundary */
+    } mhdr_st;
+    u_char	mhdr_pos;
+    u_char	mime_nest;		/* # of nested MIME entities */
+    u_char	mime_bnd_matches;	/* # of active boundary matches */
+    DCC_CK_BND	mime_bnd[3];
+    enum {
+	DCC_CK_CT_TEXT = 0,
+	DCC_CK_CT_HTML,
+	DCC_CK_CT_BINARY
+    } mime_ct;
+    const u_char *mime_cset;
+    enum {
+	DCC_CK_CE_ASCII = 0,
+	DCC_CK_CE_QP,
+	DCC_CK_CE_B64
+    } mime_ce;
+    struct {
+	u_int	    total;		/* non-whitespace text */
+	u_char	    flen;		/* bytes of ">From" seen */
+	MD5_CTX	    md5;
+    } ctx_body;
+    DCC_CTX_FUZ1 fuz1;
+    DCC_CTX_FUZ2 fuz2;
+    DNSBL_WORK	*dnsbl;			/* pointer to malloc()'ed state */
+    DCC_CKSUM_THOLDS tholds_rej;
+    u_char	flags;
+#    define	 DCC_CKS_MIME_BOL	0x01    /* header decoder is at BOL */
+#    define	 DCC_CKS_MIME_QUOTED	0x02    /* quoted boundary */
+} DCC_GOT_CKS;
+
+typedef DCC_TGTS DCC_CKS_WTGTS[DCC_WSUMS];
+
+
+/* sscanf() a checksum */
+#define DCC_CKSUM_HEX_PAT	"%8x %8x %8x %8x"
+
+
+
+
+/* whiteclnt files */
+
+#define DCC_WHITE_SUFFIX	".dccw"
+#define DCC_WHITE_NEW_SUFFIX	".dccx"	/* hash table under construction */
+
+typedef char DCC_WHITE_MAGIC[128];
+#define WHITE_MAGIC_B_STR "DCC client whitelist hash table version "
+#define WHITE_MAGIC_V_STR "23"
+
+typedef struct {
+    DCC_TGTS	tgts;
+    int		bits;
+    struct in6_addr addr;
+    struct in6_addr mask;
+    u_short	lno;
+    u_char	fno;
+} DCC_WHITE_CIDR_ENTRY;
+
+typedef struct {
+    u_char	len;
+#    define	 WHITE_CIDR_MAX_ENTRIES 64
+    DCC_WHITE_CIDR_ENTRY e[WHITE_CIDR_MAX_ENTRIES];
+} DCC_WHITE_CIDR;
+
+typedef u_int DCC_WHITE_INX;		/* 1-based index into hash table */
+
+typedef struct {
+    DCC_WHITE_INX fwd;
+    DCC_TGTS	tgts;
+    u_short	lno;
+    u_char	fno;
+    DCC_CK_TYPE_B type;
+    DCC_SUM	sum;
+} DCC_WHITE_ENTRY;
+
+#ifdef DCC_WIN32
+#define DCC_WHITE_TBL_BINS 521		/* should be prime */
+#else
+#define DCC_WHITE_TBL_BINS 8209		/* should be prime */
+#endif
+typedef u_int DCC_WHITE_FGS;
+typedef struct {
+    DCC_WHITE_MAGIC magic;		/* WHITE_MAGIC_* in ckwhite.c */
+    struct {
+	DCC_WHITE_INX entries;		/* # of entries in the file */
+	DCC_WHITE_FGS flags;
+#	 define	 DCC_WHITE_FG_PER_USER	    0x00000001
+#	 define	 DCC_WHITE_FG_HOSTNAMES	    0x00000002	/* have some */
+#	 define	 DCC_WHITE_FG_GREY_LOG_ON   0x00000004
+#	 define	 DCC_WHITE_FG_GREY_LOG_OFF  0x00000008
+#	 define	 DCC_WHITE_FG_GREY_ON	    0x00000010
+#	 define	 DCC_WHITE_FG_GREY_OFF	    0x00000020
+#	 define	 DCC_WHITE_FG_LOG_ALL	    0x00000040
+#	 define	 DCC_WHITE_FG_LOG_NORMAL    0x00000080
+#	 define	 DCC_WHITE_FG_DISCARD_OK    0x00000100
+#	 define	 DCC_WHITE_FG_NO_DISCARD    0x00000200
+#	 define	 DCC_WHITE_FG_DCC_ON	    0x00000400
+#	 define	 DCC_WHITE_FG_DCC_OFF	    0x00000800
+#	 define	 DCC_WHITE_FG_REP_ON	    0x00001000
+#	 define	 DCC_WHITE_FG_REP_OFF	    0x00002000
+#	 define	 DCC_WHITE_FG_MTA_FIRST	    0x00004000
+#	 define	 DCC_WHITE_FG_MTA_LAST	    0x00008000
+#	 define	 DCC_WHITE_FG_LOG_D	    0x00010000
+#	 define	 DCC_WHITE_FG_LOG_H	    0x00020000
+#	 define	 DCC_WHITE_FG_LOG_M	    0x00040000
+#	 define	 DCC_WHITE_FG_TRAP_ACC	    0x00080000	/* spam trap */
+#	 define	 DCC_WHITE_FG_TRAP_REJ	    0x00100000	/* spam trap */
+#	  define  DCC_WHITE_FG_TRAPS (DCC_WHITE_FG_TRAP_ACC		\
+				       | DCC_WHITE_FG_TRAP_REJ)
+#	 define	 DCC_WHITE_FG_DNSBL_ON(n)  (0x00200000*DNSBL_G2B(n))
+#	  define  DCC_WHITE_FG_DNSBL_ON_M   0x00e00000
+#	 define	 DCC_WHITE_FG_DNSBL_OFF(n) (0x01000000*DNSBL_G2B(n))
+#	  define  DCC_WHITE_FG_DNSBL_OFF_M  0x07000000
+	time_t	reparse;		/* re-parse after this */
+	time_t	broken;			/* serious broken until then */
+	time_t	ascii_mtime;		/* ASCII file mtime at last parsing */
+	struct {			/* included files */
+	    time_t	mtime;
+	    DCC_PATH	nm;
+	} white_incs[8];
+
+	DCC_CKSUM_THOLDS tholds_rej;
+
+	DCC_SUM	ck_sum;			/* checksum of following contents */
+	DCC_WHITE_CIDR cidr;
+    } hdr;
+    DCC_WHITE_INX bins[DCC_WHITE_TBL_BINS]; /* 1-based indeces or 0 for empty */
+    DCC_WHITE_ENTRY tbl[1];
+} DCC_WHITE_TBL;
+
+
+/* a ASCII whiteclnt file (including the files it includes) can be
+ *	OK
+ *	unreadable or otherwise badly broken
+ *	missing
+ *	    if permanent, the hash table should be removed, but
+ *	    it might be temporary as an editor changes it
+ * a hash table can be
+ *	OK
+ *	out of date compared to the ASCII file(s)
+ *	badly broken but not removable (e.g. bad directory permissions).
+ *
+ * Do not stat() the files iand so do not complain on every message by
+ *	using wf->next_stat_time
+ * Avoid generating a complaint on every error message using wf->broken
+ * Reparse ASCII files with errors using wf->reparse or when their mtimes
+ *	change, but not more often than we use stat() to check mtimes..
+ * Reparse the main ASCII file if it contains host names by noticing the
+ *	DCC_WHITE_FG_HOSTNAMES bit and that the mtime of the hash table is
+ *	more than DCC_WHITECLNT_RESOLVE seconds old
+ */
+#define DCC_WHITE_REPARSE_DELAY	(30*60)	/* look for new DNS values */
+#define DCC_WHITE_BROKEN_DELAY	(5*60)	/* do not complain more often */
+#define DCC_WHITE_STAT_DELAY	10	/* don't stat() more often */
+
+/* computed from DCC_WHITE_FGS */
+typedef u_int32_t FLTR_SWS;
+#define	FLTR_SW_SET		0x00000001
+#define FLTR_SW_NO_DISCARD	0x00000002  /* always reject spam */
+#define	FLTR_SW_DCC_OFF		0x00000004  /* no DCC check */
+#define	FLTR_SW_REP_ON		0x00000008  /* honor DCC reputation */
+#define	FLTR_SW_LOG_ALL		0x00000010
+#define	FLTR_SW_GREY_OFF	0x00000020  /* greylisting off */
+#define	FLTR_SW_GREY_LOG_OFF	0x00000040  /* log greylist embargos */
+#define FLTR_SW_LOG_D		0x00000080
+#define FLTR_SW_LOG_H		0x00000100
+#define FLTR_SW_LOG_M		0x00000200
+#define	FLTR_SW_MTA_FIRST	0x00000400  /* MTA IS/NOTSPAM checked first */
+#define	FLTR_SW_TRAP_ACC	0x00000800  /* accepting spam trap */
+#define	FLTR_SW_TRAP_REJ	0x00001000  /* rejecting spam trap */
+# define  FLTR_SW_TRAPS (FLTR_SW_TRAP_ACC | FLTR_SW_TRAP_REJ)
+#define	FLTR_SW_DNSBL(n)       (0x00002000*DNSBL_G2B(n))    /* check DNSBL */
+# define FLTR_SW_DNSBL_M	0x0000e000
+# define FLTR_SW_DNSBL_BITS(sws) (((sws)&FLTR_SW_DNSBL_M)		\
+				  / FLTR_SW_DNSBL(0))
+
+/* bits set to enable something */
+#define FLTR_SWS_SETTINGS_ON	(FLTR_SW_REP_ON				\
+				 | FLTR_SW_LOG_ALL			\
+				 | FLTR_SW_MTA_FIRST			\
+				 | FLTR_SW_DNSBL_M			\
+				 | FLTR_SW_TRAPS)
+/* bits set to disable something */
+#define FLTR_SWS_SETTINGS_OFF	(FLTR_SW_DCC_OFF			\
+				 | FLTR_SW_NO_DISCARD			\
+				 | FLTR_SW_TRAP_REJ			\
+				 | FLTR_SW_GREY_OFF			\
+				 | FLTR_SW_GREY_LOG_OFF)
+/* get bits of enabled FILTERS */
+#define FLTR_SWS_ON(sws)    (((sws) ^ FLTR_SW_DCC_OFF)			\
+			     & (FLTR_SW_DCC_OFF				\
+				| FLTR_SW_REP_ON			\
+				| FLTR_SW_DNSBL_M			\
+				| FLTR_SW_TRAP_REJ))
+
+
+/* everything there is to know about a currently active whitelist file */
+typedef struct {
+    DCC_WHITE_TBL *wtbl;
+    int		ht_fd;			/* hash table file */
+    struct stat ht_sb;
+    DCC_WHITE_FGS wtbl_flags;
+    u_int	wtbl_entries;		/* # of entries mapped window */
+    u_int	wtbl_size;		/* bytes in mapped window */
+    time_t	next_stat_time;
+    time_t	reparse;		/* when to re-parse file */
+    time_t	broken;			/* something very sick until then */
+#ifdef DCC_WIN32
+    HANDLE	ht_map;			/* WIN32 hash table map handle */
+#endif
+    u_int	ascii_nm_len;
+    DCC_PATH	ascii_nm;
+    DCC_PATH	ht_nm;
+    int		fno, lno;		/* currently being parsed */
+    u_char	wf_flags;
+#    define	 DCC_WF_PER_USER    0x01    /* hostnames not allowed */
+#    define	 DCC_WF_NOFILE	    0x02    /* no whiteclnt file */
+#    define	 DCC_WF_EITHER	    0x04    /* either global or per-user ok */
+#    define	 DCC_WF_RO	    0x08    /* read only access */
+#    define	 DCC_WF_WLIST	    0x10    /* wlist command */
+#    define	 DCC_WF_WLIST_RO    0x20    /* read-only wlist command */
+#    define	 DCC_WF_WLIST_RW    0x40    /* read/write wlist command */
+    u_char	closed;
+    u_char	need_reopen;		/* lock-safe change flag */
+} DCC_WF;
+
+extern DCC_WF cmn_wf, cmn_tmp_wf;
+
+typedef enum {				/* greylist result */
+    ASK_GREY_FAIL,			/* greylist server or other failure */
+    ASK_GREY_OFF,			/* client whitelist or blacklist */
+    ASK_GREY_EMBARGO,
+    ASK_GREY_EMBARGO_END,		/* first time server says ok */
+    ASK_GREY_PASS,			/* greylist server says ok */
+    ASK_GREY_WHITE,			/* greylist server says whitelisted */
+    ASK_GREY_SPAM			/* reported as spam to server */
+} ASK_GREY_RESULT;
+
+extern u_char grey_on;
+extern u_char grey_query_only;
+
+extern u_int dcc_ck_qp_decode(DCC_GOT_CKS *, const char **, u_int *,
+			      char *, u_int);
+extern u_int dcc_ck_b64_decode(DCC_GOT_CKS *, const char **, u_int *,
+			       char *, u_int);
+
+extern int dcc_ck_url(DCC_URL_SKIP *, char, char **);
+#define DCC_CK_URL_MASK	    0xff
+#define DCC_CK_URL_SHIFT    8
+typedef enum {
+    DCC_CK_URL_CHAR,			/* character in URL after host name */
+    DCC_CK_URL_CK_LEN,			/* 2nd slash */
+    DCC_CK_URL_HOST,			/* character in URL host name */
+    DCC_CK_URL_DOT,			/* dot in host name */
+    DCC_CK_URL_HOST_END,		/* end of host name */
+    DCC_CK_URL_HOST_RESET,		/* wasn't in host name after all */
+    DCC_CK_URL_SKIP			/* shipping URL or not in URL */
+} DCC_CK_URL;
+
+extern void dcc_ck_fuz1_init(DCC_GOT_CKS *);
+extern void dcc_ck_fuz1(DCC_GOT_CKS *, const char *, u_int);
+extern void dcc_ck_fuz1_fin(DCC_GOT_CKS *);
+
+extern void dcc_ck_fuz2_init(DCC_GOT_CKS *);
+extern void dcc_ck_fuz2(DCC_GOT_CKS *, const char *, u_int);
+extern void dcc_ck_fuz2_fin(DCC_GOT_CKS *);
+
+
+extern void dcc_wf_init(DCC_WF *, u_int);
+extern void dcc_wf_lock(DCC_WF *);
+extern void dcc_wf_unlock(DCC_WF *);
+
+extern void dcc_str2ck(DCC_SUM, const char *, u_int, const char *);
+extern u_char dcc_get_cks(DCC_GOT_CKS *, DCC_CK_TYPES, const char *, u_char);
+extern u_char dcc_ck_get_sub(DCC_GOT_CKS *, const char *, const char *);
+extern u_char dcc_add_sub_hdr(DCC_EMSG, const char *);
+extern void dcc_ck_ipv6(DCC_SUM, const struct in6_addr *);
+extern void dcc_get_ipv6_ck(DCC_GOT_CKS *, const struct in6_addr *);
+extern void dcc_unget_ip_ck(DCC_GOT_CKS *);
+extern u_char dcc_get_str_ip_ck(DCC_GOT_CKS *, const char *);
+extern const char *parse_received(const char *, DCC_GOT_CKS *,
+				  char *, int, char *, int, char *, int);
+extern u_char parse_return_path(const char *,  char *, int);
+extern u_char parse_unix_from(const char *, char *, int);
+extern u_char parse_mail_host(const char *, char *, int);
+extern void dcc_print_cks(LOG_WRITE_FNC, void *, u_char, DCC_TGTS,
+			  const DCC_GOT_CKS *, DCC_CKS_WTGTS, u_char);
+#define PRINT_CK_TYPE_LEN	25
+#define PRINT_CK_SUM_LEN	35
+#define PRINT_CK_PAT_CK		"%25s: %-35s"
+#define PRINT_CK_PAT_LIM_CK	"%25.*s%c %-35.*s"
+#define PRINT_CK_PAT_SRVR	" %7s"
+#define PRINT_CK_PAT_SRVR_LEN   8
+#define PRINT_CK_PAT_WLIST	" %5s"
+#define PRINT_CK_PAT_WLIST_LEN	6
+#define PRINT_CK_PAT_THOLD	PRINT_CK_PAT_WLIST
+
+extern void dcc_cks_init(DCC_GOT_CKS *);
+extern void dcc_ck_mime_hdr(DCC_GOT_CKS *, const char *, const char *);
+extern u_char parse_mime_hdr(DCC_GOT_CKS *, const char *, u_int, u_char);
+extern void dcc_ck_body(DCC_GOT_CKS *, const void *, u_int);
+extern void dcc_cks_fin(DCC_GOT_CKS *);
+
+extern u_char dcc_get_white(DCC_EMSG, DCC_WHITE_INX);
+
+typedef int (*DCC_PARSED_CK_FNC)(DCC_EMSG, DCC_WF *,
+				DCC_CK_TYPES,	/* type of checksum */
+				DCC_SUM,    /* computed checksum */
+				DCC_TGTS);  /* "OK2" etc */
+typedef int (*DCC_PARSED_CK_CIDR_FNC)(DCC_EMSG, DCC_WF *,
+				      int,
+				      const struct in6_addr *,
+				      const struct in6_addr *,
+				      DCC_TGTS);
+extern int dcc_parse_ck(DCC_EMSG, DCC_WF *wf,
+			const char *, DCC_CK_TYPES, const char *, DCC_TGTS,
+			DCC_PARSED_CK_FNC, DCC_PARSED_CK_CIDR_FNC);
+extern int dcc_parse_hex_ck(DCC_EMSG, DCC_WF *wf,
+			    const char *, DCC_CK_TYPES, const char *, DCC_TGTS,
+			    DCC_PARSED_CK_FNC);
+extern const char *wf_fnm(const DCC_WF *, int);
+extern const char *wf_fnm_lno(DCC_FNM_LNO_BUF *, const DCC_WF *);
+extern DCC_TGTS dcc_str2thold(DCC_CK_TYPES, const char *);
+extern int dcc_parse_whitefile(DCC_EMSG, DCC_WF *, int,
+			       DCC_PARSED_CK_FNC, DCC_PARSED_CK_CIDR_FNC);
+
+typedef enum {
+    DCC_WHITE_USE_DCC,
+    DCC_WHITE_LISTED,
+    DCC_WHITE_UNLISTED,
+    DCC_WHITE_BLACK
+} DCC_WHITE_LISTING;
+
+typedef enum {
+    DCC_WHITE_OK,
+    DCC_WHITE_NOFILE,			/* no ASCII file */
+    DCC_WHITE_CONTINUE,			/* modest error or bad host name */
+    DCC_WHITE_COMPLAIN,			/* bad hash table */
+    DCC_WHITE_SILENT			/* no more complaints */
+} DCC_WHITE_RESULT;
+#define DCC_WHITE_RESULT_FAILURE DCC_WHITE_UNLISTED
+
+u_char dcc_new_white_nm(DCC_EMSG, DCC_WF *, const char *);
+extern DCC_WHITE_RESULT dcc_rdy_white(DCC_EMSG, DCC_WF *, DCC_WF *);
+extern DCC_WHITE_RESULT dcc_white_sum(DCC_EMSG, DCC_WF *,
+				      DCC_CK_TYPES, const DCC_SUM,
+				      DCC_TGTS *, DCC_WHITE_LISTING *);
+extern u_char dcc_white_mx(DCC_EMSG, DCC_TGTS *, const DCC_GOT_CKS *);
+extern DCC_WHITE_RESULT dcc_white_cks(DCC_EMSG, DCC_WF *, DCC_GOT_CKS *,
+				      DCC_CKS_WTGTS, DCC_WHITE_LISTING *);
+
+
+typedef u_int32_t ASK_ST;
+#define ASK_ST_INVALID_MSG	0x00000001  /* incomplete SMTP transaction */
+#define	ASK_ST_QUERY		0x00000002  /* ask but do not report */
+#define	ASK_ST_QUERY_GREY	0x00000004
+#define	ASK_ST_SRVR_OK2		0x00000008  /* have honored DCC_TGTS_OK2 */
+#define	ASK_ST_SRVR_NOTSPAM	0x00000010  /* not spam by DCC server */
+#define	ASK_ST_SRVR_ISSPAM	0x00000020  /* spam by DCC server & threshold */
+#define ASK_ST_REP_ISSPAM	0x00000040  /* spam by reputation & threshold */
+#define	ASK_ST_MTA_NOTSPAM	0x00000080  /* MTA says it is not spam */
+#define	ASK_ST_MTA_ISSPAM	0x00000100  /* MTA says it is spam */
+#define	ASK_ST_WLIST_NOTSPAM	0x00000200  /* locally whitelisted message */
+#define	ASK_ST_WLIST_ISSPAM	0x00000400  /* locally blacklisted message */
+#define	ASK_ST_CLNT_ISSPAM	0x00000800  /* report to DCC server as spam */
+#define	ASK_ST_GREY_EMBARGO	0x00001000  /* embargo this message */
+#define	ASK_ST_GREY_LOGIT	0x00002000  /* greylist logging indicated */
+#define	ASK_ST_LOGIT		0x00004000  /* log message for all recipients */
+#define	ASK_ST_DNSBL_HIT(n)    (0x00008000*DNSBL_G2B(n))    /* DNSBL hit */
+# define ASK_ST_DNSBL_HIT_M	0x00038000
+# define ASK_ST_DNSBL_HIT_BITS(st) (((st)&ASK_ST_DNSBL_HIT_M)		    \
+				    / ASK_ST_DNSBL_HIT(0))
+#define	ASK_ST_DNSBL_TIMEO(n)  (0x00040000*DNSBL_G2B(n))    /* DNSBL timeout */
+# define ASK_ST_DNSBL_TIMEO_M	0x001c0000
+# define ASK_ST_DNSBL_TIMEO_BITS(st) (((st)&ASK_ST_DNSBL_TIMEO_M)	    \
+				      / ASK_ST_DNSBL_TIMEO(0))
+#define	ASK_ST_DNSBL_TFAIL(n)  (0x00200000*DNSBL_G2B(n))
+# define ASK_ST_DNSBL_TFAIL_M	0x00e00000
+# define ASK_ST_DNSBL_TFAIL_BITS(st) (((st)&ASK_ST_DNSBL_TFAIL_M)	    \
+				      / ASK_ST_DNSBL_TFAIL(0))
+
+
+extern u_char dcc_ck_grey_answer(DCC_EMSG, const DCC_OP_RESP *);
+extern int ask_dcc(DCC_EMSG, DCC_CLNT_CTXT *, u_char,
+		   DCC_HEADER_BUF *, DCC_GOT_CKS *, ASK_ST *, u_char,
+		   DCC_TGTS);
+extern u_char unthr_ask_white(DCC_EMSG, ASK_ST *, FLTR_SWS *, const char *,
+			      DCC_GOT_CKS *, DCC_CKS_WTGTS);
+extern u_char unthr_ask_dcc(DCC_EMSG, DCC_CLNT_CTXT*, DCC_HEADER_BUF*, ASK_ST *,
+			    DCC_GOT_CKS *, u_char, DCC_TGTS);
+extern void dcc_clear_tholds(void);
+extern u_char dcc_merge_tholds(DCC_CKSUM_THOLDS,
+			       const DCC_CKSUM_THOLDS, const DCC_WHITE_TBL *);
+extern void dcc_parse_honor(const char *);
+extern u_char dcc_parse_tholds(const char *, const char *);
+extern void dcc_honor_log_cnts(ASK_ST *, const DCC_GOT_CKS *, DCC_TGTS);
+extern FLTR_SWS wf2sws(FLTR_SWS, const DCC_WF *);
+extern void log_ask_st(LOG_WRITE_FNC, void *, ASK_ST, FLTR_SWS, u_char,
+		       const DCC_HEADER_BUF *);
+extern u_char dcc_parse_client_grey(const char *);
+extern ASK_GREY_RESULT ask_grey(DCC_EMSG, DCC_CLNT_CTXT *, DCC_OPS,
+				DCC_SUM, DCC_SUM,
+				const DCC_GOT_CKS *, const DCC_SUM,
+				DCC_TGTS *, DCC_TGTS *, DCC_TGTS *);
+
+extern u_char dcc_parse_dnsbl(DCC_EMSG, const char *, const char *, u_char);
+extern const REPLY_TPLT *dnsbl_parse_reply(const char *);
+extern void helper_save_arg(const char *, const char *);
+extern void helper_init(int);
+extern void dcc_dnsbl_init(DCC_GOT_CKS *,
+			   DCC_CLNT_CTXT *, void *, const char *);
+extern void url_dnsbl(DNSBL_WORK *);
+extern void dcc_mail_host_dnsbl(DNSBL_WORK *, const char *);
+extern void dcc_client_dnsbl(DNSBL_WORK *, const struct in6_addr *,
+			     const char *);
+extern void dcc_dnsbl_result(ASK_ST *, DNSBL_WORK *);
+extern int PATTRIB(3,4) thr_log_print(void *, u_char, const char *, ...);
+extern int PATTRIB(2,3) thr_error_msg(void *, const char *, ...);
+extern void PATTRIB(2,3) thr_trace_msg(void *, const char *, ...);
+
+#endif /* DCC_CK_H */
diff -r 000000000000 -r c7f6b056b673 include/dcc_clnt.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dcc_clnt.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,545 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * internal client definitions
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.120 $Revision$
+ */
+
+#ifndef DCC_CLNT_H
+#define DCC_CLNT_H
+
+#include "dcc_defs.h"
+
+
+#define	DCC_DCCD_DELAY	    (100*1000)	/* typical server delay */
+
+/* The minimum assumed RTT should be long enough for the DCC server to do
+ * some disk operations even if the server and network have usually
+ * been faster. */
+#define DCC_MIN_RTT	    (DCC_DCCD_DELAY + 150*1000)
+
+#define DCC_MAX_XMITS	    4
+
+/* worst client-->server-->client RTT
+ * The total delay for N exponentially increasing delays starting with  X
+ * is about X*(2**N).  With the delay limited to Y, it is Y*N.
+ * The RTT measuring done with NOPs uses retransmission delays based on
+ * DCC_MIN_RTT, and so will not find a server with a worse delay than
+ * DCC_MAX_RTT */
+#define DCC_MAX_RTT	    (DCC_MIN_RTT<<DCC_MAX_XMITS)
+#define DCC_MAX_RTT_SECS    (DCC_MAX_RTT/DCC_US)
+
+#define DCC_RTT_ADJ_MAX	    (DCC_MAX_RTT/2) /* limit manual RTT bias */
+#define DCC_RTT_VERS_ADJ    DCC_RTT_ADJ_MAX /* penalize strange versions */
+#define	DCC_RTT_BAD	    (DCC_MAX_RTT*2+1)
+
+/* If DCC_MAX_XMITS and the retransmission delay limited to DCC_MAX_RTT_SECS,
+ * the the worst case delay is DCC_MAX_XMITS*DCC_MAX_RTT_SECS=16 seconds */
+#define DCC_MAX_DELAY	    (DCC_MAX_XMITS*DCC_MAX_RTT)
+#define DCC_MAX_DELAY_SECS  (DCC_MAX_DELAY/DCC_US)
+
+
+/* No client's retransmission can be delayed by more than this starting
+ * from the initial intransmission and including network delays for the
+ * last retransmission
+ * This controls how long a DCC server must remember old requests
+ * to recognize retransmissions.  */
+#define DCC_MAX_RETRANS_DELAY_SECS  (DCC_MAX_DELAY_SECS*2)
+
+
+#ifndef DCC_MAP_RESOLVE
+#define	DCC_MAP_RESOLVE	    (2*60*60)	/* DNS resolve server host names */
+#endif
+#ifndef DCC_WHITECLNT_RESOLVE
+#define	DCC_WHITECLNT_RESOLVE (2*60*60)	/* DNS resolve whitelist hosts */
+#endif
+
+#define DCC_MAX_SRVR_NMS    8		/* server hostnames */
+#define DCC_MAX_SRVR_ADDRS  12		/* server IP addresses */
+
+extern u_char dcc_clnt_debug;
+extern int dcc_debug_ttl;
+
+extern char dcc_clnt_hostname[MAXHOSTNAMELEN];
+
+/* information kept by a client about a server */
+typedef struct {
+    int		rtt_adj;
+    DCC_CLNT_ID	clnt_id;		/* our client-ID sent to the server */
+    u_int16_t	port;			/* network byte order port # */
+    u_char	defined;		/* name resolves into an IP address */
+    char	hostname[DCC_MAXDOMAINLEN]; /* null terminated */
+    u_char	pad;
+    DCC_PASSWD	passwd;			/* null filled but not terminated */
+} DCC_SRVR_NM;
+
+/* the port number must be installed in this before use */
+#define DCC_SRVR_NM_DEF_HOST "127.0.0.1"
+#define DCC_SRVR_NM_DEF {0, DCC_ID_ANON, 0, 0, {DCC_SRVR_NM_DEF_HOST}, 0, {""}}
+
+typedef u_int16_t NAM_INX;
+typedef struct {			/* a single server address */
+    DCC_IP	ip;
+    time_t	rtt_updated;		/* when RTT last updated */
+    int		rtt;			/* round trip time in microseconds */
+    int		srvr_wait;		/* microseconds server penalizes us */
+    u_int32_t	resp_mem;		/* 1 bit per recent xmit */
+#    define	 DCC_TOTAL_XMITS_MAX (sizeof(u_int32_t)*8)
+    NAM_INX	nam_inx;		/* DCC_NO_NM or DCC_SRVR_NM.nms index */
+#    define	 NO_NAM		    ((NAM_INX)(-1))
+#    define	 GOOD_NAM(n)	    ((n) < DCC_MAX_SRVR_NMS)
+    DCC_SRVR_ID	srvr_id;		/* the server's claimed ID */
+    u_char	total_xmits;
+    u_char	total_resps;
+    u_char	srvr_pkt_vers;
+    u_char	flags;
+#    define	 DCC_SRVR_ADDR_MHOME 0x01   /* do not use connect */
+    DCC_BRAND	brand;			/* the server's announced brand name */
+} DCC_SRVR_ADDR;
+
+typedef u_char SRVR_INX;
+typedef struct {
+    time_t	resolve;		/* when names need to be resolved */
+    time_t	measure;		/* when RTT measurements need */
+    int		gen;
+    int		base_rtt;		/* RTT to the chosen server */
+    int		thold_rtt;		/* threshold for switching */
+    int		avg_thold_rtt;		/* long term threshold */
+    int		num_srvrs;		/* addresses in .addrs */
+    time_t	fail_time;		/* DCC broken until then */
+    u_char	fail_exp;		/* backoff */
+#    define	 DCC_INIT_FAIL_SECS  (DCC_MAX_DELAY_SECS*2)
+#    define	 DCC_MAX_FAIL_EXP    6
+#    define	 DCC_MAX_FAIL_SECS   (DCC_INIT_FAIL_SECS<<DCC_MAX_FAIL_EXP)
+    SRVR_INX	srvr_inx;		/* NO_SRVR or index in .addrs */
+#    define	 NO_SRVR	((SRVR_INX)(-1))
+#    define	 GOOD_SRVR(c,s)	((s) < (c)->num_srvrs)
+#    define	 HAVE_SRVR(c)   GOOD_SRVR(c,c->srvr_inx)
+    DCC_SRVR_NM	nms[DCC_MAX_SRVR_NMS];
+    DCC_SRVR_ADDR addrs[DCC_MAX_SRVR_ADDRS];
+} DCC_SRVR_CLASS;
+
+/* Information about DCC servers shared among clients on a host
+ *	This is the shape of the mmap()'ed file. */
+typedef struct {
+#    define	 DCC_MAP_INFO_VERSION "DCC client mapped data version #11"
+    char	version[36];
+    u_int	residue;		/* random # for picking addresses */
+    u_char	flags;
+#    define	 DCC_INFO_FG_IPV6   0x01
+#ifdef NO_IPV6
+#     define	   DCC_INFO_IPV6()  0
+#else
+#     define	   DCC_INFO_IPV6()  (dcc_clnt_info			\
+				     && (dcc_clnt_info->flags		\
+					 & DCC_INFO_FG_IPV6) != 0)
+#endif
+#    define	 DCC_INFO_FG_SOCKS  0x02
+#    define	 DCC_INFO_FG_TMP    0x04
+    DCC_HDR     proto_hdr;		/* prototype DCC request header */
+    DCC_SRVR_CLASS dcc;			/* normal DCC services */
+    DCC_SRVR_CLASS grey;		/* grey listing */
+    DCC_IP	src;			/* send from this interface */
+    time_t	dccproc_last;
+    time_t	dccproc_dccifd_try;
+    int		dccproc_c;
+#    define	 DCCPROC_TRY_DCCIFD    (60*60)	/* start dccifd once/hour */
+#    define	 DCCPROC_MAX_CREDITS	500	/* at least 500 */
+#    define	 DCCPROC_COST		 10	/*  and at least 0.1/second */
+} DCC_CLNT_INFO;
+
+
+#define	DCC_MAP_INFO_VERSION_10 "DCC client mapped data version #10"
+#ifdef DCC_MAP_INFO_VERSION_10
+typedef struct {
+    int		rtt_adj;
+    DCC_CLNT_ID	clnt_id;
+    u_int16_t	port;
+    u_char	defined;
+    char	hostname[MAXHOSTNAMELEN];
+    u_char	pad;
+    DCC_PASSWD	passwd;
+} DCC_V10_SRVR_NM;
+typedef struct {
+    time_t	resolve;
+    time_t	measure;
+    int		gen;
+    int		base_rtt;
+    int		thold_rtt;
+    int		avg_thold_rtt;
+    int		num_addrs;
+    time_t	fail_time;
+    u_char	fail_exp;
+    u_char	act_inx;
+    DCC_V10_SRVR_NM nms[DCC_MAX_SRVR_NMS];
+    DCC_SRVR_ADDR addrs[DCC_MAX_SRVR_ADDRS];
+} DCC_V10_SRVR_CLASS;
+typedef struct {
+    char	version[36];
+    u_int	residue;
+    u_char	flags;
+    DCC_HDR     proto_hdr;
+    DCC_V10_SRVR_CLASS dcc;
+    DCC_V10_SRVR_CLASS grey;
+    DCC_IP	src;
+    time_t	dccproc_last;
+    time_t	dccproc_dccifd_try;
+    int		dccproc_c;
+} DCC_V10_CLNT_INFO;
+#endif /* DCC_MAP_INFO_VERSION_10 */
+
+#define	DCC_MAP_INFO_VERSION_9 "DCC client mapped data version #9"
+#ifdef DCC_MAP_INFO_VERSION_9
+typedef struct {
+    char	version[36];
+    u_int	residue;
+    u_char	flags;
+    DCC_HDR     proto_hdr;
+    DCC_V10_SRVR_CLASS dcc;
+    DCC_V10_SRVR_CLASS grey;
+    DCC_IP	src;
+} DCC_V9_CLNT_INFO;
+#endif /* DCC_MAP_INFO_VERSION_9 */
+
+#define	DCC_MAP_INFO_VERSION_8 "DCC client mapped data version #8"
+#ifdef DCC_MAP_INFO_VERSION_8
+typedef struct {
+    char	version[36];
+    u_int	residue;
+    u_char	flags;
+    DCC_HDR     proto_hdr;
+    DCC_V10_SRVR_CLASS dcc;
+    DCC_V10_SRVR_CLASS grey;
+} DCC_V8_CLNT_INFO;
+#endif /* DCC_MAP_INFO_VERSION_8 */
+
+#ifndef DCC_WIN32
+#define	DCC_MAP_INFO_VERSION_7 "DCC client mapped data version #7"
+#endif
+#ifdef DCC_MAP_INFO_VERSION_7
+typedef struct {
+    DCC_SOCKU	su;
+    time_t	rtt_updated;
+    int		rtt;
+    int		srvr_wait;
+    u_int32_t	resp_mem;
+    u_int16_t	nm_inx;
+    DCC_SRVR_ID	srvr_id;
+    u_char	total_xmits;
+    u_char	total_resps;
+    u_char	srvr_pkt_vers;
+    u_char	unused[1];
+    DCC_BRAND	brand;
+} DCC_V7_IPV6_SRVR_ADDR;
+typedef struct {
+    union {
+	struct sockaddr sa;
+	struct sockaddr_in ipv4;
+	struct dcc_sockaddr_in6 ipv6;
+    } su;
+    time_t	rtt_updated;
+    int		rtt;
+    int		srvr_wait;
+    u_int32_t	resp_mem;
+    u_int16_t	nm_inx;
+    DCC_SRVR_ID	srvr_id;
+    u_char	total_xmits;
+    u_char	total_resps;
+    u_char	srvr_pkt_vers;
+    u_char	unused[1];
+    DCC_BRAND	brand;
+} DCC_V7_NOIPV6_SRVR_ADDR;
+
+typedef struct {
+    time_t	resolve;
+    time_t	measure;
+    int		gen;
+    int		base_rtt;
+    int		thold_rtt;
+    int		avg_thold_rtt;
+    int		num_addrs;
+    time_t	fail_time;
+    u_char	fail_exp;
+    u_char	act_inx;
+    DCC_V10_SRVR_NM	nms[8];
+    DCC_V7_IPV6_SRVR_ADDR addrs[12];
+} DCC_V7_IPV6_SRVR_CLASS;
+typedef struct {
+    time_t	resolve;
+    time_t	measure;
+    int		gen;
+    int		base_rtt;
+    int		thold_rtt;
+    int		avg_thold_rtt;
+    int		num_addrs;
+    time_t	fail_time;
+    u_char	fail_exp;
+    u_char	act_inx;
+    DCC_V10_SRVR_NM nms[8];
+    DCC_V7_NOIPV6_SRVR_ADDR addrs[12];
+} DCC_V7_NOIPV6_SRVR_CLASS;
+
+typedef struct {
+    char	version[36];
+    u_int	residue;
+    u_char	flags;
+    DCC_HDR     proto_hdr;
+    DCC_V7_IPV6_SRVR_CLASS dcc;
+    DCC_V7_IPV6_SRVR_CLASS grey;
+} DCC_V7_IPV6_CLNT_INFO;
+typedef struct {
+    char	version[36];
+    u_int	residue;
+    u_char	flags;
+    DCC_HDR     proto_hdr;
+    DCC_V7_NOIPV6_SRVR_CLASS dcc;
+    DCC_V7_NOIPV6_SRVR_CLASS grey;
+} DCC_V7_NOIPV6_CLNT_INFO;
+#endif /* DCC_MAP_INFO_VERSION_7 */
+
+#ifndef DCC_WIN32
+#define	DCC_MAP_INFO_VERSION_6 "DCC client mapped data version #6"
+#endif
+#ifdef DCC_MAP_INFO_VERSION_6
+typedef struct {
+    char	version[36];
+    time_t	resolve;
+    time_t	measure;
+    time_t	srvr_fail_time;
+    int		base_rtt;
+    int		thold_rtt;
+    int		avg_thold_rtt;
+    u_char	unused[8];
+    u_char	srvr_fail_exp;
+    u_char	act_inx;
+    u_char	total_addrs;
+    u_char	flags;
+    DCC_HDR     proto_hdr;
+    DCC_V10_SRVR_NM nms[8];
+} DCC_V6_CLNT_INFO;
+
+typedef struct {
+    DCC_CLNT_ID	clnt_id;
+    u_int16_t	port;
+    char	hostname[MAXHOSTNAMELEN+1];
+    DCC_PASSWD	passwd;
+    signed char	rtt_adj;
+} DCC_V5_SRVR_NM;
+
+typedef struct {
+#    define	 DCC_MAP_INFO_VERSION_5 "DCC client mapped data version #5"
+    char	version[36];
+    time_t	resolve;
+    int		min_delay;
+    u_int16_t	act_inx;
+    u_char	unused[13];
+    u_char	total_addrs;
+    u_char	working_addrs;
+    u_char	flags;
+    DCC_HDR     proto_hdr;
+    DCC_V5_SRVR_NM nms[8];
+} DCC_V5_CLNT_INFO;
+#endif /* DCC_MAP_INFO_VERSION_6 */
+
+#define DCC_IS_GREY(class)  ((class) == &dcc_clnt_info->grey)
+#define DCC_IS_GREY_STR(c)  (DCC_IS_GREY(c) ? "greylist" : "DCC")
+#define	DCC_GREY2PORT(mode) htons((mode)? DCC_GREY_PORT : DCC_SRVR_PORT)
+#define DCC_CLASS2PORT(c)   DCC_GREY2PORT(DCC_IS_GREY(c))
+#define DCC_GREY2CLASS(g)   ((g) ? &dcc_clnt_info->grey : &dcc_clnt_info->dcc)
+
+extern DCC_CLNT_INFO *dcc_clnt_info;    /* memory mapped shared data */
+extern u_char dcc_all_srvrs;		/* try to contact all servers */
+extern DCC_PATH dcc_info_nm;
+
+
+typedef struct {			/* record of 1 transmission */
+    DCC_OP_NUMS	op_nums;
+    time_t	sent_us;		/* microseconds since operation start */
+    DCC_OPS	op;
+    DCC_CLNT_ID	id;
+    int		addrs_gen;
+    DCC_SOCKU	su;
+    int		addr_inx;
+    DCC_PASSWD  passwd;
+} DCC_XLOG_ENTRY;
+
+typedef struct {
+    u_char	outstanding;		/* unheard responses */
+    u_char	working_addrs;
+    struct {
+	u_char	    xmits;
+	u_char	    resps;
+    } cur[DCC_MAX_SRVR_ADDRS];
+    DCC_XLOG_ENTRY *base;		/* start of array */
+    DCC_XLOG_ENTRY *next;		/* next entry to use */
+    DCC_XLOG_ENTRY *last;		/* last entry in array */
+} DCC_XLOG;
+
+/* invalid timer */
+#define DCC_OP_NUMS_NULL ((u_int32_t)-1)
+
+
+typedef struct dcc_clnt_ctxt {
+    struct dcc_clnt_ctxt *fwd;
+    struct timeval now;			/* current time */
+    struct timeval start;		/* when operation started */
+    int		now_us;			/* usecs since start of operation */
+    time_t	bind_time;		/* when to retry binding socket */
+#    define	 DCC_CTXT_REBIND_SECS 3600  /* check local interface hourly */
+    SOCKET	soc;
+    DCC_SOCKU	bind_su;		/* socket bound to this address */
+    DCC_SOCKU	conn_su;		/* socket connected to this address */
+    DCC_XLOG	xlog;
+    DCC_XLOG_ENTRY xlog_entries[DCC_MAX_SRVR_ADDRS*DCC_MAX_XMITS];
+    u_char	flags;
+#    define	 DCC_CTXT_USING_IPV4	0x01	/* socket is opened for IPv4 */
+#    define	 DCC_CTXT_SRCBAD	0x02	/* bind() failed */
+} DCC_CLNT_CTXT;
+
+#define DCC_INFO_USE_IPV4  "IPv6 off"
+#define DCC_INFO_USE_IPV6  "IPv6 on"
+#define DCC_INFO_USE_SOCKS  "use SOCKS"
+#define DCC_INFO_USE_SRC    "src="
+#define DCC_INFO_USE_SRCBAD "but unused"
+
+
+/* many POSIX thread implementations have unexpected side effects on
+ * ordinary system calls, so allow the calling application
+ * to not use threads by having both threaded and unthreaded
+ * DCC client interfaces */
+
+extern void dcc_ctxts_lock(void);
+extern void dcc_ctxts_unlock(void);
+extern void dcc_syslog_lock(void);
+extern void dcc_syslog_unlock(void);
+extern u_char dcc_clnt_wake_resolve(void);
+extern void dcc_clnt_stop_resolve(void);
+extern void dcc_clnt_unthread_init(void);
+extern void dcc_clnt_thread_init(void);
+extern void lock_work(void);
+extern void unlock_work(void);
+extern void lock_wf(void);
+extern void unlock_wf(void);
+#ifdef DCC_DEBUG_CLNT_LOCK
+extern void assert_ctxts_locked(void);
+extern void assert_ctxts_unlocked(void);
+extern void assert_info_locked(void);
+extern void assert_info_unlocked(void);
+extern void assert_cwf_locked(void);
+#else
+#define assert_ctxts_locked()
+#define assert_ctxts_unlocked()
+#define assert_info_locked()
+#define assert_info_unlocked()
+#define assert_cwf_locked()
+#endif /* DCC_DEBUG_CLNT_LOCK */
+extern u_char helper_lock_init(void);
+extern void helper_lock(void);
+extern void helper_unlock(void);
+
+
+extern const char *dcc_ap2str(const DCC_SRVR_ADDR *);
+extern int dcc_ap2str_opt(char *, int,
+			  const DCC_SRVR_CLASS *, u_char inx, char);
+
+extern FILE *dcc_open_srvr_nm(DCC_EMSG, const char *);
+extern const char *dcc_parse_nm_port(DCC_EMSG, const char *, u_int,
+				     char *, u_int, u_int16_t *, char *, u_int,
+				     const char *, int);
+extern int dcc_parse_srvr_nm(DCC_EMSG, DCC_SRVR_NM *, u_char *,
+			     const char *, const char *, int);
+extern const char *dcc_srvr_nm(u_char);
+extern DCC_CLNT_CTXT *dcc_alloc_ctxt(void);
+extern void dcc_rel_ctxt(DCC_CLNT_CTXT *);
+extern u_char dcc_info_unlock(DCC_EMSG);
+extern u_char dcc_info_lock(DCC_EMSG);
+extern void dcc_force_measure_rtt(DCC_SRVR_CLASS *);
+extern u_char dcc_create_map(DCC_EMSG, const DCC_PATH, int *,
+			     const DCC_SRVR_NM *, int,
+			     const DCC_SRVR_NM *, int,
+			     const DCC_IP *, u_char);
+extern u_char dcc_unmap_close_info(DCC_EMSG);
+extern u_char dcc_map_info(DCC_EMSG, const char *, int);
+extern u_char dcc_map_lock_info(DCC_EMSG, const char *, int);
+extern u_char dcc_map_tmp_info(DCC_EMSG, const DCC_SRVR_NM *,
+			       const DCC_IP *, u_char);
+typedef u_char	DCC_CLNT_FGS;
+#define DCC_CLNT_FG_NONE	    0x00
+#define DCC_CLNT_FG_GREY	    0x01
+#define DCC_CLNT_FG_BAD_SRVR_OK	    0x02
+#define DCC_CLNT_FG_NO_PICK_SRVR    0x04
+#define DCC_CLNT_FG_NO_FAIL	    0x08
+#define DCC_CLNT_FG_RETRY	    0x10    /* retrying with different server */
+#define DCC_CLNT_FG_SLOW	    0x20    /* slow timeouts */
+#define DCC_CLNT_FG_RETRANS	    0x40    /* use old op_nums.r */
+extern u_char dcc_clnt_rdy(DCC_EMSG, DCC_CLNT_CTXT *, DCC_CLNT_FGS);
+extern DCC_CLNT_CTXT *dcc_tmp_clnt_init(DCC_EMSG, DCC_CLNT_CTXT *,
+					const DCC_SRVR_NM *,
+					const DCC_IP *, DCC_CLNT_FGS, u_char);
+extern DCC_CLNT_CTXT *dcc_clnt_start(DCC_EMSG, DCC_CLNT_CTXT *,
+				     const char *, DCC_CLNT_FGS);
+extern DCC_CLNT_CTXT *dcc_clnt_start_fin(DCC_EMSG, DCC_CLNT_CTXT *);
+extern DCC_CLNT_CTXT *dcc_clnt_init(DCC_EMSG, DCC_CLNT_CTXT *,
+				    const char *, DCC_CLNT_FGS);
+extern u_char dcc_clnt_connect(DCC_EMSG, DCC_CLNT_CTXT *, const DCC_SOCKU *);
+extern void dcc_clnt_soc_close(DCC_CLNT_CTXT *);
+extern u_char dcc_clnt_soc_reopen(DCC_EMSG, DCC_CLNT_CTXT *);
+extern int dcc_select_poll(DCC_EMSG, SOCKET, u_char, int);
+extern u_char dcc_clnt_op(DCC_EMSG, DCC_CLNT_CTXT *, DCC_CLNT_FGS,
+			  const SRVR_INX *, DCC_SRVR_ID *, DCC_SOCKU *,
+			  DCC_HDR *, int, DCC_OPS, DCC_OP_RESP *, int);
+
+extern DCC_OPS dcc_aop(DCC_EMSG, DCC_CLNT_CTXT *,
+		       DCC_CLNT_FGS, SRVR_INX, time_t,
+		       DCC_AOPS, u_int32_t, u_char, u_char, u_char,
+		       u_char *, u_int, DCC_OP_RESP *, DCC_SOCKU *);
+extern u_char dcc_aop_persist(DCC_EMSG, DCC_CLNT_CTXT *, DCC_CLNT_FGS, u_char,
+			      DCC_AOPS, u_int32_t, int, DCC_OP_RESP *);
+
+extern void dcc_print_info(const char *, const DCC_CLNT_INFO *,
+			   u_char, u_char, u_char, u_char, u_char);
+
+extern int get_xhdr_fname(char *, int, const DCC_CLNT_INFO *);
+extern void xhdr_init(DCC_HEADER_BUF *, DCC_SRVR_ID);
+extern void xhdr_add_str(DCC_HEADER_BUF *, const char *,  ...) PATTRIB(2,3);
+extern void xhdr_add_ck(DCC_HEADER_BUF *, DCC_CK_TYPES, DCC_TGTS);
+extern void xhdr_write(LOG_WRITE_FNC, void *, const char *, int, u_char);
+extern void xhdr_whitelist(DCC_HEADER_BUF *);
+extern u_char is_xhdr(const char *, int);
+
+
+#endif /* DCC_CLNT_H */
diff -r 000000000000 -r c7f6b056b673 include/dcc_config.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dcc_config.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,212 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * autoconf configuration settings
+ *	modified for WIN32
+ * Run ./configure on UNIX-like systems
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.112 $Revision$
+ * @configure_input@
+ */
+
+#ifndef DCC_CONFIG_H
+#define DCC_CONFIG_H
+
+#undef UNIX
+#define DCC_WIN32 1
+
+#define DCC_HOMEDIR "c:\\Program Files\\DCC"
+#undef DCC_LIBEXECDIR
+#define DCC_RUNDIR	"/var/run/dcc"
+
+/* use kludge file if asked */
+#undef NEED_KLUDGE_H
+
+/* deal with systems such as Solaris that do not have a __P() macro
+ *	assume they understand prototypes */
+#define HAVE_SYS_CDEFS_H 1
+#if !defined(HAVE_SYS_CDEFS_H) && !defined(__P)
+#define __P(protos) protos
+#endif
+
+/* some systems have uint32_t, others have u_int32_t, and some have both */
+#define DCC_HAVE_U_INT32_T 1
+/* and then there is u_*int64_t */
+#define DCC_HAVE_U_INT64_T 1
+
+/* 64-bit long int */
+#undef HAVE_64BIT_LONG
+
+/* 64-bit void* */
+#undef HAVE_64BIT_PTR
+
+/* ./configure does not check for pid_t on the grounds that only WIN32
+ * lacks it, and Windows is handled by the genbundle script */
+#undef HAVE_PID_T
+
+/* maximum number of DCC server rate-limiting blocks */
+#undef RL_MIN_MAX
+
+/* turn off dccifd AF_UNIX sockets on HP-UX */
+#undef HP_UX_BAD_AF_UNIX
+
+/* Use poll() instead of select() on Solaris and some other systems
+ * because socket() can yield file descripters larger than FD_SETSIZE. */
+#undef HAVE_POLL
+#undef USE_POLL
+
+/* number of cached open per-user whitelist files */
+#define NUM_CWFS 20
+
+#define TIME_WITH_SYS_TIME 1
+#undef HAVE_UTIME_H
+#undef HAVE_FUTIMES
+
+#undef HAVE_SETPGID
+
+#undef HAVE_GCC_ATTRIBUTES
+#undef HAVE_GCC_INLINE
+
+/* fill holes in the target */
+#undef HAVE_DAEMON
+#undef HAVE_VSYSLOG
+#define HAVE_HSTRERROR 1
+#undef HAVE_INET_NTOP
+#undef HAVE_GETHOSTID
+#undef HAVE_LOCALTIME_R
+#undef HAVE_GMTIME_R
+#undef HAVE_TIMEGM
+#undef HAVE_EACCESS
+#undef HAVE_ALTZONE
+
+#undef NEED_STRINGS_H
+#undef HAVE_STRLCPY
+#undef HAVE_STRLCAT
+
+/* A way to get the size of physical memory
+ *  Linux and Solaris have sysconf(_SC_PHYS_PAGES)
+ *  BSD systems have sysctl(HW_PHYSMEM)
+ *  HP-UX has pstat_getstatic() */
+#undef HAVE_PHYSMEM_TOTAL
+#define HAVE__SC_PHYS_PAGES 1
+#undef HAVE_HW_PHYSMEM
+#undef HAVE_PSTAT_GETSTATIC
+#define GOT_PHYSMEM 1
+/* use `dbclean -F` on Solaris to force less unneeded disk I/O */
+#undef USE_DBCLEAN_F
+
+/* can assume the hash table is junk after a reboot */
+#define HAVE_BOOTTIME 1
+
+
+/* files with 64-bit offsets */
+#undef HAVE_BIG_FILES
+
+/* 0 or minimum size of server database buffer or window */
+#define DB_MIN_MBYTE 0
+/* 0 or maximum size of server database buffer */
+#define DB_MAX_MBYTE 0
+
+
+/* 4.4BSD sockets */
+#undef HAVE_SA_LEN
+#undef HAVE_SOCKLEN_T
+#undef HAVE_AF_LOCAL
+
+#undef HAVE_INET_ATON
+
+#define HAVE_GETIPNODEBYNAME 1
+#define HAVE_GETIPNODEBYADDR 1
+#define HAVE_FREEHOSTENT 1
+
+#define HAVE_GETADDRINFO 1
+#define HAVE_GETNAMEINFO 1
+#define HAVE_FREEADDRINFO 1
+#define HAVE_GAI_STRERROR 1
+
+#define NO_IPV6 1
+#undef NO_AF_INET6
+#define CONF_S6_ADDR32 __u6_addr.__u6_addr32
+
+/* use getifaddrs() to get list of interface addresses */
+#define HAVE_GETIFADDRS 1
+#undef USE_DCC_GETIFADDRS
+/* Some systems have getifaddrs() but not freeifaddrs() */
+#define HAVE_FREEIFADDRS 1
+
+/* BIND resolver library */
+#undef HAVE_RESOLV_H
+#undef HAVE_ARPA_NAMESER_H
+#undef HAVE__RES
+#define HAVE_RES_INIT 1
+#define HAVE_RES_QUERY 1
+#define HAVE_DN_EXPAND 1
+
+/* Solaris and WIN32 do not have paths.h */
+#undef HAVE_PATHS_H
+
+/* Some systems have their own MD5 libraries */
+#undef HAVE_MD5
+
+#undef HAVE_SIGINTERRUPT
+
+#undef HAVE_PTHREADS
+
+/* HP_UX has sys/pthread.h instead of pthread.h */
+#define HAVE_PTHREAD_H 1
+
+/* Windows systems lack UNIX permission bits */
+#undef HAVE_PRIVATE_FILES
+
+/* __progname defined by crt0 and so a reasonable default for syslog */
+#undef HAVE___PROGNAME
+/* slightly more portable way to get the program name */
+#undef HAVE_GETPROGNAME
+
+/* very old BSD/OS has only 2 parameters for msync()
+ * and newer versions ignore the third parameter */
+#undef HAVE_OLD_MSYNC
+
+#define HAVE_COHERENT_MMAP 1
+
+/* use SOCKS */
+#undef HAVE_RSENDTO
+
+/* save only this much of mail messages in log files */
+#define MAX_LOG_KBYTE 32
+
+
+#endif /* DCC_CONFIG_H */
diff -r 000000000000 -r c7f6b056b673 include/dcc_config.h.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dcc_config.h.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,210 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * autoconf configuration settings
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.112 $Revision$
+ * @configure_input@
+ */
+
+#ifndef DCC_CONFIG_H
+#define DCC_CONFIG_H
+
+#define UNIX
+#undef DCC_WIN32
+
+#define DCC_HOMEDIR	"/var/dcc"
+#define DCC_LIBEXECDIR	"/var/dcc/libexec"
+#define DCC_RUNDIR	"/var/run/dcc"
+
+/* use kludge file if asked */
+#undef NEED_KLUDGE_H
+
+/* deal with systems such as Solaris that do not have a __P() macro
+ *	assume they understand prototypes */
+#undef HAVE_SYS_CDEFS_H
+#if !defined(HAVE_SYS_CDEFS_H) && !defined(__P)
+#define __P(protos) protos
+#endif
+
+/* some systems have uint32_t, others have u_int32_t, and some have both */
+#undef DCC_HAVE_U_INT32_T
+/* and then there is u_*int64_t */
+#undef DCC_HAVE_U_INT64_T
+
+/* 64-bit long int */
+#undef HAVE_64BIT_LONG
+
+/* 64-bit void* */
+#undef HAVE_64BIT_PTR
+
+/* ./configure does not check for pid_t on the grounds that only WIN32
+ * lacks it, and Windows is handled by the genbundle script */
+#undef HAVE_PID_T
+
+/* maximum number of DCC server rate-limiting blocks */
+#undef RL_MIN_MAX
+
+/* turn off dccifd AF_UNIX sockets on HP-UX */
+#undef HP_UX_BAD_AF_UNIX
+
+/* Use poll() instead of select() on Solaris and some other systems
+ * because socket() can yield file descripters larger than FD_SETSIZE. */
+#undef HAVE_POLL
+#undef USE_POLL
+
+/* number of cached open per-user whitelist files */
+#define NUM_CWFS 20
+
+#undef TIME_WITH_SYS_TIME
+#undef HAVE_UTIME_H
+#undef HAVE_FUTIMES
+
+#undef HAVE_SETPGID
+
+#undef HAVE_GCC_ATTRIBUTES
+#undef HAVE_GCC_INLINE
+
+/* fill holes in the target */
+#undef HAVE_DAEMON
+#undef HAVE_VSYSLOG
+#undef HAVE_HSTRERROR
+#undef HAVE_INET_NTOP
+#undef HAVE_GETHOSTID
+#undef HAVE_LOCALTIME_R
+#undef HAVE_GMTIME_R
+#undef HAVE_TIMEGM
+#undef HAVE_EACCESS
+#undef HAVE_ALTZONE
+
+#undef NEED_STRINGS_H
+#undef HAVE_STRLCPY
+#undef HAVE_STRLCAT
+
+/* A way to get the size of physical memory
+ *  Linux and Solaris have sysconf(_SC_PHYS_PAGES)
+ *  BSD systems have sysctl(HW_PHYSMEM)
+ *  HP-UX has pstat_getstatic() */
+#undef HAVE_PHYSMEM_TOTAL
+#undef HAVE__SC_PHYS_PAGES
+#undef HAVE_HW_PHYSMEM
+#undef HAVE_PSTAT_GETSTATIC
+#undef GOT_PHYSMEM
+/* use `dbclean -F` on Solaris to force less unneeded disk I/O */
+#undef USE_DBCLEAN_F
+
+/* can assume the hash table is junk after a reboot */
+#undef HAVE_BOOTTIME
+
+
+/* files with 64-bit offsets */
+#undef HAVE_BIG_FILES
+
+/* 0 or minimum size of server database buffer or window */
+#define DB_MIN_MBYTE 0
+/* 0 or maximum size of server database buffer */
+#define DB_MAX_MBYTE 0
+
+
+/* 4.4BSD sockets */
+#undef HAVE_SA_LEN
+#undef HAVE_SOCKLEN_T
+#undef HAVE_AF_LOCAL
+
+#undef HAVE_INET_ATON
+
+#undef HAVE_GETIPNODEBYNAME
+#undef HAVE_GETIPNODEBYADDR
+#undef HAVE_FREEHOSTENT
+
+#undef HAVE_GETADDRINFO
+#undef HAVE_GETNAMEINFO
+#undef HAVE_FREEADDRINFO
+#undef HAVE_GAI_STRERROR
+
+#undef NO_IPV6
+#undef NO_AF_INET6
+#define CONF_S6_ADDR32 __u6_addr.__u6_addr32
+
+/* use getifaddrs() to get list of interface addresses */
+#undef HAVE_GETIFADDRS
+#undef USE_DCC_GETIFADDRS
+/* Some systems have getifaddrs() but not freeifaddrs() */
+#undef HAVE_FREEIFADDRS
+
+/* BIND resolver library */
+#undef HAVE_RESOLV_H
+#undef HAVE_ARPA_NAMESER_H
+#undef HAVE__RES
+#undef HAVE_RES_INIT
+#undef HAVE_RES_QUERY
+#undef HAVE_DN_EXPAND
+
+/* Solaris and WIN32 do not have paths.h */
+#undef HAVE_PATHS_H
+
+/* Some systems have their own MD5 libraries */
+#undef HAVE_MD5
+
+#undef HAVE_SIGINTERRUPT
+
+#undef HAVE_PTHREADS
+
+/* HP_UX has sys/pthread.h instead of pthread.h */
+#undef HAVE_PTHREAD_H
+
+/* Windows systems lack UNIX permission bits */
+#undef HAVE_PRIVATE_FILES
+
+/* __progname defined by crt0 and so a reasonable default for syslog */
+#undef HAVE___PROGNAME
+/* slightly more portable way to get the program name */
+#undef HAVE_GETPROGNAME
+
+/* very old BSD/OS has only 2 parameters for msync()
+ * and newer versions ignore the third parameter */
+#undef HAVE_OLD_MSYNC
+
+#undef HAVE_COHERENT_MMAP
+
+/* use SOCKS */
+#undef HAVE_RSENDTO
+
+/* save only this much of mail messages in log files */
+#define MAX_LOG_KBYTE 32
+
+
+#endif /* DCC_CONFIG_H */
diff -r 000000000000 -r c7f6b056b673 include/dcc_defs.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dcc_defs.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,786 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * common definitions internal to client libraries and servers
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.309 $Revision$
+ */
+
+#ifndef DCC_DEFS_H
+#define DCC_DEFS_H
+
+#define DCC_VERSION "1.3.103"
+
+#include "dcc_config.h"
+
+/* work on WIN32 and any reasonable UNIX platform */
+#ifdef UNIX
+#include <stdarg.h>
+#include <stdio.h>			/* for FreeBSD */
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+#undef EX_OK				/* IRIX defines EX_OK in unistd.h */
+#include <limits.h>			/* for FreeBSD */
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/param.h>			/* for FreeBSD */
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#else /* !UNIX or DCC_WIN32 */
+#define STRICT
+#define WIN32_LEAN_AND_MEAN
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <winerror.h>
+#include <limits.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <io.h>
+
+typedef unsigned int	u_int32_t;
+typedef signed int	int32_t;
+typedef unsigned short	u_int16_t;
+#endif /* !UNIX or DCC_WIN32 */
+
+/* even some UNIX systems have ancient, unusable versions of sysexits.h */
+#include "sendmail-sysexits.h"
+
+/* use kludge file if asked */
+#ifdef NEED_KLUDGE_H
+#include "kludge.h"
+#endif
+
+#ifdef NEED_STRINGS_H
+#include <strings.h>
+#endif
+
+#if !defined(DCC_HAVE_U_INT32_T)
+#define u_int32_t uint32_t
+#define u_int16_t uint16_t
+#endif
+#if !defined(DCC_HAVE_U_INT64_T)
+#define u_int64_t uint64_t
+#endif
+
+#ifdef HAVE_GCC_ATTRIBUTES
+#define UATTRIB __attribute__((unused))
+#define PATTRIB(f,l) __attribute__((format (printf,f,l)))
+#define NRATTRIB __attribute((__noreturn__))
+#else
+#define UATTRIB
+#define PATTRIB(f,l)
+#define NRATTRIB
+#endif
+
+#ifndef HAVE_GCC_INLINE
+#define inline
+#endif
+
+typedef char DCC_PASSWD[32];
+#define DCC_PASSWD_PAT "%.32s"
+
+typedef char DCC_EMSG[120];
+
+/* deal with ancient UNIX */
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+#ifndef STDERR_FILENO
+#define STDERR_FILENO 2
+#endif
+
+#ifdef UNIX
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+#ifdef HAVE_OLD_MSYNC
+#define MSYNC(addr,len,flags) msync((void *)(addr),(len))
+#else
+#define MSYNC(addr,len,flags) msync((void *)(addr),(len),(flags))
+#endif
+
+#ifndef FD_CLOEXEC
+#define FD_CLOEXEC 1
+#endif
+
+#define WIN32_SOC_CAST
+
+typedef int SOCKET;
+#define INVALID_SOCKET (-1)
+#define SOCKET_ERROR (-1)
+#define WSAAPI
+#define closesocket close
+#define ERROR_STR() ERROR_STR1(errno)
+#define ERROR_STR1(e) strerror(e)
+/* at least some filters including IPFW say EACCES on hits,
+ * so treat EACCES like Unreachables */
+#define UNREACHABLE_ERRORS() (errno == ECONNREFUSED		    \
+			      || errno == EHOSTUNREACH		    \
+			      || errno == ENETUNREACH		    \
+			      || errno == EHOSTDOWN		    \
+			      || errno == ENETDOWN		    \
+			      || errno == EACCES)
+#define DCC_SELECT_NERROR() (errno == EINTR || errno == EAGAIN)
+/* EWOULDBLOCK and EAGAIN differ on HP-UX */
+#define DCC_BLOCK_ERROR() (errno == EWOULDBLOCK || errno == EAGAIN)
+
+/* PATH_MAX is an over generous 1024 on many UNIX varients,
+ * but an incredible, ridiculous waste of 4096 on some Linux flavors.
+ * Each of the hundreds of dccm and dccifd recipient structures contain
+ * 2 paths.  Then there are the 8 paths in .dccw files.  So this matters . */
+typedef char DCC_PATH[768];
+
+#else /* !UNIX or DCC_WIN32 */
+extern void win32_init(void);
+
+#undef errno
+#define errno WSAGetLastError()
+#define h_errno WSAGetLastError()
+#define ERROR_STR() ERROR_STR1(errno)
+#define ERROR_STR1(e) ws_strerror(e)
+const char *ws_strerror(int);
+#define UNREACHABLE_ERRORS() ((errno) == WSAECONNREFUSED	\
+			      || (errno) == WSAEHOSTUNREACH	\
+			      || (errno) == WSAENETUNREACH	\
+			      || (errno) == WSAEHOSTDOWN	\
+			      || (errno) == WSAENETDOWN)
+#define DCC_SELECT_NERROR() (errno == WSAEINTR)
+#define DCC_BLOCK_ERROR() (errno == WSAEWOULDBLOCK || errno == WSAEINTR)
+#define EADDRINUSE	WSAEADDRINUSE
+
+typedef char DCC_PATH[MAX_PATH+1];
+
+#define MAXHOSTNAMELEN   256
+
+/* Microsoft sendto(), recvfrom() want char *buffers */
+#define WIN32_SOC_CAST (char *)
+
+/* some WIN32 versions lack snprintf() */
+#define snprintf dcc_snprintf
+extern int dcc_snprintf(char *, int, const char *, ...);
+#define vsnprintf dcc_vsnprintf
+extern int dcc_vsnprintf(char *, int, const char *, va_list);
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+extern int getopt(int, char * const [], const char *);
+
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+extern int getpid(void);
+#define usleep(us) Sleep((us+500)/1000)
+
+#define LOG_ERR	    0
+#define LOG_MAIL    0
+#define LOG_NOTICE  0
+#define LOG_PID	    0
+extern void openlog(const char *, int, int);
+extern void syslog(int, const char *, ...);
+extern void closelog(void);
+
+struct timezone {
+    int     tz_minuteswest;		/* minutes west of Greenwich */
+    int     tz_dsttime;			/* type of dst correction */
+};
+extern int gettimeofday(struct timeval *, struct timezone *);
+
+#ifndef HAVE_PID_T
+#undef pid_t
+#define pid_t int
+#endif
+
+/* FlushViewOfFile() on Win98 sometimes returns 0 with GetLastError()==0 */
+#define MSYNC(addr,len,flags) (FlushViewOfFile(addr,0), 0)
+
+#define R_OK	04
+#define W_OK	02
+#ifndef S_ISDIR
+#define S_ISDIR(mode) (mode & _S_IFDIR)
+#endif
+
+extern u_char win32_lock(HANDLE, DWORD);
+extern u_char win32_unlock(HANDLE);
+
+extern void win32_unmap(HANDLE *, void *, const char *);
+extern void *win32_map(DCC_EMSG, HANDLE *, const char *, int, int);
+#endif /* !UNIX or DCC_WIN32 */
+
+
+#ifndef HAVE_DAEMON
+#define daemon dcc_daemon
+extern int daemon(int, int);
+#endif
+extern void dcc_daemon_restart(const char *, void(*)(void));
+extern void dcc_daemon_su(const char *);
+extern void dcc_pidfile(DCC_PATH, const char *);
+
+/* AIX is missing some prototypes or has them #ifdef'ed with strange switches */
+#ifdef _AIX41
+#include <sys/select.h>
+typedef unsigned long long int uint64_t;
+typedef unsigned int uint32_t;
+typedef int int32_t;
+typedef unsigned short uint16_t;
+
+extern void openlog(const char *, int, int);
+extern int snprintf(char *, int, const char *, ...);
+extern int vsnprintf(char *, int, const char *, const char *, ...);
+extern int seteuid(uid_t);
+extern int flock(int, int);
+#define	AF_LOCAL AF_UNIX
+#endif /* _AIX41 */
+
+#ifdef __hpux
+#define seteuid(euid) setresuid(-1,euid,-1)
+#define setegid(egid) setresgid(-1,egid,-1)
+#endif /* __hpux */
+
+
+#define DCC_MAXDOMAINLEN   256		/* limit host names; with \0 */
+
+/* 4.4BSD sockets */
+#ifdef HAVE_SA_LEN
+#define DCC_SU_LEN(s) ((s)->sa.sa_len)
+#else
+#define DCC_SU_LEN(s) (((s)->sa.sa_family == AF_INET)	\
+		       ? sizeof((s)->ipv4) : sizeof((s)->ipv6))
+#endif
+#ifdef HAVE_SOCKLEN_T
+/* use #define to avoid problems with SOCKS prototypes */
+#define DCC_SOCKLEN_T socklen_t
+#else
+#define DCC_SOCKLEN_T int
+#endif
+#if !defined(HAVE_AF_LOCAL) && !defined(AF_LOCAL)
+#define AF_LOCAL AF_UNIX
+#endif
+
+/* these are needed even when IPV6 is available to handle old map file
+ * formats in dcc_clnt.h */
+struct dcc_in6_addr {
+    u_int32_t dcc_s6_addr32[4];
+};
+struct dcc_sockaddr_in6 {
+    u_char	sin6_len;
+    u_char	sin6_family;
+    u_int16_t	sin6_port;
+    u_int32_t	sin6_flowinfo;
+    struct dcc_in6_addr sin6_addr;
+};
+/* define in6_addr here after u_int32_t has been seen in sys/types.h */
+#ifdef NO_IPV6
+#undef in6_addr
+#define in6_addr dcc_in6_addr		/* defend against lurking definitions */
+#undef s6_addr32
+#define s6_addr32 dcc_s6_addr32
+#define sockaddr_in6 dcc_sockaddr_in6	/* defend against lurking definitions */
+#endif /* NO_IPV6 */
+
+
+#include "dcc_proto.h"
+
+
+/* see if an address is in an IPv6 CIDR block */
+#define DCC_IN_BLOCK(a,b,m) ((((a).s6_addr32[3] & (m).s6_addr32[3])	\
+			      == (b).s6_addr32[3])			\
+			     && (((a).s6_addr32[2] & (m).s6_addr32[2])	\
+				 == (b).s6_addr32[2])			\
+			     && (((a).s6_addr32[1] & (m).s6_addr32[1])	\
+				 == (b).s6_addr32[1])			\
+			     && (((a).s6_addr32[0] & (m).s6_addr32[0])	\
+				 == (b).s6_addr32[0]))
+
+
+/* puzzle out something for s6_addr32 */
+#if !defined(s6_addr32) && defined(CONF_S6_ADDR32)
+#define s6_addr32 CONF_S6_ADDR32
+#endif
+
+#ifdef NO_AF_INET6
+#define AF_INET6 24
+#endif
+
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+#ifndef INET_ADDRSTRLEN
+#define INET_ADDRSTRLEN 16
+#endif
+
+#define MAXPORTNAMELEN	64
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#define DCC_SU_EQ(a,b) ((a)->sa.sa_family == (b)->sa.sa_family		\
+			&& (((a)->sa.sa_family == AF_INET)		\
+			    ? ((a)->ipv4.sin_addr.s_addr		\
+			       == (b)->ipv4.sin_addr.s_addr)		\
+			    : !memcmp(&(a)->ipv6.sin6_addr,		\
+				      &(b)->ipv6.sin6_addr,		\
+				      sizeof((a)->ipv6.sin6_addr))))
+
+/* zero port number if sa_family is AF_UNSPEC */
+#define DCC_SU_PORT(su) ((su)->sa.sa_family == AF_INET			\
+			 ? (su)->ipv4.sin_port				\
+			 : (su)->sa.sa_family == AF_INET6		\
+			 ? (su)->ipv6.sin6_port				\
+			 : 0)
+
+/* use IPv4 port number if sa_family is AF_UNSPEC */
+#define DCC_SU_PORTP(su) ((su)->sa.sa_family == AF_INET6		\
+			  ? &(su)->ipv6.sin6_port			\
+			  : &(su)->ipv4.sin_port)
+
+#define DCC_IN6_ADDR_V4MAPPED(ap) ((ap)->s6_addr32[0] == 0		\
+				   && (ap)->s6_addr32[1] == 0		\
+				   && (ap)->s6_addr32[2] == ntohl(0x0000ffff))
+
+#define DCC_IN6_ADDR_V4COMPAT(ap) ((ap)->s6_addr32[0] == 0		\
+				   && (ap)->s6_addr32[1] == 0		\
+				   && (ap)->s6_addr32[2] == 0		\
+				   && (ap)->s6_addr32[3] == 0		\
+				   && (ap)->s6_addr32[3] == ntohl(1))
+
+
+
+/* printf patterns for 64-bit values */
+#ifdef HAVE_64BIT_LONG
+#define LL_PAT	    "l"
+#else
+#define LL_PAT	    "ll"
+#endif
+#define	L_HPAT	    "%#"LL_PAT"x"
+#define	L_HWPAT(w)  "%#"#w LL_PAT"x"	/* hex with specified width */
+#define L_DPAT	    "%"LL_PAT"d"
+#define L_DWPAT(w)  "%"#w LL_PAT"d"	/* decimal with specified width */
+
+/* printf pattern for size_t and off_t */
+#ifdef HAVE_BIG_FILES
+#define OFF_HPAT    L_HPAT
+#define OFF_DPAT    L_DPAT
+#else
+#define OFF_HPAT    "%#lx"
+#define OFF_DPAT    "%ld"
+#endif
+
+#define DIM(_a)	    ((int)(sizeof(_a) / sizeof((_a)[0])))
+#define LAST(_a)    (&(_a)[DIM(_a)-1])
+/* abbreviation to silence common compiler warnings */
+#define ISZ(_s)	    ((int)sizeof(_s))
+
+#ifdef HAVE_STRLCPY
+#define STRLCPY(d,s,lim) strlcpy(d,s,lim)
+#else
+#define STRLCPY(d,s,lim) ((lim)<=0 ? d		\
+			  : ((d)[(lim)-1] = '\0', strncpy(d,s,(lim)-1)))
+#endif
+#define BUFCPY(d,s) STRLCPY(d,s,sizeof(d))
+#ifdef HAVE_STRLCAT
+#define STRLCAT(d,s,lim) strlcat(d,s,lim)
+#else
+#define STRLCAT(d,s,lim) dcc_strlcat(d,s,lim)
+extern int dcc_strlcat(char *, const char *, int);
+#endif
+
+#define LITZ(_s)	((int)sizeof(_s)-1)
+#define CLITCMP(b,_s)	strncasecmp(b, _s, LITZ(_s))
+#define LITCMP(b,_s)	strncmp(b, _s, LITZ(_s))
+
+
+#ifndef HAVE___PROGNAME
+#define __progname dcc_progname
+#endif
+
+#undef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#undef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+/* Is it time or has the local clock been changed?
+ *	Is the clock after the target date or earlier than the original date? */
+#define DCC_IS_TIME(now,tgt,lim) ((now) >= (tgt) || (now)+(lim) < (tgt))
+
+#define DCC_ADJ_TIMER(now,tgt,lim,new_lim) {				\
+	if (*(tgt) > (now)+(new_lim))					\
+		*(tgt) = (now)+(new_lim);				\
+	*(lim) = (new_lim);						\
+}
+
+#define FOREVER_SECS	1000
+#define FOREVER_US	(FOREVER_SECS*DCC_US)
+#define us2tv(tvp,us)	((tvp)->tv_sec = (us) / DCC_US,			\
+			  (tvp)->tv_usec = (us) % DCC_US)
+extern time_t tv_diff2us(const struct timeval *, const struct timeval *);
+extern void tv_add(struct timeval *,
+		   const struct timeval *, const struct timeval *);
+extern void tv_add_us(struct timeval *, time_t);
+
+/* prefer gmtime_r even where we don't really care */
+#ifdef HAVE_GMTIME_R
+#define DCC_GMTIME_R(src,tgt) gmtime_r(src,tgt)
+#else
+#define DCC_GMTIME_R(src,tgt) memcpy(tgt,gmtime(src),sizeof(struct tm))
+#endif
+
+#define DCC_WHITESPACE " \t\n\r"
+
+/* ctype() is now a slow mess that does not give constant results on all
+ * systems */
+#define DCC_IS_WHITE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c)== '\n')
+#define DCC_IS_UPPER(c) ((c) >= 'A' && (c) <= 'Z')
+#define DCC_IS_LOWER(c) ((c) >= 'a' && (c) <= 'z')
+#define DCC_TO_LOWER(c) (DCC_IS_UPPER(c) ? ((c) + ('a'-'A')) : (c))
+#define DCC_TO_UPPER(c) (DCC_IS_LOWER(c) ? ((c) - ('a'-'A')) : (c))
+
+typedef union {
+    struct sockaddr sa;
+    struct sockaddr_in ipv4;
+    struct sockaddr_in6 ipv6;
+} DCC_SOCKU;
+
+/* we cannot always use DCC_SOCKU because sizeof(sockaddr_in6) is not defined
+ * on systems without IPv6 and when they do get IPv6, the native sockaddr_in
+ * often differs in size from the work-around version in the DCC source */
+typedef struct {
+    union {
+	struct in_addr v4;
+	struct in6_addr v6;
+    } u;
+    u_int16_t	port;
+    u_char	family;
+} DCC_IP;
+
+#ifdef HAVE_RSENDTO
+extern int Rconnect(int, const struct sockaddr *, DCC_SOCKLEN_T);
+extern ssize_t Rsend(int, const void *, size_t, int);
+extern ssize_t Rsendto(int, const void *, size_t, int,
+		       const struct sockaddr *, size_t);
+extern ssize_t Rrecv(int, void *, size_t, int);
+extern ssize_t Rrecvfrom(int, void *, size_t, int,
+			 struct sockaddr *, DCC_SOCKLEN_T *);
+extern struct hostent *Rgethostbyname(const char *);
+#else
+#define Rconnect    connect
+#define Rsend	    send
+#define Rsendto	    sendto
+#define Rrecv	    recv
+#define Rrecvfrom   recvfrom
+#define Rgethostbyname gethostbyname
+#endif
+
+/* use very old fashioned gethostbyname() if we are not doing any IPv6 */
+#ifdef NO_IPV6
+#undef HAVE_GETIPNODEBYNAME
+#undef HAVE_GETADDRINFO
+#endif
+
+#if !defined(HAVE_GETIPNODEBYNAME) || !defined(HAVE_FREEHOSTENT) || !defined(HAVE_GETIPNODEBYADDR)
+#undef HAVE_GETIPNODEBYNAME
+#endif
+
+#if !defined(HAVE_GETADDRINFO) || !defined(HAVE_FREEADDRINFO) || !defined(HAVE_GAI_STRERROR) || !defined(HAVE_GETNAMEINFO)
+#undef HAVE_GETADDRINFO
+#endif
+
+/* prefer getaddrinfo() for IPv6 resolution */
+#if defined(HAVE_GETADDRINFO)
+#define USE_GETADDRINFO
+#undef HAVE_GETIPNODEBYNAME
+#endif
+#ifdef HAVE_GETIPNODEBYNAME
+#define USE_GETIPNODEBYNAME
+#undef HAVE_GETADDRINFO
+#endif
+
+#undef DCC_HSTRERROR
+#ifdef UNIX
+#ifdef USE_GETADDRINFO
+#define DCC_HSTRERROR(e) gai_strerror(e)
+#endif
+#if !defined(DCC_HSTRERROR) && defined(HAVE_HSTRERROR)
+#define DCC_HSTRERROR(e) hstrerror(e)
+#endif
+#ifndef DCC_HSTRERROR
+#define DCC_HSTRERROR(e) dcc_hstrerror(e)
+#endif
+#else /* !UNIX or DCC_WIN32 */
+#define DCC_HSTRERROR(e) ws_strerror(e)
+#endif /* !UNIX or DCC_WIN32 */
+
+
+#ifndef HAVE_POLL
+#undef USE_POLL
+#endif
+
+#define LOGBUF_SIZE	(DCC_MAXDOMAINLEN*2)
+
+extern int dcc_ex_code;			/* not thread safe */
+extern void dcc_pemsg(int, DCC_EMSG, const char *, ...) PATTRIB(3,4);
+extern void dcc_vpemsg(int, DCC_EMSG, const char *, va_list);
+#define DCC_FNM_LNO_PAT " in line %d of %s"
+typedef struct {
+	char b[sizeof(DCC_PATH)+sizeof(DCC_FNM_LNO_PAT)+8];
+} DCC_FNM_LNO_BUF;
+extern const char *fnm_lno(DCC_FNM_LNO_BUF *, const char *, int);
+
+
+#define DCC_MAX_HDR_LINE    78		/* by RFC 2822 */
+#define DCC_MAX_XHDR_LEN    240		/* largest possible X-DCC header */
+typedef struct {
+    u_int	start_len;		/* length of start up to ':' */
+    u_int	used;			/* bytes of buffer used */
+    int		col;			/* current column */
+    char	buf[DCC_MAX_XHDR_LEN];
+} DCC_HEADER_BUF;
+
+#define DCC_MAP_NM_DEF	    "map"
+
+
+#ifndef HAVE_PTHREADS
+#undef HAVE_HELPERS
+#else
+#define HAVE_HELPERS 1
+#endif
+
+
+typedef void(*LOG_WRITE_FNC)(void *context, const char *buf, u_int buflen);
+
+extern void dcc_sign(const char *, int, void *, u_int);
+extern u_char dcc_ck_signature(const char *, int, const void *, u_int);
+
+extern DCC_PATH dcc_homedir;
+extern u_char fnm2rel(DCC_PATH, const char *, const char *);
+extern void fnm2rel_good(DCC_PATH, const char *, const char *);
+extern u_char fnm2abs(DCC_PATH, const char *, const char *);
+extern const char *fnm2abs_err(DCC_PATH, const char *);
+extern const char *path2fnm(const char *);
+extern u_char dcc_cdhome(DCC_EMSG, const char *, u_char);
+
+extern uid_t dcc_real_uid, dcc_effective_uid;
+extern gid_t dcc_real_gid, dcc_effective_gid;
+extern void dcc_get_priv(void);
+extern u_char dcc_get_priv_home(const char *);
+extern void dcc_rel_priv(void);
+extern void dcc_init_priv(void);
+extern u_char dcc_ck_private(DCC_EMSG, struct stat *, const char *, int);
+extern int dcc_lock_open(DCC_EMSG, const char *, int, u_char, int, u_char *);
+# define DCC_LOCK_OPEN_NOWAIT	0x1	/* don't wait to get lock */
+# define DCC_LOCK_OPEN_NOLOCK	0x2	/* already locked */
+# define DCC_LOCK_OPEN_SHARE	0x4
+#define DCC_LOCK_ALL_FILE   (-1)
+extern u_char dcc_unlock_fd(DCC_EMSG, int, int, const char *, const char *);
+extern u_char dcc_exlock_fd(DCC_EMSG, int, int, int,
+			    const char *, const char *);
+extern u_char dcc_set_mtime(DCC_EMSG, const char *, int,
+			    const struct timeval *);
+
+extern DCC_PATH dcc_main_logdir;
+extern void tmp_path_init(const char *, const char *);
+#define	DCC_TMP_LOG_PREFIX "/tmp."	/* must be the same length */
+#define	DCC_FIN_LOG_PREFIX "/msg."
+#define DCC_MKSTEMP_LEN 6		/* characters added by dcc_mkstemp() */
+#define DCC_MKSTEMP_LEN_STR "6"
+extern int dcc_mkstemp(DCC_EMSG, char *, int, char *, int,
+		       const char *, const char *, const char *, u_char, int);
+extern u_char dcc_main_logdir_init(DCC_EMSG, const char *);
+typedef enum {				/* type of log subdirector */
+    LOG_MODE_FLAT,			/*	logdir/ */
+    LOG_MODE_DAY,			/*	logdir/ddd/ */
+    LOG_MODE_HOUR,			/*	logdir/ddd/hh/ */
+    LOG_MODE_MINUTE			/*	logdir/ddd/hh/mm/ */
+} LOG_MODE;
+extern int dcc_log_open(DCC_EMSG, DCC_PATH, char *, int,
+			const char *, const char *, const char *, LOG_MODE);
+extern int dcc_main_log_open(DCC_EMSG, DCC_PATH log_path, char *, int);
+extern u_char dcc_log_keep(DCC_EMSG, char *);
+extern u_char dcc_log_close(DCC_EMSG, const char *, int,
+			    const struct timeval *);
+#define DCC_LOG_DATE_PAT "VERSION: 3\nDATE: %s"
+#define DCC_LOG_DATE_FMT "%x %X %Z"
+#define DCC_LOG_MSG_SEP "\n### end of message body ########################\n"
+#define DCC_LOG_TRN_MSG0 "### log truncated ######################"
+#define DCC_LOG_TRN_MSG "\n"DCC_LOG_TRN_MSG0"\n"
+#define DCC_LOG_TRN_MSG_CR "\r\n"DCC_LOG_TRN_MSG0"\r\n"
+typedef struct {
+    int	    len;
+    char    buf[500];
+} EARLY_LOG;
+extern int dcc_vearly_log(EARLY_LOG *, const char *, va_list);
+extern const char *optopt2str(int);
+
+
+#define DCC_US	(1000*1000)
+
+extern const struct tm *dcc_localtime(time_t, struct tm *);
+#ifndef HAVE_LOCALTIME_R
+extern void dcc_localtime_lock(void);
+extern void dcc_localtime_unlock(void);
+#endif
+const char *dcc_time2str(char *, size_t, const char *, time_t);
+#ifdef HAVE_TIMEGM
+#define DCC_TIMEGM(tm) timegm(tm)
+#else
+#ifdef HAVE_ALTZONE
+#define DCC_TIMEGM(tm) ((tm)->tm_isdst=-1, mktime(tm) - altzone)
+#else
+#define DCC_TIMEGM(tm) ((tm)->tm_isdst=-1, mktime(tm))
+#endif
+#endif
+
+extern int dcc_get_secs(const char *, const char **, int, int, int);
+#define DCC_MAX_SECS 0x7fffffff
+extern u_int dcc_get_port(DCC_EMSG, const char *, u_int, const char *, int);
+#define DCC_GET_PORT_INVALID    0x10000
+extern u_char dcc_host_locked;
+#define MAX_DCC_HOSTADDRS 20
+extern DCC_SOCKU dcc_hostaddrs[MAX_DCC_HOSTADDRS];
+extern char dcc_host_canonname[DCC_MAXDOMAINLEN];
+extern DCC_SOCKU *dcc_hostaddrs_end;
+extern void dcc_host_lock(void);
+extern void dcc_host_unlock(void);
+extern u_char dcc_get_host(const char *, u_char, int *);
+extern u_char dcc_get_host_SOCKS(const char *, u_char, int *);
+extern const char *dcc_hstrerror(int);
+extern DCC_SOCKU *dcc_mk_su(DCC_SOCKU *, int, const void *, u_short);
+extern u_char dcc_str2ip(DCC_SOCKU *, const char *);
+extern void dcc_bits2mask(struct in6_addr *, int);
+extern int dcc_str2cidr(DCC_EMSG, struct in6_addr *, struct in6_addr *,
+			u_char *, const char *, const char *, int);
+extern int dcc_udp_bind(DCC_EMSG, SOCKET *, const DCC_SOCKU *, int *);
+extern DCC_CLNT_ID dcc_get_id(DCC_EMSG, const char *, const char *, int);
+extern const char *dcc_get_srvr_id(DCC_EMSG, DCC_SRVR_ID *,
+				   const char *, const char *,
+				   const char *, int);
+extern const char *dcc_parse_word(DCC_EMSG, char *, int,
+				  const char *, const char *,
+				  const char *, int);
+extern const char *parse_passwd(DCC_EMSG, DCC_PASSWD, const char *,
+				const char *, const char *, int);
+extern u_char dcc_ck_word_comma(const char **, const char *);
+
+extern void dcc_ipv4toipv6(struct in6_addr *, const struct in_addr);
+extern u_char dcc_ipv6toipv4(struct in_addr *, const struct in6_addr *);
+extern void dcc_su2ip(DCC_IP *, const DCC_SOCKU *);
+extern u_char dcc_ipv6sutoipv4(DCC_SOCKU *, const DCC_SOCKU *);
+extern u_char dcc_ipv4sutoipv6(DCC_SOCKU *, const DCC_SOCKU *);
+
+const char *dcc_trim_ffff(const char *);
+#define DCC_SU2STR_SIZE (INET6_ADDRSTRLEN+1+6+1)
+extern const char *dcc_ipv4tostr(char *, int, const struct in_addr *);
+extern const char *dcc_ipv6tostr(char *, int, const struct in6_addr *);
+extern const char *dcc_ipv6tostr2(char *, int, const struct in6_addr *);
+const char *dcc_ip2str(char *, int, const DCC_IP *);
+extern const char *dcc_su2str(char *, int, const DCC_SOCKU *);
+extern const char *dcc_su2str2(char *, int, const DCC_SOCKU *);
+extern const char *dcc_su2str3(char *, int, const DCC_SOCKU *, u_short);
+extern const char *dcc_su2str_err(const DCC_SOCKU *);
+extern const char * dcc_host_portname(char *, int, const char *, const char *);
+extern const char *dcc_su2name(char *, int, const DCC_SOCKU *);
+
+extern void clean_stdio(void);
+extern u_char dcc_no_syslog;
+extern int dcc_error_priority, dcc_trace_priority;
+extern u_char dcc_parse_log_opt(const char *);
+extern void dcc_syslog_init(u_char, const char *, const char *);
+extern DCC_PATH dcc_progname;
+extern int dcc_progname_len;
+extern void dcc_vfatal_msg(const char *, va_list);
+extern int dcc_verror_msg(const char *, va_list);
+extern void dcc_error_msg(const char *, ...) PATTRIB(1,2);
+extern void dcc_vtrace_msg(const char *, va_list);
+extern void dcc_trace_msg(const char *, ...) PATTRIB(1,2);
+extern u_char trace_quiet;
+extern void quiet_trace_msg(const char *p, ...) PATTRIB(1,2);
+#ifndef HAVE_VSYSLOG
+#define vsyslog dcc_vsyslog
+extern void vsyslog(int, const char *, va_list);
+#endif
+
+extern void NRATTRIB dcc_logbad(int, const char *, ...) PATTRIB(2,3);
+#define DCC_ASSERT(c) ((c) ? 0 : dcc_logbad(EX_SOFTWARE, #c))
+
+extern const char *id2str(char *, u_int, DCC_CLNT_ID);
+extern char *dcc_ck2str(char *, u_int, DCC_CK_TYPES, const DCC_SUM, u_int32_t);
+#define DCC_CK2STR_LEN (sizeof(DCC_SUM)*2+sizeof(DCC_SUM)/4+1)
+extern const char *dcc_ck2str_err(DCC_CK_TYPES, const DCC_SUM, u_int32_t);
+extern char *dcc_tgts2str(char *, u_int, DCC_TGTS, u_char);
+extern char *dcc_thold2str(char *, u_int, DCC_CK_TYPES, DCC_TGTS);
+extern char *dcc_type2str(char *, u_int, DCC_CK_TYPES,
+			  const char *, u_char, u_char);
+extern const char *dcc_type2str_err(DCC_CK_TYPES,
+				    const char *, u_char, u_char);
+#define SET_ALL_THOLDS	(DCC_CK_TYPE_LAST+1)
+#define SET_CMN_THOLDS	(SET_ALL_THOLDS+1)
+extern DCC_CK_TYPES dcc_str2type_del(const char *, int);
+extern DCC_CK_TYPES dcc_str2type_db(const char *, int);
+extern DCC_CK_TYPES dcc_str2type_thold(const char *, int);
+extern DCC_CK_TYPES dcc_str2type_wf(const char *, int);
+extern DCC_TGTS dcc_str2cnt(const char *);
+
+extern const char *dcc_aop2str(char *, int, DCC_AOPS, u_int32_t);
+extern const char *dcc_hdr_op2str(char *, int, const DCC_HDR *);
+#define DCC_OPBUF 32
+
+
+#endif /* DCC_DEFS_H */
diff -r 000000000000 -r c7f6b056b673 include/dcc_heap_debug.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dcc_heap_debug.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,61 @@
+/* Distributed Checksum Clearinghouse heap debugging
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.11 $Revision$
+ */
+
+#ifndef DCC_HEAP_DEBUG_H
+#define DCC_HEAP_DEBUG_H
+
+#ifdef DCC_DEBUG_HEAP
+extern void *dcc_malloc(size_t);
+extern void *dcc_calloc(size_t, size_t);
+extern void dcc_malloc_check(void);
+extern char *dcc_strdup(const char *);
+extern void dcc_free(void *);
+#ifdef UNIX
+extern char *malloc_options;
+#endif
+
+#else /* !DCC_DCC_DEBUG_HEAP */
+#define dcc_malloc(l) malloc(l)
+#define dcc_calloc(l,n) calloc(l,n)
+#define dcc_malloc_check()
+#define dcc_strdup(s) strdup(s)
+#define dcc_free(p) free(p)
+#endif /* !DCC_DCC_DEBUG_HEAP */
+
+#endif /* DCC_HEAP_DEBUG_H */
diff -r 000000000000 -r c7f6b056b673 include/dcc_ids.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dcc_ids.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,77 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * server-IDs
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.29 $Revision$
+ */
+
+#ifndef DCC_IDS_H
+#define DCC_IDS_H
+
+/* IDs file used by server, server utilities, and cdcc */
+extern DCC_PATH ids_path;
+extern time_t ids_mtime;
+#define IDS_NM_DEF  "ids"
+
+
+/* authenticated client database */
+typedef u_char ID_TBL_FLGS;
+typedef struct id_tbl {
+    struct id_tbl *fwd;
+    DCC_CLNT_ID	id;
+    time_t	delay_us;
+#    define	 DCC_ANON_DELAY_US_DEF	(50*1000)
+#    define	 DCC_ANON_DELAY_US_BLACKLIST DCC_ANON_DELAY_MAX
+    u_int	delay_inflate;
+#    define	 DCC_ANON_INFLATE_OFF	((u_int)(-1))
+    ID_TBL_FLGS	flags;
+#    define	 ID_FLG_SETTINGS    0x01    /* not just a placeholder */
+#    define	 ID_FLG_RPT_OK	    0x02    /* override dccd -Q */
+    u_char	srvr_type;		/* DCC_ID_SRVR_* */
+    DCC_PASSWD	cur_passwd;
+    DCC_PASSWD	next_passwd;
+} ID_TBL;
+
+extern u_char parse_dccd_delay(DCC_EMSG, time_t *, u_int *,
+			       const char *, const char *, int);
+extern ID_TBL *find_id_tbl(DCC_CLNT_ID);
+extern ID_TBL *add_id_tbl(DCC_CLNT_ID);
+extern ID_TBL *enum_ids(ID_TBL *);
+extern u_char set_ids_path(DCC_EMSG, const char *);
+extern int load_ids(DCC_EMSG, DCC_CLNT_ID, const ID_TBL **, u_char);
+
+#endif /* DCC_IDS_H */
diff -r 000000000000 -r c7f6b056b673 include/dcc_ifaddrs.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dcc_ifaddrs.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,73 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * kludge replacement for getifaddrs(3) for systems that lack it
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.12 $Revision$
+ */
+
+
+#ifndef DCC_IFADDRS_H
+#define DCC_IFADDRS_H
+
+#include <net/if.h>
+
+#ifndef USE_DCC_GETIFADDRS
+#include <ifaddrs.h>
+
+#else
+#define HAVE_GETIFADDRS
+#define HAVE_FREEIFADDRS
+
+struct ifaddrs {
+    struct ifaddrs  *ifa_next;
+    char	    *ifa_name;
+    u_int	    ifa_flags;
+    struct sockaddr *ifa_addr;
+};
+
+struct ifaddrs_all {
+    struct ifaddrs  ifa;
+    DCC_SOCKU	    addr;
+    char	    name[IFNAMSIZ];
+};
+
+#define getifaddrs(pif) dcc_getifaddrs(pif)
+extern int dcc_getifaddrs(struct ifaddrs **);
+#define freeifaddrs(p) dcc_free(p)
+
+#endif /* USE_DCC_GETIFADDRS */
+#endif /* DCC_IFADDRS_H */
diff -r 000000000000 -r c7f6b056b673 include/dcc_md5.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dcc_md5.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,52 @@
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ * rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+/*  Rhyolite Software DCC 1.3.103-1.4 $Revision$ */
+
+#ifndef DCC_MD5_H
+#define DCC_MD5_H
+
+#define MD5_DIGEST_LEN 16
+typedef u_char MD5_DIGEST[MD5_DIGEST_LEN];
+
+typedef struct {
+    u_int32_t state[4];			/* state (ABCD) */
+    u_int32_t count[2];			/* # of bits, modulo 2^64 (LSB 1st) */
+    unsigned char buffer[64];		/* input buffer */
+} DCC_MD5_CTX;
+
+extern void DCC_MD5Init(DCC_MD5_CTX *);
+extern void DCC_MD5Update(DCC_MD5_CTX *, const void *, u_int);
+extern void DCC_MD5Final(u_char[16], DCC_MD5_CTX *);
+
+
+#ifdef HAVE_MD5
+#include <md5.h>
+#else
+#define MD5_CTX DCC_MD5_CTX
+#define MD5Init DCC_MD5Init
+#define MD5Update DCC_MD5Update
+#define MD5Final DCC_MD5Final
+#endif
+
+
+#endif	/* DCC_MD5_H */
diff -r 000000000000 -r c7f6b056b673 include/dcc_paths.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dcc_paths.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,70 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * supplement or replace missing /usr/include/paths.h
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.5 $Revision$
+ */
+
+#ifndef DCC_PATHS_H
+#define DCC_PATHS_H
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifdef DCC_WIN32
+extern const char *_PATH_TMP;
+
+#ifndef _PATH_DEVNULL
+#define _PATH_DEVNULL "NUL"
+#endif
+
+#else /* !DCC_WIN32 */
+#ifndef _PATH_TMP
+#define _PATH_TMP "/tmp/"
+#endif
+
+#ifndef _PATH_DEVNULL
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+#ifndef _PATH_VARRUN
+#define _PATH_VARRUN "/var/run/"
+#endif
+#endif /* !DCC_WIN32 */
+
+#endif	/* DCC_PATHS_H */
diff -r 000000000000 -r c7f6b056b673 include/dcc_proto.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dcc_proto.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,612 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * client-server and server-server protocols
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.137 $Revision$
+ */
+
+#ifndef DCC_PROTO_H
+#define DCC_PROTO_H
+
+#define DCC_PKT_VERSION4    4
+#define	DCC_PKT_VERSION5    5
+#define	DCC_PKT_VERSION6    6
+#define	DCC_PKT_VERSION7    7
+#define	DCC_PKT_VERSION8    8
+#define	DCC_PKT_VERSION	    9		/* current */
+#define	DCC_PKT_VERSION_MIN	DCC_PKT_VERSION4    /* recognized */
+#define	DCC_PKT_VERSION_MAX	DCC_PKT_VERSION
+#define DCC_PKT_VERSION_MIN_VALID   DCC_PKT_VERSION_MIN	/* sanity check */
+#define	DCC_PKT_VERSION_MAX_VALID   0x7f
+
+
+#define DCC_SRVR_PORT	6277		/* default DCC server port # */
+#define DCC_GREY_PORT	6276		/* grey listing server port */
+
+
+/* types of checksums */
+typedef enum {
+    DCC_CK_INVALID	    =0,		/* deleted from database when seen */
+    DCC_CK_IP		    =1,		/* MD5 of binary source IPv6 address */
+    DCC_CK_ENV_FROM	    =2,		/*  "  "  envelope Mail From value */
+    DCC_CK_FROM		    =3,		/*  "  "  header From: line */
+    DCC_CK_SUB		    =4,		/*  "  "  substitute header line */
+    DCC_CK_MESSAGE_ID	    =5,		/*  "  "  header Message-ID: line */
+    DCC_CK_RECEIVED	    =6,		/*  "  "  last header Received: line */
+    DCC_CK_BODY		    =7,		/*  "  "  body */
+    DCC_CK_FUZ1		    =8,		/*  "  "  filtered body */
+    DCC_CK_FUZ2		    =9,		/*  "  "     "      "   */
+    DCC_CK_G_MSG_R_TOTAL    =10,
+#    define DCC_CK_GREY_MSG   DCC_CK_G_MSG_R_TOTAL
+#    define DCC_CK_REP_TOTAL  DCC_CK_G_MSG_R_TOTAL
+    DCC_CK_G_TRIPLE_R_BULK  =11,
+#    define DCC_CK_GREY3      DCC_CK_G_TRIPLE_R_BULK
+#    define DCC_CK_REP_BULK   DCC_CK_G_TRIPLE_R_BULK
+    DCC_CK_SRVR_ID	    =12,	/* hostname for server-ID check */
+    DCC_CK_ENV_TO	    =13		/* MD5 of envelope Rcpt To value */
+#    define DCC_CK_FLOD_PATH  DCC_CK_ENV_TO /* flooding path in server-IDs */
+} DCC_CK_TYPES;
+typedef u_char DCC_CK_TYPE_B;		/* big enough */
+#define DCC_CK_TYPE_FIRST   DCC_CK_IP
+#define DCC_CK_TYPE_LAST    DCC_CK_ENV_TO
+#define DCC_NUM_CKS	    DCC_CK_TYPE_LAST    /* # of valid types */
+
+/* DCC_DIM_CKS dimensions arrays of checksum types including DCC_CK_INVALID
+ * Beware that DCC_DIM_CKS is used in the database header. */
+#define DCC_DIM_CKS	    (DCC_CK_TYPE_LAST+1)
+
+/* record of path taken by a report */
+#define DCC_NUM_FLOD_PATH ((int)(sizeof(DCC_SUM)/sizeof(DCC_SRVR_ID)))
+typedef struct {
+    u_char	hi, lo;
+} DCC_FLOD_PATH_ID;
+/* DCC_MAX_FLOD_PATH assumes that among the maximum 14 possible slots for
+ * checksums in a flooded report (dimension of cks in a DB_RCD or DCC_DIM_CKS),
+ *	DCC_CK_INVALID is never used
+ *	DCC_CK_SRVR_ID is never used when any real checksums are present,
+ * is so there is always room for 2 extra DCC_CK_FLOD_PATH entries besides
+ * the natural DCC_CK_FLOD_PATH entry. */
+#define DCC_MAX_FLOD_PATH_CKSUMS 3
+/* This must not be too large lest reports become larger than DCC_DIM_CKS */
+#define DCC_MAX_FLOD_PATH (DCC_NUM_FLOD_PATH*DCC_MAX_FLOD_PATH_CKSUMS)
+
+
+/* checksums in client-server protocol */
+#define	DCC_QUERY_MAX DCC_DIM_CKS	/* even to prevent structure padding */
+
+/* Ensure that arrays of DCC_CKs contain an even number so that structures
+ * containing them will have no extra structure packing */
+#define DCC_COMP_DIM_CKS    ((((DCC_NUM_CKS+1)+1)/2)*2)	/* == DCC_DIM_CKS */
+
+/* keep some checksums in the database longer than others */
+#define DCC_CK_LONG_TERM(t) ((t) >= DCC_CK_FUZ1				\
+			     && (t) <= DCC_CK_G_TRIPLE_R_BULK)
+
+#define DCC_CK_IS_BODY(t) ((t) >= DCC_CK_BODY && (t) <= DCC_CK_FUZ2)
+
+/* MD5 of greylist msg+sender+target */
+#define DCC_CK_IS_GREY_MSG(g,t) ((g) && (t) == DCC_CK_G_MSG_R_TOTAL)
+/* MD5 of greylisted triple */
+#define DCC_CK_IS_GREY_TRIPLE(g,t) ((g) && (t) == DCC_CK_G_TRIPLE_R_BULK)
+#define DCC_CK_IS_GREY(g,t) (DCC_CK_IS_GREY_MSG(g,t)			\
+			     || DCC_CK_IS_GREY_TRIPLE(g,t))
+
+/* total messages sent by an IP address */
+#define DCC_CK_IS_REP_TOTAL_CMN(g,t) (!(g) && (t) == DCC_CK_G_MSG_R_TOTAL)
+/* bulk messages sent by an IP address */
+#define DCC_CK_IS_REP_BULK_CMN(g,t) (!(g) && (t) == DCC_CK_G_TRIPLE_R_BULK)
+#define DCC_CK_IS_REP_CMN(g,t) (DCC_CK_IS_REP_TOTAL_CMN(g,t)		\
+				|| DCC_CK_IS_REP_BULK_CMN(g,t))
+#define DCC_CK_IS_REP_TOTAL_OP(g,t) 0
+#define DCC_CK_IS_REP_BULK_OP(g,t)  0
+#define DCC_CK_IS_REP_OP(g,t)	    0
+
+
+
+typedef enum {
+    DCC_OP_INVALID	=0,
+    DCC_OP_NOP		=1,		/* see if the server is alive */
+    DCC_OP_REPORT	=2,		/* old client reporting and querying */
+    DCC_OP_QUERY	=3,		/* client querying */
+    DCC_OP_ANSWER	=4,		/* server responding */
+    DCC_OP_ADMN		=5,		/* control the server */
+    DCC_OP_OK		=6,		/* administrative operation ok */
+    DCC_OP_ERROR	=7,		/* server failing or complaining */
+    DCC_OP_DELETE	=8,		/* delete some checksums */
+    DCC_OP_GREY_REPORT	=9,		/* greylist report */
+    DCC_OP_GREY_QUERY	=10,		/*   "   "  query */
+    DCC_OP_GREY_SPAM	=11,		/* forget greylisted spammer */
+    DCC_OP_GREY_WHITE	=12		/* whitelisted greylist triple */
+} DCC_OPS;
+
+typedef u_int32_t DCC_CLNT_ID;
+#define DCC_ID_INVALID	    0
+#define DCC_ID_ANON	    1		/* anonymous (non-paying) client */
+#define DCC_ID_WHITE	    2		/* whitelisted */
+#define DCC_ID_COMP	    3		/* compressed */
+#define	DCC_ID_SRVR_SIMPLE  4		/* simple server */
+#define DCC_ID_SRVR_REP_OK  5		/* ok to use reputations */
+#define	DCC_ID_SRVR_IGNORE  6		/* ignore its flooded reports */
+#define	DCC_ID_SRVR_ROGUE   7		/* crazy server */
+#define DCC_ID_SRVR_TYPE(id) ((id) == DCC_ID_SRVR_SIMPLE		\
+			      || (id) == DCC_ID_SRVR_REP_OK		\
+			      || (id) == DCC_ID_SRVR_IGNORE		\
+			      || (id) == DCC_ID_SRVR_ROGUE)
+#define DCC_SRVR_ID_MIN	    100		/* below reserved for special uses */
+#define	DCC_SRVR_ID_MAX	    32767	/* below are servers--must be 2**n-1 */
+#define DCC_CLNT_ID_MIN	    (DCC_SRVR_ID_MAX+1)
+#define DCC_CLNT_ID_MAX	    16777215
+typedef u_int16_t DCC_SRVR_ID;
+#define	DCC_SRVR_ID_AUTH (DCC_SRVR_ID_MAX+1)	/* client was authenticated */
+
+/* client's identification of its transaction */
+typedef u_int32_t DCC_OP_NUM;
+typedef struct {
+    DCC_OP_NUM	h;			/* client host ID, e.g. IP address */
+    DCC_OP_NUM	p;			/* process ID, serial #, timestamp */
+    DCC_OP_NUM	r;			/* report ID */
+    DCC_OP_NUM	t;			/* client (re)transmission # */
+} DCC_OP_NUMS;
+
+/* The inter-DCC server flooding algorithm depends on unique-per-server
+ * timestamps to detect duplicates.  That imposes a requirement on
+ * timestamps that they have resolution enough to separate reports
+ * from clients arriving at any single server.
+ * The timestamps are 48 bits consisting of 17 bits of 8's of microseconds
+ * and 31 bits of seconds.  That's sufficient for the UNIX epoch.
+ * If the DCC is still around in the 2030's (and in the unlikely case that
+ * 8 microseconds are still fine enough), we can make the 31 bits be
+ * an offset in a bigger window.
+ */
+#define DCC_TS_US_RSHIFT    3
+#define DCC_TS_US_MULT	    (1<<DCC_TS_US_RSHIFT)
+#define DCC_TS_SECS_LSHIFT  17
+#define DCC_TS_US_MASK	    ((1<<DCC_TS_SECS_LSHIFT) - 1)
+typedef struct {
+    u_char  b[6];
+} DCC_TS;
+
+/* The start of any DCC packet.
+ *	The length and version are early, since they are the only fields
+ *	constrained in future versions. */
+typedef struct {
+    u_int16_t	len;			/* total DCC packet length */
+    u_char	pkt_vers;		/* packet protocol version */
+    u_char	op;			/* one of DCC_OPS */
+    /* Identify the transaction.
+     *	    Each client can have many hosts, each host can be multi-homed,
+     *	    and each host can be running many processes talking to the
+     *	    server.  Each packet needs to be uniquely numbered, so that the
+     *	    server can recognize as interchangable all of the (re)transmissions
+     *	    of a single report (rid) from a client process (pid) on a single
+     *	    host (hid), and the client can know which transmission (tid)
+     *	    produced a given server response to maintain the client's RTT
+     *	    value for the server. */
+    DCC_CLNT_ID	sender;			/* client-ID or server-ID */
+    DCC_OP_NUMS	op_nums;		/* op_num.t must be last */
+} DCC_HDR;
+
+typedef u_char DCC_SIGNATURE[16];
+
+typedef struct {
+    DCC_HDR	hdr;
+    DCC_SIGNATURE signature;
+} DCC_NOP;
+
+
+/* DCC_OP_ADMN administrative requests from localhost
+ *	Most of these can be changed, because the administrative tools
+ *	should match the daemon. */
+typedef enum {
+    DCC_AOP_OK		=-1,		/* never really sent */
+    DCC_AOP_STOP	= 1,		/* stop gracefully */
+    DCC_AOP_DB_UNLOAD	= 2,		/* flush cache */
+    DCC_AOP_FLOD	= 3,		/* start or stop flooding */
+    DCC_AOP_DB_CLEAN	= 4,		/* start cleaning */
+    DCC_AOP_DB_NEW	= 5,		/* finish switch to new database */
+    DCC_AOP_STATS	= 6,		/* return counters--val=buffer size */
+    DCC_AOP_STATS_CLEAR	= 7,		/* return and zero counters */
+    DCC_AOP_TRACE_ON	= 8,
+    DCC_AOP_TRACE_OFF	= 9,
+    DCC_AOP_unused1	= 10,		/*	was dcc1-1-36 clients command */
+    DCC_AOP_CLIENTS	= 11,		/* some client IP addresses */
+    DCC_AOP_CLIENTS_ID	= 12,		/* some client IDs */
+    DCC_AOP_ANON_DELAY	= 13,		/* anonymous delay parameters */
+    DCC_AOP_CLOCK_CHECK	= 14
+} DCC_AOPS;
+
+/* for DCC_AOP_FLOD */
+typedef enum {
+    DCC_AOP_FLOD_CHECK=0,
+    DCC_AOP_FLOD_SHUTDOWN,
+    DCC_AOP_FLOD_HALT,
+    DCC_AOP_FLOD_RESUME,
+    DCC_AOP_FLOD_REWIND,
+    DCC_AOP_FLOD_LIST,
+    DCC_AOP_FLOD_STATS,
+    DCC_AOP_FLOD_STATS_CLEAR,
+#    define DCC_AOP_FLOD_STATS_ID   " server-ID %u "
+    DCC_AOP_FLOD_FFWD_IN,
+    DCC_AOP_FLOD_FFWD_OUT
+} DCC_AOP_FLODS;
+
+typedef struct {			/* with operation DCC_OP_ADMN */
+    DCC_HDR	hdr;
+    int32_t	date;			/* seconds since epoch on caller */
+    u_int32_t	val1;			/* request type, buffer size, etc. */
+    u_char	aop;			/* one of DCC_AOPS */
+    u_char	val2;
+    u_char	val3;
+    u_char	val4;
+#    define	 MAX_DCC_ADMN_REQ_VAL5	64
+    u_char	val5[MAX_DCC_ADMN_REQ_VAL5];
+    DCC_SIGNATURE signature;
+} DCC_ADMN_REQ;
+#define DCC_ADMN_REQ_MIN_SIZE (ISZ(DCC_ADMN_REQ) - MAX_DCC_ADMN_REQ_VAL5)
+
+
+typedef u_char DCC_AOP_CLIENTS_CIDR[17];    /* IPv6 netmask and length */
+
+/* val2 for DCC_AOP_CLIENTS or DCC_AOP_CLIENTS_ID */
+#define DCC_AOP_CLIENTS_AVG		0x01	/* want averages */
+#define DCC_AOP_CLIENTS_VERS		0x02	/* want protocol version */
+#define DCC_AOP_CLIENTS_ANON		0x04	/* only anonymous clients */
+#define DCC_AOP_CLIENTS_NON_ANON	0x08	/* non-anonymous only */
+
+#define DCC_AOP_CLIENTS_MAX_OFFSET	((1<<24)-1)
+
+/* scale client's response buffer size in part of val1 by this */
+#define DCC_ADMIN_RESP_CLIENTS_SHIFT	5
+/* limit threshold in part of val1 */
+#define DCC_ADMIN_RESP_CLIENTS_MAX_THOLD    ((1<<16)-1)
+
+/* noisy response to DCC_AOP_CLIENTS or DCC_AOP_CLIENTS_ID */
+#define	DCC_ADMN_RESP_CLIENTS_BL	0x01	/* this client blacklisted */
+#define	DCC_ADMN_RESP_CLIENTS_IPV6	0x02	/* address is IPv6 */
+#define	DCC_ADMN_RESP_CLIENTS_SKIP	0x04	/* last_used=clients skipped */
+#define	DCC_ADMN_RESP_CLIENTS_ID1	0x08	/* suppressed ID=DCC_ID_ANON */
+#define DCC_ADMN_RESP_CLIENTS_VERS	0x10	/* protocol version # present */
+#define DCC_ADMN_RESP_CLIENTS_LAST	0x20
+#define DCC_ADMN_RESP_CLIENTS_BAD	0x40	/* naughty client */
+
+/* worst case bytes used to describe a client */
+#define DCC_ADMN_RESP_CLIENTS_MAX_SIZE	(1+1+4*4+16)
+
+#define DCC_ADMIN_RESP_MAX_CLIENTS	(10*1000)
+
+
+#ifdef DCC_PKT_VERSION6
+typedef struct {
+    u_char	clnt_id[4];
+    u_char	last_used[4];
+    u_char	requests[3];
+    u_char	nops[2];
+    u_char	flags;
+    union {
+	u_char	ipv6[16];
+	u_char	ipv4[4];
+    } addr;
+} DCC_ADMN_RESP_CLIENTSv6;
+#endif /* DCC_PKT_VERSION6 */
+
+typedef struct {
+    u_char	inflate[4];
+    u_char	delay[2];
+#    define	 DCC_ANON_DELAY_MAX	DCC_MAX_RTT
+#    define	 DCC_ANON_DELAY_FOREVER	((u_int16_t)-1)
+#    define	 DCC_NO_ANON_DELAY	((u_int16_t)-2)
+} DCC_ADMN_RESP_ANON_DELAY;
+typedef union {
+    char	string[80*22];
+    u_char	clients[1];
+#ifdef DCC_PKT_VERSION6
+    DCC_ADMN_RESP_CLIENTSv6 clientsV6[1];
+#endif
+    DCC_ADMN_RESP_ANON_DELAY anon_delay;
+} DCC_ADMN_RESP_VAL;
+typedef struct {
+    DCC_HDR	hdr;
+    DCC_ADMN_RESP_VAL val;
+    DCC_SIGNATURE signature;
+} DCC_ADMN_RESP;
+
+
+#define DCC_TRACE_ADMN_BIT  0x0001	/* administrative requests */
+#define DCC_TRACE_ANON_BIT  0x0002	/* anonymous client errors */
+#define DCC_TRACE_CLNT_BIT  0x0004	/* authenticated client errors */
+#define DCC_TRACE_RLIM_BIT  0x0008	/* rate limited messages */
+#define DCC_TRACE_QUERY_BIT 0x0010	/* all queries and reports */
+#define DCC_TRACE_RIDC_BIT  0x0020	/* RID cache messages */
+#define DCC_TRACE_FLOD_BIT  0x0040	/* general inter-server flooding */
+#define DCC_TRACE_FLOD2_BIT 0x0080	/* individual flooded reports */
+#define DCC_TRACE_IDS_BIT   0x0100	/* monitor client- and server-IDs */
+#define DCC_TRACE_BL_BIT    0x0200	/* blacklisted clients */
+#define DCC_TRACE_DB_BIT    0x0400	/* odd database events */
+#define DCC_TRACE_WLIST_BIT 0x0800	/* whitelisted checksums */
+#define DCC_TRACE_BITS  (DCC_TRACE_ADMN_BIT | DCC_TRACE_ANON_BIT	\
+			 | DCC_TRACE_CLNT_BIT | DCC_TRACE_RLIM_BIT	\
+			 | DCC_TRACE_QUERY_BIT | DCC_TRACE_RIDC_BIT	\
+			 | DCC_TRACE_FLOD_BIT | DCC_TRACE_FLOD2_BIT	\
+			 | DCC_TRACE_IDS_BIT | DCC_TRACE_BL_BIT		\
+			 | DCC_TRACE_DB_BIT | DCC_TRACE_WLIST_BIT)
+#define DCC_TRACE_ON_DEF_BITS (DCC_TRACE_ANON_BIT | DCC_TRACE_CLNT_BIT)
+#define DCC_TRACE_OFF_DEF_BITS (DCC_TRACE_BITS				\
+				& ~(DCC_TRACE_ANON_BIT | DCC_TRACE_CLNT_BIT))
+
+
+#define DCC_BRAND_MAXLEN 64
+typedef char DCC_BRAND[DCC_BRAND_MAXLEN];
+
+/* administrative or NOP ok */
+typedef struct {
+    DCC_HDR	hdr;
+    u_char	max_pkt_vers;		/* server can handle this version */
+    u_char	unused;
+    u_int16_t	qdelay_ms;
+    DCC_BRAND	brand;			/* identity or brandname of sender */
+    DCC_SIGNATURE signature;
+} DCC_OK;
+
+
+typedef u_int32_t DCC_TGTS;		/* database is limited to 24 bits */
+#define	DCC_TGTS_TOO_MANY   0x00fffff0	/* >= 16777200 targets */
+#define	DCC_TGTS_OK	    0x00fffff1	/* certified not spam */
+#define	DCC_TGTS_OK2	    0x00fffff2	/* half certified not spam */
+#define DCC_TGTS_GREY_WHITE DCC_TGTS_OK2    /* whitelisted for greylisting */
+#define	DCC_TGTS_DEL	    0x00fffff3	/* a deleted checksum */
+#define DCC_TGTS_REP_ADJ    0x00fffff4	/* scale a reputation */
+#define DCC_TGTS_OK_MX	    0x00fffff5	/* partly whitelist MX secondary */
+#define DCC_TGTS_OK_MXDCC   0x00fffff6	/*	MX secondary with DCC client */
+#define DCC_TGTS_SUBMIT_CLIENT 0x00fffff7   /* SMTP submission client */
+#define DCC_TGTS_MAX_DB	    DCC_TGTS_REP_ADJ
+#define DCC_TGTS_INVALID    0x01000000
+#define DCC_TGTS_SPAM	    DCC_TGTS_INVALID
+#define DCC_TGTS_REP_SPAM   0x02000000	/* reputation hit */
+#define DCC_TGTS_MASK	    0x00ffffff
+
+#define DCC_TGTS_RPT_MAX    2000
+#define DCC_TGTS_FLOD_RPT_MAX (DCC_TGTS_RPT_MAX*1000)
+#define BULK_THRESHOLD	    10
+#define	REFLOOD_THRESHOLD   300
+#define DCC_TGTS_REP_TOTAL_THOLD (BULK_THRESHOLD*2)
+#define DCC_TGTS_REP_TOTAL_EXPIRE BULK_THRESHOLD
+
+/* checksums kept by most servers */
+#define DCC_CK_IS_GREY_BODY(g,t) ((g) ? ((t)==DCC_CK_BODY) : DCC_CK_IS_BODY(t))
+#define DB_GLOBAL_NOKEEP(g,t) (!DCC_CK_IS_GREY_BODY(g,t)		\
+			       && (t) != DCC_CK_SRVR_ID			\
+			       && ((t) != DCC_CK_G_MSG_R_TOTAL || !(g))	\
+			       && ((t) != DCC_CK_G_TRIPLE_R_BULK || !(g)))
+
+/* checksums subject to thresholds */
+#define DCC_CK_THOLD_OK(t)  ((t) > DCC_CK_INVALID			\
+			     && (t) <= DCC_CK_G_TRIPLE_R_BULK)
+
+#define IS_ALL_CKSUM(t)	(DCC_CK_THOLD_OK(t) && !DCC_CK_IS_REP_CMN(0, t))
+#define IS_CMN_CKSUM(t)	DCC_CK_IS_BODY(t)
+
+
+/* a reported checksum from a client */
+typedef u_char DCC_SUM[16];		/* for now all have 16 bytes */
+typedef struct {
+    DCC_CK_TYPE_B type;
+    u_char	len;			/* total length of this checksum */
+    DCC_SUM	sum;
+} DCC_CK;
+
+/* most packets from client to server */
+typedef struct {
+    DCC_HDR	hdr;
+    DCC_TGTS	tgts;			/* # of addressees */
+    DCC_CK	cks[DCC_QUERY_MAX];	/* even to prevent structure padding */
+    DCC_SIGNATURE signature;
+} DCC_REPORT;
+
+/* most responses */
+typedef struct {
+    DCC_TGTS	c;			/* current value, with this report */
+    DCC_TGTS	p;			/* previous value, before this report */
+} DCC_ANSWER_BODY_CKS;
+typedef struct {
+    DCC_HDR	hdr;
+    DCC_ANSWER_BODY_CKS b[DCC_QUERY_MAX];
+    DCC_SIGNATURE signature;
+} DCC_ANSWER;
+
+#ifdef DCC_PKT_VERSION5
+typedef struct {
+    DCC_HDR	hdr;
+    DCC_TGTS	b[DCC_QUERY_MAX];	/* current values */
+    DCC_SIGNATURE signature;
+} DCC_ANSWERv5;
+#endif
+
+typedef struct {
+    DCC_HDR	hdr;
+    DCC_TGTS	msg;
+    DCC_TGTS	triple;
+    DCC_SIGNATURE signature;
+} DCC_GREY_ANSWER;
+
+
+/* DCC_OP_DELETE request to delete checksums */
+typedef struct {
+    DCC_HDR	hdr;
+    int32_t	date;			/* seconds since epoch on client */
+    DCC_CK	ck;
+    u_char	pad[2];
+    DCC_SIGNATURE signature;
+} DCC_DELETE;
+
+
+/* DCC_OP_GREY_SPAM restore greylist embargo */
+typedef struct {
+    DCC_HDR	hdr;
+    DCC_CK	ip;
+    DCC_CK	msg;
+    DCC_CK	triple;
+    DCC_SIGNATURE signature;
+} DCC_GREY_SPAM;
+
+
+/* error response from server to client */
+typedef struct {
+    DCC_HDR	hdr;
+#    define	 DCC_ERROR_MSG_LEN  128
+    char	msg[DCC_ERROR_MSG_LEN];
+    DCC_SIGNATURE signature;
+} DCC_ERROR;
+
+
+typedef union {
+    DCC_HDR	hdr;
+    DCC_ANSWER	ans;
+#ifdef DCC_PKT_VERSION5
+    DCC_ANSWERv5 ans5;
+#endif
+    DCC_GREY_ANSWER gans;
+    DCC_OK	ok;
+    DCC_ERROR	error;
+    DCC_ADMN_RESP resp;
+    int		w[6];
+} DCC_OP_RESP;
+
+
+
+
+/* ************** server-to-server flooding protocol ************ */
+
+/* This protocol is the sort of botch of a mess that results from assuming
+ * a considered protocol is not necessary and then extending it. */
+
+/* A flooding connection starts with the TCP client or connection originator
+ * sending an ASCII "magic" string including a version number, the connection
+ * originator's server-ID, whether SOCKS is involved, and a cryptographic
+ * hash of the fixed-length message.  In the common case, the connection
+ * orginator will send a flood of DCC reports.  If SOCKS is involved, the
+ * connection is immediately turned around. */
+#define DCC_FLOD_VERSION_STR_BASE	"DCC flod version "
+#define DCC_FLOD_VERSION7		7
+/* in the next version, add a length to DCC_FLOD_END */
+#define DCC_FLOD_VERSION7_STR		DCC_FLOD_VERSION_STR_BASE"7"
+#define DCC_FLOD_VERSION_DEF		0
+#define DCC_FLOD_VERSION_CUR		DCC_FLOD_VERSION7
+#define DCC_FLOD_VERSION_CUR_STR	DCC_FLOD_VERSION7_STR
+typedef struct {
+#    define DCC_FLOD_VERSION_STR_LEN 64
+    char	str[DCC_FLOD_VERSION_STR_LEN];
+    u_char	sender_srvr_id[sizeof(DCC_SRVR_ID)];
+    u_char	turn;			/* 1=turn connection around for SOCKS */
+    u_char	unused[3];
+} DCC_FLOD_VERSION_BODY;
+typedef struct {
+    DCC_FLOD_VERSION_BODY body;
+    char	pad[256-sizeof(DCC_FLOD_VERSION_BODY)-sizeof(DCC_SIGNATURE)];
+    DCC_SIGNATURE signature;
+} DCC_FLOD_VERSION_HDR;
+
+
+/* flood sender's position or serial number
+ *	Only the sender understands sender positions except for these
+ *	special values.  However, the special values imply that the position
+ *	must be big endian. */
+typedef u_char DCC_FLOD_POS[8];
+/* special cases sent by the receiver back to the sender */
+typedef enum {
+    DCC_FLOD_POS_END	    =0,		/* receiver closing with message */
+    DCC_FLOD_POS_END_REQ    =1,		/* receiver wants to stop */
+    DCC_FLOD_POS_NOTE	    =2,		/* receiver has a tracing message */
+    DCC_FLOD_POS_COMPLAINT  =3,		/* receiver has a problem message */
+    DCC_FLOD_POS_REWIND	    =4,		/* receiver's database emptied */
+    DCC_FLOD_POS_FFWD_IN    =5		/* receiver wants fast-forward */
+} DCC_FLOD_POS_OPS;
+#define DCC_FLOD_POS_MIN    10
+
+#define DCC_FLOD_OK_STR	    "DCC flod ok: "
+
+/* final result sent from flood receiver to flood sender
+ *	This structure always ends the TCP stream.  The length of the msg is
+ *	obtain from the number of buts to the end of the stream.  It should
+ *	have had an explicit length in the structure.  */
+typedef struct {
+    DCC_FLOD_POS pos;			/* one of DCC_FLOD_POS_* */
+#    define DCC_FLOD_MAX_RESP   200
+    char	msg[DCC_FLOD_MAX_RESP];	/* no '\0'; uses length to EOF */
+} DCC_FLOD_END;
+#define FLOD_END_OVHD ISZ(((DCC_FLOD_END*)0)->pos)
+
+/* report forwarded among servers */
+typedef struct {
+    DCC_FLOD_POS pos;
+    u_char	tgts[sizeof(DCC_TGTS)];
+    u_char	srvr_id_auth[sizeof(DCC_SRVR_ID)];  /* receiving server */
+    DCC_TS	ts;			/* date reported */
+    u_char	num_cks;
+    DCC_CK	cks[DCC_QUERY_MAX];
+} DCC_FLOD_RPT;
+#define DCC_FLOD_RPT_LEN(n) (ISZ(DCC_FLOD_RPT) - ISZ(DCC_CK)*DCC_QUERY_MAX  \
+			     + ISZ(DCC_CK)*(n))
+
+/* what can appear in the stream of flooded reports */
+typedef union {
+    DCC_FLOD_VERSION_HDR v;
+    DCC_FLOD_END    e;
+    DCC_FLOD_RPT    r;
+} DCC_FLOD_STREAM;
+
+
+/* a response to flooded reports */
+typedef union {
+    DCC_FLOD_POS    pos;
+    DCC_FLOD_END    end;		/* final result */
+    struct {
+	DCC_FLOD_POS    op;		/* one of DCC_FLOD_POS_* */
+	u_char		len;		/* total length */
+	char		str[DCC_FLOD_MAX_RESP]; /* includes trailing '\0' */
+    } note;
+} DCC_FLOD_RESP;
+#define FLOD_NOTE_OVHD (ISZ(((DCC_FLOD_RESP*)0)->note) - DCC_FLOD_MAX_RESP)
+
+
+/* parts of error messages sent between flooding peers */
+#define DCC_FLOD_BAD_VER_MSG	"unrecognized flod version"
+#define DCC_FLOD_BAD_ID_MSG	"unauthorized ID"
+#define DCC_FLOD_BAD_AUTH_MSG	"bad authentication for ID"
+#define DCC_FLOD_PASSWD_ID_MSG	"unknown passwd-ID"
+
+
+#endif /* DCC_PROTO_H	*/
diff -r 000000000000 -r c7f6b056b673 include/dcc_xhdr.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dcc_xhdr.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,142 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * X-DCC-Warning header definitions
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.66 $Revision$
+ */
+
+#ifndef DCC_XHDR_H
+#define DCC_XHDR_H
+
+/* the DCC SMTP X- header is of the form
+ *
+ * X-DCC-name-Metrics: host_addr server-ID; [bulk] cknm1=cnt1 cknm2=cnt2 ...
+ *
+ * where
+ *    chost		is the FQDN of the DCC client that added the header
+ *    server-ID		is the ID of the DCC server providing the counts
+ *    cknm1, cknm2, ...	are names of checksums
+ *    cnt1, cnt2, ...	are counts or special counts
+ */
+#define DCC_XHDR_START		"X-DCC-"
+#define DCC_XHDR_END		"-Metrics"
+#define DCC_XHDR_PAT		DCC_XHDR_START"%.*s"DCC_XHDR_END
+
+/* minimum length of X-DCC field name */
+#define DCC_XHDR_WHITELIST_FNAME_LEN	LITZ(DCC_XHDR_START DCC_XHDR_END)
+
+#define DCC_XHDR_BULK		"bulk"
+#define DCC_XHDR_BULK_REP	"bulk rep"
+#define	DCC_XHDR_WHITELIST	"whitelist"
+
+#define	DCC_XHDR_REPORTED	"reported:" /* header for block of checksums */
+
+/* names of checksums */
+#define DCC_XHDR_TYPE_IP	    "IP"	    /* binary source IP addr */
+#define DCC_XHDR_TYPE_ENV_FROM	    "env_From"	    /* envelope Mail From */
+#define DCC_XHDR_TYPE_FROM	    "From"	    /* header line */
+#define DCC_XHDR_TYPE_SUB	    "substitute"    /* random header line */
+#define DCC_XHDR_TYPE_MESSAGE_ID    "Message-ID"    /* header line */
+#define DCC_XHDR_TYPE_RECEIVED	    "Received"	    /* last header line */
+#define DCC_XHDR_TYPE_BODY	    "Body"	    /* body */
+#define DCC_XHDR_TYPE_FUZ1	    "Fuz1"	    /* filtered body */
+#define DCC_XHDR_TYPE_FUZ2	    "Fuz2"	    /*   "       "   */
+#define DCC_XHDR_TYPE_GREY_MSG	    "Grey_Msg"	    /* greylist msg checksum */
+#define DCC_XHDR_TYPE_GREY_TRIPLE   "Grey3"	    /* greylist triple cksum */
+#define DCC_XHDR_TYPE_REP_TOTAL	    "rep-total"
+#define DCC_XHDR_TYPE_REP_BULK	    "rep"
+#define DCC_XHDR_TYPE_SRVR_ID	    "server-ID"
+#define DCC_XHDR_TYPE_ENV_TO	    "env_To"	    /* envelope Rcpt To */
+#define DCC_XHDR_TYPE_FLOD_PATH	    "flood path"
+#define DCC_XHDR_MAX_TYPE_LEN  (ISZ("unknown xxx"))
+
+/* special target values */
+#define DCC_XHDR_TOO_MANY	"many"	    /* too many targets to number */
+#define DCC_XHDR_THOLD_NEVER	"never"
+#define DCC_XHDR_GREY_PASS	"pass"	    /* embargo has ended */
+#define DCC_XHDR_OK		"ok"	    /* certified not spam */
+#define DCC_XHDR_OK2		"ok2"	    /* half certified not spam */
+#define DCC_XHDR_OK_MX		"mx"	    /* semi-trusted MX server */
+#define DCC_XHDR_OK_MXDCC	"mxdcc"	    /* semi-trusted MX server w/DCC */
+#define DCC_XHDR_SUBMIT_CLIENT	"submit"    /* SMTP submission client */
+#define DCC_XHDR_DEL		"del-req"   /* delete request */
+#define DCC_XHDR_TGTS_REP_ADJ	"rep-adj"   /* adjust reputation total */
+#define DCC_XHDR_INVALID	"-"
+#define DCC_XHDR_MAX_TGTS_LEN	12
+
+/* special server-IDs */
+#define DCC_XHDR_ID_INVALID	"invalid"	/* DCC_ID_INVALID */
+#define DCC_XHDR_ID_ANON	"anon"		/* DCC_ID_ANON */
+#define DCC_XHDR_ID_WHITE   DCC_XHDR_WHITELIST	/* DCC_ID_WHITE */
+#define DCC_XHDR_ID_COMP	"compressed"	/* DCC_ID_COMP */
+#define DCC_XHDR_ID_DELETED	"deleted"	/* DCC_ID_DELETED */
+#define DCC_XHDR_ID_SIMPLE	"simple"	/* DCC_ID_SRVR_SIMPLE */
+#define DCC_XHDR_ID_REP_OK	"commercial"    /* DCC_ID_SRVR_REP_OK */
+#define DCC_XHDR_ID_IGNORE	"ignore"	/* DCC_ID_SRVR_IGNORE */
+#define DCC_XHDR_ID_ROGUE	"rogue"		/* DCC_ID_SRVR_ROGUE */
+
+#define	DCC_XHDR_REJ_DATA_MSG	"rejection message: "
+#define DCC_XHDR_RESULT		"result: "
+#define DCC_XHDR_RESULT_I_A	"ignore and accept"
+#define	DCC_XHDR_RESULT_A_GREY	"after greylist embargo"
+#define	DCC_XHDR_RESULT_DISCARD	"discard"
+#define	DCC_XHDR_RESULT_ACCEPT	"accept"
+#define	DCC_XHDR_RESULT_REJECT	"reject"
+#define DCC_XHDR_RESULT_FORCED	" forced by other recipients"
+#define DCC_XHDR_RESULT_DCC_FAIL "DCC failure"
+#define	DCC_XHDR_REJ_RCPT_MSG	"Rcpt To rejected with: "
+#define DCC_XHDR_INCOMPAT_WLIST	"reject temporarily for incompatible whitelists"
+#define DCC_XHDR_MTA_REJECTION	"MTA rejection"
+#define DCC_XHDR_TOO_MANY_RCPTS	"reject temporarily for too many recipients"
+
+#define DCC_XHDR_ISOK		"-->OK"
+#define DCC_XHDR_ISSPAM		"-->spam"
+
+#define DCC_XHDR_TRAP_ACC	"spam-trap-accept"
+#define DCC_XHDR_TRAP_REJ	"spam-trap-reject"
+
+/* greylist embargo results that are recognized by other programs */
+#define DCC_XHDR_GREY_RECIP	"greylist recipient"
+#define DCC_XHDR_EMBARGO_FAIL	"Sys Failure"
+#define DCC_XHDR_EMBARGO_ENDED	"Embargo Ended"
+#define DCC_XHDR_EMBARGO	"Embargo"
+#define DCC_XHDR_EMBARGO_NUM	"Embargo #%d"
+#define DCC_XHDR_EMBARGO_PASS	"Pass"
+#define DCC_XHDR_EMBARGO_OK	"Recognized OK"
+#define DCC_XHDR_EMBARGO_RESET	"Embargo #%d reset"
+
+#endif /* DCC_XHDR_H	*/
diff -r 000000000000 -r c7f6b056b673 include/dccif.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/dccif.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,142 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * external definitions for DCC interface daemon
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.26 $Revision$
+ */
+
+#ifndef DCCIF_H
+#define DCCIF_H
+
+#include "dcc_defs.h"
+
+/* The DCC interface daemon is intended for use with programs that
+ * need a more efficient DCC client than dccproc.  The dccif daemon
+ * receives messages containing SMTP messages including their envelopes,
+ * checks the messages' checksums with a DCC server, and answers
+ * with indications of what to do with the mail messages.
+ *
+ * The protocol used by dccif is simple and easily implemented even
+ * in shell scripts.  The protocol uses ASCII strings delimited by '\n'
+ * despite their reduced efficiency, because strings are quicker and
+ * easier to handle in scripts.  There are Perl and C implementations of the
+ * interface that are useful in common situations.
+ *
+ * The interface daemon is given
+ *	optional information including whether the message is spam: */
+#define DCCIF_OPT_LOG	    "log"	/* log the transaction */
+#define DCCIF_OPT_SPAM	    "spam"	/* message is known to be spam */
+#define DCCIF_OPT_BODY	    "body"	/* daemon should return the body */
+#define DCCIF_OPT_HEADER    "header"    /* return only the header */
+#define DCCIF_OPT_QUERY	    "query"	/* `dccifd -Q` for this message */
+#define DCCIF_OPT_GREY_QUERY "grey-query"   /* `dccifd -Gquery` */
+#define DCCIF_OPT_NO_REJECT "no-reject" /* ignore DCC result; greylist only */
+#define DCCIF_OPT_RCVD_NXT  "rcvd-nxt"	/* parse next Received: header */
+#define DCCIF_OPT_RCVD_NEXT "rcvd-next"	/*	confused duplicate */
+/*
+ *	optional IP address as an ASCII string
+ *	    optionally followed by '\r' and the name of the
+ *	    SMTP client.  The IP address must be present if the name is.
+ *	    If neither is present, the IP address is sought in the first
+ *	    Received header.
+ *	the HELO value without ESMTP options,
+ *	the complete sender without ESMTP options,
+ *	the complete recipient strings without ESMTP options but with
+ *	    optional local user name for per-user logging.  The local user
+ *	    name follows a '\r' character after the SMTP Rcpt_To value,
+ *	    Only something like an MTA can decide whether user@domain
+ *	    example.com, user@example.com, and user@domain.com are the same
+ *	    or different mailboxes and so should have one, two, or three
+ *	    per-user logs.  Without a local user name, dccifd can do no per-user
+ *	    logging or whitelisting.
+ *	    With no recipients, dccifd will assume a query is intended.
+ *	and the body of the message or text after the DATA command
+ *	    up to but not including the period, with any escaping
+ *	    of leading periods removed. */
+
+/* The daemon responds with
+ *	a line that is the overall result: */
+typedef enum {
+    DCCIF_RESULT_OK	= 'A',		/* accept for all recipients */
+    DCCIF_RESULT_GREY	= 'G',		/* greylist temporary rejection */
+    DCCIF_RESULT_REJECT = 'R',		/* reject the message */
+    DCCIF_RESULT_SOME   = 'S',		/* no longer used */
+    DCCIF_RESULT_TEMP   = 'T'		/* temporary failure by DCC system */
+} DCCIF_RESULT_CHAR;
+/*
+ *	followed by a line of characters indicating which recipients should
+ *	    receive the message */
+#define DCCIF_RCPT_ACCEPT   'A'
+#define DCCIF_RCPT_REJECT   'R'
+#define DCCIF_RCPT_GREY	    'G'
+/*
+ *	followed by the body, usually with an added X-DCC header and any
+ *	    spurious copies of the X-DCC header removed.
+ *	    MTAs that need a copy of the body must say so with the
+ *	    DCCIF_OPT_BODY string among the options in the first line.
+ */
+
+
+
+/* sample C interface routine */
+
+#define DCC_DCCIF_UDS	    "dccifd"	/* name of UNIX domain socket */
+
+/* Put parameters for the daemon into the dcc_conf file in home directory. */
+#define DCC_START_DCCIFD "start-dccifd"  /* script to (re)start daemon */
+
+typedef struct dccif_rcpt {
+    struct dccif_rcpt *next;
+    const char *addr;			/* SMTP Rcpt_To value */
+    const char *user;			/* local user name */
+    u_char	ok;			/* 0=do not receive for this one */
+} DCCIF_RCPT;				/* chain of target addresses */
+
+extern u_char dccif(DCC_EMSG,		/* put error message here */
+		    int,		/* -1 or write body to here */
+		    char **,		/* 0 or pointer to resulting body */
+		    const char *,	/* string of options */
+		    const DCC_SOCKU *,	/* SMTP client IPv4 or IPv6 address */
+		    const char *,	/* optional client name */
+		    const char *,	/* HELO string */
+		    const char *,	/* envelope sender or Mail_from value */
+		    DCCIF_RCPT *,	/* envelope recipients */
+		    int,		/* -1 or read body from here */
+		    const char *,	/* incoming body */
+		    const char *);	/* home directory */
+
+#endif /* DCCIF_H */
diff -r 000000000000 -r c7f6b056b673 include/helper.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/helper.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,129 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * DNS blacklist and external filter definitions
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.33 $Revision$
+ */
+
+#ifndef HELPER_H
+#define HELPER_H
+
+#include "dcc_ck.h"
+
+#define HELPER_IDLE_STOP_SECS	(10*60)	/* helpers die of this much boredom */
+#define HELPER_IDLE_RESTART	(HELPER_IDLE_STOP_SECS - 30)
+#define HELPER_AUTO_REAP	(HELPER_IDLE_STOP_SECS / 20)
+
+#define HELPER_PAT "helper=%d,%d,%d"
+
+
+typedef struct {
+    u_int	sn;			/* serial # of parent */
+    u_int	gen;			/* generation of children */
+    u_int	failures;		/* failures in this generation */
+    int		pipe_write;
+    int		pipe_read;
+    /* save the socket and its port number obtained for the first child
+     * to give to later children */
+    SOCKET	soc;
+    DCC_SOCKU	su;
+    int		req_len;
+    int		argc;
+    int		free_args;
+    char const	**argv;
+    pid_t	*pids;
+    int		total_helpers;
+    int		max_helpers;
+    int		idle_helpers;
+    int		slow_helpers;		/* hung or at least slow helpers */
+    int		debug;
+    time_t	idle_restart;		/* restart helpers after then */
+    u_char	is_child;
+} HELPER;
+
+extern HELPER helper;
+
+
+typedef struct {
+    u_int	version;
+    u_int	magic;
+    u_int	sn;
+    struct timeval start;		/* when job started */
+    time_t	avail_us;		/* microseconds available for job */
+    char	id[DCC_MSG_ID_LEN+1];
+} HELPER_REQ_HDR;
+
+typedef struct {
+    u_int	version;
+    u_int	magic;
+    u_int	sn;
+} HELPER_RESP_HDR;
+
+#define HELPER_VERSION	    0x10
+#define HELPER_MAGIC_REQ    0xbeefdead
+#define HELPER_MAGIC_RESP   0xdeadbeef
+
+typedef struct {
+    HELPER_REQ_HDR hdr;
+    DNSBL_FGS	fg;			/* what to look for */
+    DNSBL_UNHIT	unhit;			/* groups of DNSBLs not still yet hit */
+    DNSBL_FGS	fgs[MAX_DNSBL_GROUPS];
+    DNSBL_TGT	tgt;
+} DNSBL_REQ;
+
+typedef struct {
+    DNSBL_FGS	fgs;			/* what was found */
+    int		bl_num;			/* # of DNSBL hit */
+    char	result[DCC_SU2STR_SIZE];    /* IP address found in DNSBL */
+    DNSBL_DOM	tgt;			/* name or address sought in DNSBL */
+    DNSBL_DOM	probe;			/* what was actually looked up */
+} DNSBL_RESP_GROUP;
+typedef struct {
+    HELPER_RESP_HDR hdr;
+    DNSBL_UNHIT	unhit;			/* groups of DNSBLs not yet hit */
+    DNSBL_RESP_GROUP groups[MAX_DNSBL_GROUPS];
+} DNSBL_RESP;
+
+
+extern void NRATTRIB helper_child(SOCKET, int, int);
+extern void reap_helpers(u_char);
+extern u_char ask_helper(DCC_CLNT_CTXT *, void *, time_t,
+			 HELPER_REQ_HDR *, int, HELPER_RESP_HDR *, int);
+
+extern u_char dnsbl_work(const DNSBL_REQ *, DNSBL_RESP *);
+
+#endif /* HELPER_H */
diff -r 000000000000 -r c7f6b056b673 include/kludge.h.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/kludge.h.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,58 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * local definitions included via `./configure --with-kludge=FILE`
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.4 $Revision$
+ * @configure_input@
+ */
+
+#ifndef KLUDGE_H
+#define KLUDGE_H
+
+/* The #include line must be generated by the ./configure script.
+ * If it were in dcc_defs.h, then dcc_defs.h would have to be generated
+ *	from dcc_defs.h.in
+ * The tarball would still need a dcc_defs.h file for Windows.
+ * Maintaining and shipping both dcc_defs.h.in and dcc_def.h is a proven
+ *	recipe for mistakes, particularly in updating the version number.
+ */
+
+#ifdef NEED_KLUDGE_H
+#include "@kludge_h@"
+#endif
+
+#endif /* KLUDGE_H */
diff -r 000000000000 -r c7f6b056b673 include/sendmail-sysexits.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/sendmail-sysexits.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,130 @@
+/*
+ * even some UNIX systems have ancient, unusable versions of sysexits.h
+ *	Rhyolite Software DCC 1.3.103-1.5 $Revision$
+ *
+ * Copyright (c) 1987, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *	@(#)sysexits.h	8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef	SENDMAIL_SYSEXITS_H
+#define	SENDMAIL_SYSEXITS_H
+
+/*
+ *  SYSEXITS.H -- Exit status codes for system programs.
+ *
+ *	This include file attempts to categorize possible error
+ *	exit statuses for system programs, notably delivermail
+ *	and the Berkeley network.
+ *
+ *	Error numbers begin at EX__BASE to reduce the possibility of
+ *	clashing with other exit statuses that random programs may
+ *	already return.  The meaning of the codes is approximately
+ *	as follows:
+ *
+ *	EX_USAGE -- The command was used incorrectly, e.g., with
+ *		the wrong number of arguments, a bad flag, a bad
+ *		syntax in a parameter, or whatever.
+ *	EX_DATAERR -- The input data was incorrect in some way.
+ *		This should only be used for user's data & not
+ *		system files.
+ *	EX_NOINPUT -- An input file (not a system file) did not
+ *		exist or was not readable.  This could also include
+ *		errors like "No message" to a mailer (if it cared
+ *		to catch it).
+ *	EX_NOUSER -- The user specified did not exist.  This might
+ *		be used for mail addresses or remote logins.
+ *	EX_NOHOST -- The host specified did not exist.  This is used
+ *		in mail addresses or network requests.
+ *	EX_UNAVAILABLE -- A service is unavailable.  This can occur
+ *		if a support program or file does not exist.  This
+ *		can also be used as a catchall message when something
+ *		you wanted to do doesn't work, but you don't know
+ *		why.
+ *	EX_SOFTWARE -- An internal software error has been detected.
+ *		This should be limited to non-operating system related
+ *		errors as possible.
+ *	EX_OSERR -- An operating system error has been detected.
+ *		This is intended to be used for such things as "cannot
+ *		fork", "cannot create pipe", or the like.  It includes
+ *		things like getuid returning a user that does not
+ *		exist in the passwd file.
+ *	EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp,
+ *		etc.) does not exist, cannot be opened, or has some
+ *		sort of error (e.g., syntax error).
+ *	EX_CANTCREAT -- A (user specified) output file cannot be
+ *		created.
+ *	EX_IOERR -- An error occurred while doing I/O on some file.
+ *	EX_TEMPFAIL -- temporary failure, indicating something that
+ *		is not really an error.  In sendmail, this means
+ *		that a mailer (e.g.) could not create a connection,
+ *		and the request should be reattempted later.
+ *	EX_PROTOCOL -- the remote system returned something that
+ *		was "not possible" during a protocol exchange.
+ *	EX_NOPERM -- You did not have sufficient permission to
+ *		perform the operation.  This is not intended for
+ *		file system problems, which should use NOINPUT or
+ *		CANTCREAT, but rather for higher level permissions.
+ */
+
+#define EX_OK		0	/* successful termination */
+
+#define EX__BASE	64	/* base value for error messages */
+
+#define EX_USAGE	64	/* command line usage error */
+#define EX_DATAERR	65	/* data format error */
+#define EX_NOINPUT	66	/* cannot open input */
+#define EX_NOUSER	67	/* addressee unknown */
+#define EX_NOHOST	68	/* host name unknown */
+#define EX_UNAVAILABLE	69	/* service unavailable */
+#define EX_SOFTWARE	70	/* internal software error */
+#define EX_OSERR	71	/* system error (e.g., can't fork) */
+#define EX_OSFILE	72	/* critical OS file missing */
+#define EX_CANTCREAT	73	/* can't create (user) output file */
+#define EX_IOERR	74	/* input/output error */
+#define EX_TEMPFAIL	75	/* temp failure; user is invited to retry */
+#define EX_PROTOCOL	76	/* remote error in protocol */
+#define EX_NOPERM	77	/* permission denied */
+#define EX_CONFIG	78	/* configuration error */
+
+#define EX__MAX	78	/* maximum listed value */
+
+
+
+/*  *********************************************************************
+ *	DCC additions */
+
+/* fix scripts that assume EX_DCC_RESTART=79 if this changes */
+#define EX_DCC_RESTART	    (EX__MAX+1)	/* restart dccm, dccifd, or dccd */
+
+
+#endif /* !SENDMAIL_SYSEXITS_H */
diff -r 000000000000 -r c7f6b056b673 misc/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,30 @@
+Makefile.in
+README
+cron-dccd.in
+crontab.in
+dcc-stats-collect.in
+dcc-stats-graph.in
+dcc-stats-init.in
+dcc.m4.in
+dccdnsbl.m4
+dccman2html
+fetch-testmsg-whitelist.in
+fetchblack.in
+fetchids.in
+hackmc
+list-clients.in
+logger
+man2html
+newwebuser.in
+rcDCC.in
+site.config.m4
+start-dccd.in
+start-dccifd.in
+start-dccm.in
+start-grey.in
+stats-get.in
+stop-dccd.in
+uninstalldcc.in
+updatedcc.in
+wlist.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 misc/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,56 @@
+# install the Distributed Checksum Clearinghouse miscellaneous files
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.38 $Revision$
+# @configure_input@
+
+DEPTH	=..
+PROG	=wlist
+SRCS	=$(PROG).c
+SSCRIPTS=cron-dccd fetchblack fetch-testmsg-whitelist fetchids hackmc	\
+	list-clients logger newwebuser rcDCC start-dccd start-grey	\
+	start-dccifd start-dccm stop-dccd dcc-stats-collect stats-get	\
+	dcc-stats-graph dcc-stats-init updatedcc uninstalldcc
+
+DCC_BINDIR=@installroot@@libexecdir@
+@MAKE_PROG@
+@MAKE_INC2@
+
+install:
+	for NM in $(SSCRIPTS); do $(BININSTALL) $$NM $(DCC_BINDIR)/$$NM;done
+
+deinstall:
+	cd $(DCC_BINDIR); rm -f $(SSCRIPTS)
diff -r 000000000000 -r c7f6b056b673 misc/README
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/README	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,52 @@
+Sample sendmail configuration files and scripts
+
+    Note that several of these files do not exist until they have been
+    built with the `configure` script their .in prototypes.
+
+    start-dccm		start dccm
+			    Installed in libexec in the DCC home directory.
+
+    start-dccd		start dccd
+			    Installed in libexec in the DCC home directory.
+
+    rcDCC		rc.d script for starting all DCC daemons.  It can be
+			    linked into rc*.d directories on Solaris and Linux.
+			    It can be called by /etc/rc.local on older BSD
+			    systems and linked into /usr/local/etc/rc.d on
+			    newer BSD systems.
+
+    cron-dccd		run dbclean and delete log files daily
+			    Installed in libexec in the DCC home directory.
+
+    crontab		example cron entry
+
+    dcc.m4		DCC sendmail "FEATURE" file for cf/feature directory
+
+    dccdnsbl.m4		DCC sendmail "FEATURE" file to connect a DNS black-list
+			    to DCC
+
+    hackmc		modify sendmail.cf generated from .mc file to report
+			    spam detected by sendmail using the "access_db"
+			    and "blacklist_recipients" features and
+			    unauthorized relaying to the DCC.  Lines in the
+			    access_db database should be of the form
+				DCC: "DISCARD spammer cyberpromo.com"
+			    or
+				DCC: "spammer cyberpromo.com"
+			    to "discard" or "reject," respectively.
+
+    site.config.m4      build milter in sendmail
+
+    wlist.c		dump whitelist hash tables
+
+    man2html		slightly modified Perl script
+
+    newwebuser		create per-user directories and white list files,
+			optionally from a prototype.
+			The required argument should be the "dir" value
+			from a dccm log file.
+			BEWARE that this script does not participate in
+			the locking done by the chgpasswd CGI script.
+
+
+	Rhyolite Software DCC 1.3.103-1.15 $Revision$
diff -r 000000000000 -r c7f6b056b673 misc/cron-dccd.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/cron-dccd.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,268 @@
+#! /bin/sh
+
+# daily DCC cron job
+
+# This script should be run daily or more often when there is a shortage
+#   of disk space to run dbclean to discard and compress old checksums.
+#   It also discards old DCC client log files.
+
+#.  By default it is installed in @libexecdir@.  Instead of being copied
+#   to a directory such as /etc/cron/daily on some systems, a symbolic link
+#   should be used.
+
+# cron tab entry like this can be used:
+# 15	2	*	*	*	@libexecdir@/cron-dccd
+#   It is best to choose different times for each of your DCC servers so
+#   that your servers are not all busy cleaning their databases at once.
+
+#   -x	    turn on debugging
+#   -F	    do not follow symbolic links because they are used to share
+#		per-user directories
+#   -h dir  override DCC home directory @prefix@
+#   -a args for dbclean in addition to DBCLEAN_ARGS in @prefix@/dcc_conf
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.81 $Revision$
+#	@configure_input@
+
+exec 1>&2 </dev/null
+# English messages so grep can suppress them;
+#   simple collating sequence for sort
+#   sane gcc error messages
+LC_ALL=C; export LC_ALL
+
+
+LOGGER_TAG=cron-dccd
+DCC_LOGGER="@DCC_LOGGER@"
+DCC_HOMEDIR=@prefix@
+DEBUG=
+QUIET=-q
+FOLLOW=-follow
+# check the args once to get the home directory
+while getopts ":xFh:a:" c; do
+    case $c in
+	x) set -x; DEBUG=-x; QUIET=;;
+	h) DCC_HOMEDIR="$OPTARG";;
+	*) ;;
+    esac
+done
+. $DCC_HOMEDIR/dcc_conf
+# deal with bash reserved $UID and old versions of dcc_conf
+if test 0"$DCC_CONF_VERSION" -lt 2 -a -z "$DCCUID" -a -n "$UID"; then
+    DCCUID="$UID"
+fi
+
+USAGE="`basename $0`: [-xF] [-h homedir] [-a args]"
+OPTIND=1
+while getopts "xFh:a:" c; do
+    case $c in
+	x) ;;
+	F) FOLLOW=;;
+	h) ;;
+	a) DBCLEAN_ARGS="$DBCLEAN_ARGS $OPTARG";;
+	*) eval $DCC_LOGGER "$USAGE"; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    eval $DCC_LOGGER "$USAGE"
+    exit 1
+fi
+
+DCCD_GETOPTS="64dVbfFQi:n:h:a:I:q:G:t:W:K:T:u:C:L:R:"
+DBCLEAN_GETOPTS="64dfFNRPSVqWi:a:h:G:s:e:E:t:L:"
+
+DBCLEAN_ARGS="$QUIET -h $DCC_HOMEDIR $DCC_LOG_ARGS $DBCLEAN_ARGS"
+
+# remove -e, -E, and -t from args for `dbclean -Gon`
+set -f
+GREY_DBCLEAN_ARGS=
+OPTIND=1
+while getopts "$DBCLEAN_GETOPTS" c $DBCLEAN_ARGS; do
+    case $c in
+	[eEt:?]) ;;
+	*) GREY_DBCLEAN_ARGS="$GREY_DBCLEAN_ARGS -$c $OPTARG";;
+    esac
+done
+
+# find addresses that dccd is using
+ADDR=
+OPTIND=1
+while getopts "$DCCD_GETOPTS" c $DCCD_ARGS; do
+    case $c in
+	[46a]) ADDR="$ADDR -$c $OPTARG";;
+	*) ;;
+    esac
+done
+DBCLEAN_ARGS="$ADDR $DBCLEAN_ARGS"
+
+ADDR=
+OPTIND=1
+while getopts "$DCCD_GETOPTS" c $GREY_DCCD_ARGS; do
+    case $c in
+	[46a]) ADDR="$ADDR -$c $OPTARG";;
+	*) ;;
+    esac
+done
+GREY_DBCLEAN_ARGS="$ADDR $GREY_DBCLEAN_ARGS"
+set +f
+
+
+# make the paths absolute and trim the per day/hour/minute business
+DCCM_LOGDIR=`echo $DCCM_LOGDIR						\
+	| sed -e 's@["'"']*@@g" -e "s@[DHM]?@@" -e "s@^[^/]@$DCC_HOMEDIR/&@"`
+DCCM_USERDIRS=`echo $DCCM_USERDIRS					\
+	| sed -e "s@^[^/]@$DCC_HOMEDIR/&@"`
+DCCIFD_LOGDIR=`echo $DCCIFD_LOGDIR					\
+	| sed -e 's@["'"']*@@g" -e "s@[DHM]?@@" -e "s@^[^/]@$DCC_HOMEDIR/&@"`
+DCCIFD_USERDIRS=`echo $DCCIFD_USERDIRS					\
+	| sed -e "s@^[^/]@$DCC_HOMEDIR/&@"`
+LOGDIRS=
+if test -n "$DCCM_LOGDIR" -a -d "$DCCM_LOGDIR"; then
+    LOGDIRS="$DCCM_LOGDIR"
+fi
+if test "$LOGDIRS" != "$DCCIFD_LOGDIR" -a -n "$DCCIFD_LOGDIR"		\
+	-a -d "$DCCIFD_LOGDIR"; then
+    LOGDIRS="$LOGDIRS $DCCIFD_LOGDIR"
+fi
+USERDIRS=
+if test -n "$DCCM_USERDIRS" -a -d "$DCCM_USERDIRS"; then
+    USERDIRS="$DCCM_USERDIRS"
+fi
+if test -n "$DCCIFD_USERDIRS" -a -d "$DCCIFD_USERDIRS"; then
+    # $DCCM_USERDIRS is often an initial substring of $DCCIFD_USERDIRS
+    if expr "$DCCIFD_USERDIRS" : "$DCCM_USERDIRS" >/dev/null; then :
+    else
+	USERDIRS="$USERDIRS $DCCIFD_USERDIRS"
+    fi
+fi
+
+# trim the greylist database
+case X"$GREY_ENABLE" in
+    [oO][nN])
+	GREY_ENABLE=on
+	;;
+    X)
+	if test -n "$GREY_CLIENT_ARGS"; then
+	    GREY_ENABLE=on
+	fi
+	;;
+esac
+if test -z "$GREY_SRVR_ID"; then
+    if test -n "`grep '# auto local greylist server-ID' $DCC_HOMEDIR/ids`" \
+	    -a -n "`grep '^32702' $DCC_HOMEDIR/ids`"; then
+	GREY_SRVR_ID=32702
+    fi
+fi
+if test -n "$GREY_SRVR_ID" -a "$GREY_ENABLE" = on; then
+    if $DCC_LIBEXEC/dbclean -Gon -i $GREY_SRVR_ID $GREY_DBCLEAN_ARGS; then :
+    else
+	# assume EX_DCC_RESTART=79
+	if test $? -eq 79; then
+	    eval $DCC_LOGGER "running dbclean -S and restarting greylist server"
+	    $DCC_LIBEXEC/dbclean -S -Gon -i $GREY_SRVR_ID $GREY_DBCLEAN_ARGS
+	    $DCC_LIBEXEC/start-grey
+	fi
+    fi
+fi
+
+
+# Delete old checksums from the dccd database if it seems dccd can run.
+#	For historical reasons, SRVR_ID set and DCCD_ENABLE null
+#	also turns on dccd.
+case "$DCCD_ENABLE" in
+    [oO][fF][fF]) DCCD_ENABLE=off;;
+    *) DCCD_ENABLE=on;;
+esac
+if test -n "$SRVR_ID" -a "$DCCD_ENABLE" = on; then
+    if $DCC_LIBEXEC/dbclean -i $SRVR_ID $SADDR $DBCLEAN_ARGS; then :
+    else
+	# assume EX_DCC_RESTART=79
+	if test $? -eq 79; then
+	    eval $DCC_LOGGER "running dbclean -S and restarting DCC server"
+	    $DCC_LIBEXEC/dbclean -S -i $SRVR_ID $SADDR $DBCLEAN_ARGS
+	    $DCC_LIBEXEC/start-dccd
+	fi
+    fi
+fi
+
+
+# Remove old dccm and dccifd log files.
+if test -n "$DBCLEAN_LOGDAYS" -a -n "$LOGDIRS$USERDIRS"; then
+    ( find $LOGDIRS $USERDIRS $FOLLOW -type f				\
+	    \( \( -name 'msg.*' -mtime +$DBCLEAN_LOGDAYS \)		\
+		-o \( -name 'tmp.*' -mtime +1 \) \) -print		\
+	| @DCC_XARGS@ /bin/rm
+    find $LOGDIRS $USERDIRS $FOLLOW -depth -type d			\
+	    \( -name '[0-9]' -o -name '[0-9][0-9]'			\
+		-o -name '[0-9][0-9][0-9]' \) -print			\
+	    | @DCC_XARGS@ /bin/rmdir
+    ) 2>&1 | grep -v 'No such file or directory'			\
+	| grep -v 'Directory not empty'
+fi
+
+# Notify users about new log files.
+#   The file $DCC_LIBEXEC/webuser-notify must be a script that will send
+#   a suitable message.  See the example in the cgi-bin directory.
+if test -n "$USERDIRS" -a -x $DCC_LIBEXEC/webuser-notify; then
+    for DIR in $USERDIRS; do
+	MARKER=$DIR/notify.marker
+	if test -r $MARKER; then
+	    NEWER="-newer $MARKER"
+	else
+	    NEWER=
+	fi
+	rm -f $MARKER.new
+	touch $MARKER.new
+
+	# Find usernames with a pending message or with a new but not newer
+	#   than new log file.  Avoid newer than new files to ensure we
+	#   generate at most one notification per log file.
+	(cd $DIR; find . $FOLLOW -type f  \( -name notify.pending	\
+			    -o \( $NEWER -name 'msg.*' \) \)		\
+		! -newer $MARKER.new -print )				\
+	    | sed -n -e 's@\./\(.*\)/log/msg\..*@\1@p'			\
+		    -e 's@\./\(.*\)/log/[0-9/]*/msg\..*@\1@p'		\
+		    -e 's@\./\(.*\)/notify.pending$@\1@p'		\
+	    | sort -u							\
+	    | $DCC_LIBEXEC/webuser-notify $DEBUG -d "$DIR"
+
+	mv -f $MARKER.new $MARKER
+    done
+fi
+
+
+# encourage local DCC clients to switch back
+@bindir@/cdcc -q rtt >/dev/null 2>&1
diff -r 000000000000 -r c7f6b056b673 misc/crontab.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/crontab.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,11 @@
+# daily DCC server cleanup
+#   Rhyolite Software DCC 1.3.103-1.6 $Revision$
+# @configure_input@
+
+# if you use more than one DCC server, different start times are best
+#   so that your servers are not all busy cleaning their databases at once.
+15	2	*	*	*	@libexecdir@/cron-dccd
+
+# optionally fetch list of empty messags
+#   Please choose a random day of the week, hour, and minute
+#minute	hour	*	*	day	@libexecdir@/fetch-testmsg-whitelist
diff -r 000000000000 -r c7f6b056b673 misc/dcc-stats-collect.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/dcc-stats-collect.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,166 @@
+#! /bin/sh
+
+# collect spam statistics in .rrd files
+#   [-x]	    debugging
+#   [-q]	    quiet
+#   [-S]	    read `cdcc stats` from stdin
+#   [-h dcc_homedir]
+#   [-D data-dir]   where to put the graphs and rrdtool files
+#   [-s stats-file] save raw `cdcc stats` output in stats-file
+#   [-t time]	    seconds since the Epoch
+#   [-T @RRDTOOL@]
+#		    see http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/
+#			or the FreeBSD package.
+#   [-O rrdopts]    "--heartbeat X" or "--step Y"
+#   [-i client-ID]  that DCC servers will accept
+#   [-p password]   that DCC servers will accept
+#   host1, host2, ... servers to ask for data
+
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.27 $Revision$
+#	@configure_input@
+
+DCC_HOMEDIR=@prefix@
+DEBUG=
+# check the args once to get the home directory
+while getopts "xqUSh:D:s:t:T:O:i:p:" c; do
+    case $c in
+	x) set -x; DEBUG=-x;;
+	h) DCC_HOMEDIR="$OPTARG";;
+	*) ;;
+    esac
+done
+. $DCC_HOMEDIR/dcc_conf
+
+QUIET=
+UPDATERRD=
+GET_ARGS=
+DATADIR=$DCC_HOMEDIR/stats
+STATSFILE=/dev/null
+TS=N
+RRDTOOL=@RRDTOOL@
+RRDOPTS=
+CLNT_ID="1"
+PASSWD=""
+USAGE="`basename $0`: [-xqUS] [-h homedir] [-D data-dir] [-s stats-file] [-t time]
+    [-T rrdtool] [-O rrdopts] [-i client-ID] [-p password] host1 host2 ..."
+OPTIND=1
+while getopts "xqUSh:D:s:t:T:O:i:p:" c; do
+    case $c in
+	x) ;;
+	q) QUIET="-q";;
+	U) UPDATERRD=yes;;
+	S) GET_ARGS="$GET_ARGS -S";;
+	h) ;;
+	D) DATADIR="$OPTARG";;
+	s) STATSFILE="$OPTARG";;
+	t) TS="$OPTARG";;
+	T) RRDTOOL="$OPTARG";;
+	O) RRDOPTS="$RRDOPTS $OPTARG";;
+	i) CLNT_ID="$OPTARG";;
+	p) PASSWD="$OPTARG";;
+	*) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -eq 0; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+
+cd $DATADIR
+
+# generate a timestamp that can be used with new and old `touch` commands 
+#   to give the .rrd and status files the right mtime
+TTS=
+if test -n "$TS" -a "$TS" != N; then
+    if TTS=`date -r $TS '+%m%d%H%S'  2>/dev/null`; then : ;
+    else
+	# deal with systems that do not have `date -r`
+	TTS=`@PERL@ -e "use POSIX qw(strftime);	       		\
+	    print strftime '%m%d%H%S', localtime($TS);"`
+    fi
+fi
+
+for HOST in $*; do
+    HOST="`basename $HOST .rrd`"
+
+    eval XSTATSFILE="$STATSFILE"
+    if test "$PASSWD" != ""; then
+	LINE=`$DCC_LIBEXEC/stats-get $GET_ARGS $DEBUG $QUIET		\
+	    -s$XSTATSFILE -i$CLNT_ID -p "$PASSWD" $HOST`
+    else
+	LINE=`$DCC_LIBEXEC/stats-get $GET_ARGS $DEBUG $QUIET		\
+	    -s$XSTATSFILE -i$CLNT_ID $HOST`
+    fi
+    if test -n "$TTS"; then
+	touch $TTS XSTATSFILE
+    fi
+
+    FILE="$HOST.rrd"
+    # update RRD file to include maximums
+    if test -s "$FILE" -a -n "$UPDATERRD"				\
+	&& test -z "`$RRDTOOL info $FILE 2>/dev/null			\
+			    | grep '^rra.*cf = .MAX.'`"; then
+	mv "$FILE" "$FILE.old"
+	# get all but the final "</rrd>" line of the xml
+	$RRDTOOL dump "$FILE.old" | sed -e '$d' >"$FILE.xml"
+	# add a "MAX" database from a copy of the last "MIN" database
+	$RRDTOOL dump "$FILE.old"					\
+	    | sed -e 's@<cf> MIN </cf>@<rra><cf> MAX </cf>@p'		\
+		    -e '1,/<cf> MAX <.cf>/d'				\
+		    -e 's@<v> [0-9.+e]* </v>@<v> NaN </v>@g' >>"$FILE.xml"
+	$RRDTOOL restore "$FILE.xml" "$FILE"
+	rm "$FILE.xml"
+    fi
+
+    if test -n "$LINE"; then
+	# create the RRD file if it does not exist
+	if test ! -s "$FILE"; then
+	    $DCC_LIBEXEC/dcc-stats-init $QUIET $DEBUG -h$DCC_HOMEDIR	\
+		-D$DATADIR -T "$RRDTOOL" -O "$RRDOPTS" "$FILE"
+	fi
+	# do not add flood checksum counts to new database
+	if test -z "`$RRDTOOL info $FILE | grep '^ds.flooded.*DERIVE'`"; then
+	    LINE=`expr "$LINE" : '\(.*\):[0-9]*$'`
+	fi
+	$RRDTOOL update "$FILE" "$TS:$LINE"
+	# ensure that the .rrd file has the right mtime
+	if test -n "$TTS"; then
+	    touch $TTS $FILE
+	fi
+    fi
+done
diff -r 000000000000 -r c7f6b056b673 misc/dcc-stats-graph.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/dcc-stats-graph.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,429 @@
+#! /bin/sh -e
+
+# graph collected DCC statistics in .png files.
+#   [-x]	    debugging
+#   [-q]	    quiet
+#   [-B]	    make big graphs
+#   [-G db]	    make graph of database size
+#   [-G db-min]	    make graph of database size without maximum size
+#   [-G traffic-noratio]	database size without spam ratios
+#   [-G traffic]		mail message rates and spam ratios
+#   [-G ratio]			spam ratios
+#   [-h dcc_homedir]
+#   [-T @RRDTOOL@]  see http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/
+#			or the FreeBSD package.
+#   [-O rrdopts]    additional rrdtool options for all graphs
+#   [-t title]	    for graphs; '%1' is replaced with the type of graph
+#   [-s span]	    time covered by graphs.
+#			The default is "1day,1week,1month,1year"
+#   [-S stop-epoch] end of the graph
+#   [-y vresol]    day, minute, ... vertical access for messages
+#   gname	    basic file name for graphs, - for stdout
+#   rrd1,...	    RRD databases that will be combined to produce the graphs
+
+# The rrd files must be initialzed with dcc-stats-init, which is called
+#   automatically by dcc-stats-collect.  Data must be collected every
+#   10 minutes with dcc-stats-collect.  The rrd files should be in
+#   @prefix@/stats
+
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.71 $Revision$
+#	@configure_input@
+
+DCC_HOMEDIR=@prefix@
+DEBUG=
+RRDTOOL=@RRDTOOL@
+# check the args once to get the home directory
+while getopts "xqBdRmh:G:T:O:t:s:S:y:" c; do
+    case $c in
+	x) set -x; DEBUG=-x=;;
+	h) DCC_HOMEDIR="$OPTARG";;
+	*) ;;
+    esac
+done
+. $DCC_HOMEDIR/dcc_conf
+
+BIG=
+GRAPH_DB=
+GRAPH_TRAFFIC=
+GRAPH_RATIO=
+GRAPH_SET=
+RRDOPTS=
+TITLE_SET=
+SPANS_SET=
+SPANS="1day,1week,1month,1year"
+STOP=
+YRESOL=86400
+YLABEL=day
+USAGE="`basename $0`: [-xqB] [-h homedir] [-T rrdtool] [-O rrdopts] [-G type]
+	[t title] [-s spans] [-S stop-epoch] [-y day|hour|min|sec] gname rrd"
+OPTIND=1
+while getopts "xqBdRmh:G:T:O:t:s:S:y:" c; do
+    case $c in
+	x) ;;				    # handled above
+	q) exec 1>/dev/null;;
+	h) ;;				    # handled above
+	B) BIG=yes;;
+	d) GRAPH_SET=yes; GRAPH_DB=yes;;			# obsolete
+	R) GRAPH_RATIO=;;					# obsolete
+	m) GRAPH_SET=yes; GRAPH_TRAFFIC=yes; GRAPH_RATIO=yes;;	# obsolete
+	G) GRAPH_SET=yes
+	    case "$OPTARG" in
+		db) GRAPH_DB=db;;
+		db-min) GRAPH_DB=db-min;;
+		traffic-noratio) GRAPH_TRAFFIC=yes;;
+		traffic) GRAPH_TRAFFIC=yes; GRAPH_RATIO=yes;;
+		ratio) GRAPH_RATIO=yes;;
+		*) echo "$USAGE" 1>&2; exit 1;;
+	    esac
+	    ;;
+	T) RRDTOOL="$OPTARG";;
+	O) RRDOPTS="$RRDOPTS $OPTARG";;
+	t) TITLE_SET=yes; TITLE_PAT="$OPTARG";;
+	s) SPANS_SET=yes; SPANS="$OPTARG";;
+	S) if expr "$OPTARG" : '[0-9]*$' >/dev/null		\
+		&& test "$OPTARG" -gt 1033870038		\
+		     -a "$OPTARG" -lt 2000000000; then
+		STOP=$OPTARG
+	    else
+		echo "$OPTARG is a bad number of seconds since the Epoch" 1>&2
+		exit 1;
+	    fi
+	    ;;
+	y)
+	case "$OPTARG" in
+	    day) YRESOL=86400; YLABEL=day;;
+	    hour) YRESOL=3600; YLABEL=hour;;
+	    min) YRESOL=60; YLABEL=min;;
+	    sec) YRESOL=1; YLABEL=sec;;
+	esac
+	;;
+	*) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -lt 1; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+
+if test -z "$GRAPH_SET"; then
+    GRAPH_RATIO=yes			# bug compatible with old versions
+fi
+
+BASE_DIR="$DCC_HOMEDIR/stats"
+cd "$BASE_DIR"
+
+GNAME=$1
+if test "$#" -ge 2; then
+    # assume .rrd file is same as the graph name if the .rrd file is absent
+    shift
+fi
+FILE=$1
+# trim unneeded directory names
+FILE=`echo "$FILE" | sed -e "s@$BASE_DIR/*@@"`
+if test ! -s "$FILE"; then
+    echo "$FILE is not a good rrd file" 1>&2
+    exit 1
+fi
+
+if test "$TITLE_SET" != yes; then
+    if test "X$GNAME" != X-; then
+	TITLE_PAT="%1 at $GNAME"
+    else
+	TITLE_PAT="%1"
+    fi
+fi
+
+if test -n "`$RRDTOOL version | grep '^RRDtool 1\.0'`"; then
+    RRDVERSION=1.0
+else
+    RRDVERSION=
+fi
+
+if test -n "$BIG"; then
+    XYEAR_MONTHS=1
+    GSIZE="--width 600 --height 240"
+    P_YGRID=					# % or spam ratio vertical grid
+    M_YGRID=					# messages vertical grid
+    H_YGRID="--alt-autoscale-max"		# database vertical grid
+    M_YLABEL="message/$YLABEL"
+    AVGFMT="%.0lf/$YLABEL"
+    LABEL_REPORTS="total mail"
+    LABEL_BULK="likely spam"
+    LABEL_SPAM="trapped spam"
+else
+    XYEAR_MONTHS=2
+    GSIZE="--width 200 --height 40"
+    P_YGRID="--y-grid 25:2"
+    if test "$RRDVERSION" = 1.0; then
+	M_YGRID="--alt-y-mrtg"
+	H_YGRID="--alt-y-mrtg"
+    else
+	M_YGRID=
+	H_YGRID=
+    fi
+    M_YLABEL=msgs/$YLABEL
+    AVGFMT="%.1lf %S/$YLABEL"
+    LABEL_REPORTS="total"
+    LABEL_BULK="likely spam"
+    LABEL_SPAM="trapped"
+fi
+
+# use only a few colors to try to be portable
+C_GREEN='#00ff7f'
+C_YELLOW='#ffff00'
+C_PINK='#ffb6c1'
+C_INDIANRED='#ff6a6a'
+C_RED2='#ee0000'
+C_BLUE='#0000ff'
+C_SKY_BLUE='#87cefa'
+C_ORANGE='#ffa500'
+C_DARK_ORANGE='#ff8c00'
+C_BLACK='#000000'
+
+
+FTYPE=png
+ATTRIBS="$GSIZE --imgformat PNG --lower-limit 0"
+
+
+
+# find good ending dates
+date2ts () {
+    if test "$3" -eq 0; then
+	eval $1=new $2="' '"
+	return
+    fi
+
+    NEW_END=$3
+    if test -n "$4"; then
+	NEW_END=`expr $NEW_END - $NEW_END % $4 || true`
+    fi
+    eval $1=$NEW_END
+
+    if NEW_TS=`date -r $NEW_END '+%x %R %Z'  2>/dev/null`; then : ;
+    else
+	# deal with systems that do not have `date -r`
+	NEW_TS=`@PERL@ -e "use POSIX qw(strftime); \
+		print strftime '%x %R %Z', localtime($LAST);"`
+    fi
+    if test "$RRDVERSION" != 1.0; then
+	NEW_TS=`echo "$NEW_TS" | sed -e 's/:/\\\:/g'`
+    fi
+    eval $2="'COMMENT:$NEW_TS'"
+}
+
+STEP=`$RRDTOOL info $FILE | sed -n -e 's/^step = \([0-9][0-9]*\)/\1/p'`
+LAST=`$RRDTOOL last $FILE`
+if test -n "$STOP" -a "$LAST" -gt 0"$STOP"; then
+    LAST="$STOP"
+fi
+# avoid odd times when individual servers were polled
+LAST=`expr $LAST - $LAST % $STEP || true`
+
+date2ts END END_COMMENT $LAST
+date2ts END_DAY END_DAY_COMMENT $LAST 86400
+
+
+for DUR in `echo $SPANS | tr ',' ' '`; do
+    case $DUR in
+	1d*)
+	    DUR=1day
+	    SPAN=24h
+	    XGRID="--x-grid HOUR:1:HOUR:2:HOUR:2:0:%k"
+	    # as the "rdtool graph" man page suggests, don't be fooled
+	    # by daylight savings time
+	    ;;
+	1w*)
+	    DUR=1week
+	    SPAN=168h
+	    # 24*3600 = 86400
+	    if test -n "$BIG"; then
+		XGRID="--x-grid HOUR:6:DAY:1:DAY:1:86400:%a\ %m/%d"
+	    else
+		XGRID="--x-grid HOUR:6:DAY:1:DAY:1:86400:%a"
+	    fi
+	    # as the "rdtool graph" man page suggests, don't be fooled
+	    # by daylight savings time
+	    ;;
+	1m*)
+	    DUR=1month
+	    SPAN=$DUR
+	    XGRID="--x-grid WEEK:1:WEEK:1:WEEK:1:0:%b/%d"
+	    ;;
+	1y*)
+	    DUR=1year
+	    SPAN=$DUR
+	    # label every month on big graphs and every other on small
+	    # 28*24*60*60 = 2419200
+	    XGRID="--x-grid MONTH:1:YEAR:1:MONTH:$XYEAR_MONTHS:2419200:%b"
+	    ;;
+	2y*)
+	    DUR=2years
+	    SPAN=$DUR
+	    if test "$XYEAR_MONTHS" = 2; then
+		# small graph with 1 label/year
+		# 365*24*60*60 = 31536000 = year
+		XGRID="--x-grid YEAR:1:YEAR:1:YEAR:1:31536000:%Y"
+	    else
+		# label every other month on big graphs
+		# 28*24*60*60 = 2419200
+		XYEAR_MONTHS=2
+		XGRID="--x-grid MONTH:1:YEAR:1:MONTH:2:2419200:%b"
+	    fi
+	    ;;
+	*)
+	    case $DUR in
+		3y*) DUR=3years;;
+		4y*) DUR=4years;;
+		# assume everything else is the 5 year maximum in the RRD files
+		*) DUR=5years;;
+	    esac
+	    SPAN=$DUR
+	    if test "$XYEAR_MONTHS" = 2; then
+		# small graph with 1 label/year
+		# 365*24*60*60 = 31536000 = year
+		XGRID="--x-grid YEAR:1:YEAR:1:YEAR:1:31536000:%Y"
+	    else
+		# big graph with 1 label/year
+		XGRID="--x-grid MONTH:1:MONTH:12:MONTH:12:0:%b/%y"
+	    fi
+	    ;;
+    esac
+
+    ONAME=-
+
+    PERCENT='reports,/,100,*,0,100,LIMIT'
+    if test $YRESOL -eq 1; then
+	YUNIT="0,1e12,LIMIT"
+    else
+	YUNIT="$YRESOL,*,0,1e12,LIMIT"
+    fi
+
+    if test "$GRAPH_RATIO" = yes; then
+	if test "X$GNAME" != X-; then
+	    ONAME=$GNAME-spam-ratio.$DUR.$FTYPE
+	    echo "$ONAME: " | tr -d '\012'
+	fi
+	TITLE=`echo "$TITLE_PAT" | sed -e 's/%1/Spam Ratio/g'`
+	RATIOS="'CDEF:percentbulk=bulk,$PERCENT'			\
+	    'CDEF:percentspam=spam,$PERCENT'				\
+	    'AREA:percentbulk$C_INDIANRED:$LABEL_BULK'			\
+	    'AREA:percentspam$C_PINK:$LABEL_SPAM'"
+	RATIOS="$RATIOS'\j' 'GPRINT:percentbulk:AVERAGE:%.0lf%%'"
+	    if test -n "$BIG"; then
+		RATIOS="$RATIOS						\
+		    '$END_COMMENT'					\
+		    'GPRINT:percentspam:AVERAGE:%.0lf%%\j'"
+	    else
+		RATIOS="$RATIOS '$END_COMMENT\j'"
+	    fi
+	eval $RRDTOOL graph $ONAME "$RRDOPTS"				\
+	    --end $END --start end-$SPAN				\
+	    $ATTRIBS "--title '$TITLE'"					\
+	    $XGRID $P_YGRID --upper-limit 100				\
+	    DEF:reports=$FILE:reports:AVERAGE				\
+	    DEF:bulk=$FILE:bulk:AVERAGE					\
+	    DEF:spam=$FILE:spam:AVERAGE					\
+	    $RATIOS
+	if test "X$GNAME" = X-; then
+	    exit
+	fi
+    fi
+
+    if test "$GRAPH_TRAFFIC" = yes; then
+	if test "X$GNAME" != X-; then
+	    ONAME=$GNAME-spam.$DUR.$FTYPE
+	    echo "$ONAME: " | tr -d '\012'
+	fi
+	TITLE=`echo "$TITLE_PAT" | sed -e 's/%1/Mail Checked/g'`
+	if test -n "$BIG"; then
+	    LEGEND="'GPRINT:greports:AVERAGE:$AVGFMT'			\
+		'GPRINT:gbulk:AVERAGE:$AVGFMT'				\
+		'GPRINT:gspam:AVERAGE:$AVGFMT'"
+	    LEGEND="$LEGEND'\j' '$END_COMMENT\c'"
+	else
+	    LEGEND="'GPRINT:greports:AVERAGE:$AVGFMT' '$END_COMMENT\j'"
+	fi
+	TRAFFIC="'DEF:reports=$FILE:reports:AVERAGE'			\
+	    'CDEF:greports=reports,$YUNIT'				\
+	    'DEF:bulk=$FILE:bulk:AVERAGE'				\
+	    'CDEF:gbulk=bulk,$YUNIT'					\
+	    'DEF:spam=$FILE:spam:AVERAGE'				\
+	    'CDEF:gspam=spam,$YUNIT'					\
+	    'AREA:greports$C_SKY_BLUE:$LABEL_REPORTS'			\
+	    'AREA:gbulk$C_INDIANRED:$LABEL_BULK'			\
+	    'AREA:gspam$C_PINK:$LABEL_SPAM'"
+	eval $RRDTOOL graph $ONAME "$RRDOPTS"				\
+	    --end $END --start end-$SPAN				\
+	    $ATTRIBS "--title '$TITLE'"					\
+	    $XGRID $M_YGRID --vertical-label $M_YLABEL			\
+	    $TRAFFIC"'\j'" $LEGEND
+	if test "X$GNAME" = X-; then
+	    exit
+	fi
+    fi
+
+    # database size graph
+    if test -n "$GRAPH_DB" -a \( -n "$SPANS_SET" -o $SPAN != 24h \); then
+	if test "X$GNAME" != X-; then
+	    ONAME=$GNAME-hashes.$DUR.$FTYPE
+	    echo "$ONAME: " | tr -d '\012'
+	fi
+	TITLE=`echo "$TITLE_PAT" | sed -e 's/%1/Checksums/g'`
+	# show only the minimum values for old RRD files
+	if test "$GRAPH_DB" = yes; then
+	    if test -z "`$RRDTOOL info $FILE				\
+			    | grep '^rra.*cf = .MAX.'`"; then
+		GRAPH_DB=db-min
+	    fi
+	fi
+	if test "$GRAPH_DB" = db-min; then
+	    DISPLAY="DEF:minhash=$FILE:hashes:MIN			\
+		AREA:minhash$C_PINK"
+	else
+	    DISPLAY="DEF:minhash=$FILE:hashes:MIN			\
+		DEF:maxhash=$FILE:hashes:MAX				\
+		AREA:maxhash$C_INDIANRED:max				\
+		AREA:minhash$C_PINK:min"
+	fi
+	# take the database values from the last server
+	eval $RRDTOOL graph $ONAME $RRDOPTS				\
+	    --end $END_DAY --start end-$SPAN				\
+	    $ATTRIBS --step 86400 "--title '$TITLE'"			\
+	    $XGRID $H_YGRID $DISPLAY "'$END_DAY_COMMENT\c'"
+    fi
+done
diff -r 000000000000 -r c7f6b056b673 misc/dcc-stats-init.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/dcc-stats-init.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,161 @@
+#! /bin/sh
+
+# initialize an RRD file to collect DCC statistics
+#   [-x]	    debugging
+#   [-q]	    quiet
+#   [-h dcc_homedir]
+#   [-D data-dir]   where to put the rrdtool file
+#   [-T @RRDTOOL@]
+#		    see http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/
+#			or the FreeBSD package.
+#   [-O rrdopts]    "--heartbeat X" or "--step Y"
+#   file	    RRD database that receives the collected data
+
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.25 $Revision$
+#	@configure_input@
+
+exec 1>&2 </dev/null
+
+DCC_HOMEDIR=@prefix@
+DEBUG=
+# check the args once to get the home directory
+while getopts "xqRh:D:T:O:" c; do
+    case $c in
+	x) set -x; DEBUG=-x=;;
+	h) DCC_HOMEDIR="$OPTARG";;
+	*) ;;
+    esac
+done
+. $DCC_HOMEDIR/dcc_conf
+
+REPS=
+DATADIR=$DCC_HOMEDIR/stats
+RRDTOOL=@RRDTOOL@
+RRDOPTS=
+USAGE="`basename $0`: [-xqR] [-h homedir] [-D data-dir] [-T rrdtool]
+    [-O rrdopts] file.rrd"
+OPTIND=1
+while getopts "xqRh:D:T:O:" c; do
+    case $c in
+	x) ;;				    # handled above
+	q) QUIET=quiet;;
+	R) REPS=yes;;
+	h) ;;				    # handled above
+	D) DATADIR="$OPTARG";;
+	T) RRDTOOL="$OPTARG";;
+	O) RRDOPTS="$RRDOPTS $OPTARG";;
+	*) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 1; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+FILE=$1
+
+if test ! -d $DATADIR; then
+    mkdir $DATADIR
+fi
+cd $DATADIR
+
+if test "$FILE" = "`basename $FILE .rrd`"; then
+    FILE=$FILE.rrd
+fi
+if test -s "$FILE"; then
+    echo "$FILE already exists" 1>&2
+    exit 1
+fi
+
+
+# assume no server will handle more than 1000 operations/second
+OPSPS=1000
+
+# default sample every 10 minutes.
+STEP=600
+
+# be willing to get STEP and heartbeat from the -O arg(s)
+HBEAT=
+while test -n "$RRDOPTS"; do
+    RRDOPTS=`expr "X$RRDOPTS" : 'X *\(.*\)'`
+    if test -z "$RRDOPTS"; then
+	break;
+    fi
+    X=`expr "X$RRDOPTS" : 'X--heartbeat[	 ][	 ]*\([0-9][0-9]*\)'`
+    if test -n "$X"; then
+	HBEAT=$X
+	RRDOPTS=`expr "X$RRDOPTS" :  "X--heartbeat[	 ]$X *\(.\)"`
+	continue
+    fi
+    X=`expr "X$RRDOPTS" : 'X--step[	 ][	 ]*\([0-9][0-9]*\)'`
+    if test -n "$X"; then
+	STEP=$X
+	RRDOPTS=`expr "X$RRDOPTS" :  "X--step[	 ]$X *\(.\)"`
+	continue
+    fi
+    echo 'unrecognized RRD option "'"$RRDOPTS"'"' 1>&2
+    exit 1
+done
+
+# Use a heartbeat of 31 minutes or 1860 seconds to allow a missed sample as
+#   well as jitter by cron.  If you use a heartbeat equal to the cron
+#   repetition rate, then a single second delay by cron will make the previous
+#   period have no samples and be undefined.
+if test -z "$HBEAT"; then
+    HBEAT=`expr $STEP \* 3 + 60`
+fi
+
+# Keep hourly (or 6-step) average data for 5 years
+HOUR_STEPS=`expr 3600 / $STEP`
+HOUR_ROWS=`expr \( 5 \* 366 \* 24 \* 3600 \) / \( $STEP \* 6 \)`
+
+# Keep 10-minute (or single-step) average data for a month
+FAST_ROWS=`expr 31 \* 24 \* $HOUR_STEPS`
+
+# Keep daily database size data for 5 years
+DB_STEPS=`expr \( 3600 \* 24 \) / $STEP`
+DB_ROWS=`expr 5 \* 366`
+
+$RRDTOOL create $FILE --step $STEP --start -61month		\
+    DS:reports:DERIVE:$HBEAT:0:$OPSPS				\
+    DS:bulk:DERIVE:$HBEAT:0:$OPSPS				\
+    DS:spam:DERIVE:$HBEAT:0:$OPSPS				\
+    DS:hashes:GAUGE:$HBEAT:U:U					\
+    RRA:AVERAGE:0.5:$HOUR_STEPS:$HOUR_ROWS			\
+    RRA:AVERAGE:0.04:1:$FAST_ROWS				\
+    RRA:MIN:0.9:$DB_STEPS:$DB_ROWS				\
+    RRA:MAX:0.9:$DB_STEPS:$DB_ROWS
diff -r 000000000000 -r c7f6b056b673 misc/dcc.m4.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/dcc.m4.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,75 @@
+divert(-1)
+#
+# This feature causes sendmail to check with a local DCCM deamon about
+# all incoming mail
+#
+# Use this file by copying it to your sendmail/cf/feature directory
+# and by adding a line like the following to your sendmail.mc configuration
+# file:
+#	`FEATURE(dcc)'
+#
+# To change the default milter failture settings or timeouts as described
+# in the sendmail milter README file, use something like:
+#	`FEATURE(dcc,``F=T, T=C:30s;S:30s;R:30s;E:30s'')'
+# The default parameters wait 30 seconds for the initial connection from
+#   sendmail to dccm (C), 30 seconds sendmail sending (S) as well as receiving
+#   responses (R) from dccm, and 30 seconds for dccm to check the message
+#   at its end (E).
+#   Adding "F=T" to the parameters cases sendmail to give SMTP clients
+#   a temporary failure when dccm fails.  "F=R" causes sendmail to reject
+#   mail when dccm fails.  When neither "F=T" nor "F=R" is present, sendmail
+#   quietly accepts the message if dccm fails or is absent.
+#   (A failure by dccm would be a serious bug.)
+#   misc/hackmc changes the default to "F=T".
+#
+# To use common directories and whiteclnt files for mail relayed to domains
+#   listed in /etc/mail/relay-domains, use a third argument as in
+#	`FEATURE(dcc, , ``local'')'
+#   If domain.com is in the relay-domains, file, then mail for all users
+#   forwarded there will use userdirs/local/domain.com/whiteclnt and
+#   userdirs/local/domain.com/log.
+#
+# To connect dccm to sendmail via TCP or use a UNIX domain socket
+# other than @dcc_rundir@/dccm, or other fancy changes, consider modifying
+# this file or the resulting sendmail.cf file.
+#
+# See also @libexecdir@/hackmc
+#
+# @configure_input@
+
+
+divert(0)
+VERSIONID(`dcc.m4 Rhyolite Software DCC 1.3.103-1.14 $Revision$')
+divert(-1)
+
+ifdef(`_DCC_DEF_',`',`dnl
+dnl define map to communicate blacklist results to DCC via dccm
+define(_NEED_MACRO_MAP_,1)dnl
+dnl set Xdcc milter parameters
+define(`_DCC_DEF_', ifelse(len(X`'_ARG_), `1', ``, T=C:30s;S:30s;R:30s;E:30s'',
+``,' _ARG_'))dnl
+dnl Sendmail version 8.11 requires _FFR_MILTER
+define(`_FFR_MILTER',`')dnl
+dnl
+dnl always pass the DCC is/notspam macros to not need FEATURE(`delay_checks')
+define(`confMILTER_MACROS_CONNECT',confMILTER_MACROS_CONNECT``, {dcc_isspam}, {dcc_notspam}'')dnl
+define(`confMILTER_MACROS_ENVFROM',confMILTER_MACROS_ENVFROM``, {dcc_isspam}, {dcc_notspam}, {dcc_mail_host}'')dnl
+define(`confMILTER_MACROS_ENVRCPT',confMILTER_MACROS_ENVRCPT``, {dcc_isspam}, {dcc_notspam}, {dcc_userdir}'')dnl
+define(`confMILTER_MACROS_EOM',confMILTER_MACROS_EOM``, {dcc_isspam}, {dcc_notspam}'')dnl
+INPUT_MAIL_FILTER(`dcc', ``S=unix:@dcc_rundir@/dccm'_DCC_DEF_')'dnl
+dnl
+`LOCAL_RULESETS
+# Define a macro for dccm that has the SMTP client host name even if
+#   a smart relay is used.
+#   This works only if ``FEATURE(delay_checks)'' is not used.
+SLocal_check_mail
+R$*			$: $1 $| $>canonify $1
+R$* $| $* < @$* > $*	$: $1 $| $3
+R$* $| $*		$: $1 $(macro {dcc_mail_host} $@ $2 $)'
+ifelse(len(X`'_ARG2_), `1', ``dnl'',``
+# use _ARG2_/domain.name/whiteclnt and _ARG2_/domain.name/log for the
+#   DCC whitelist and log directory for relayed mail
+SLocal_check_rcpt
+R$*			$: $1 $| $>canonify $1
+R$* $| $*<@ $*$=R .> $*	$: $1 $(macro {dcc_userdir} $@ _ARG2_/$4 $)
+R$* $| $*<@ $ $=R > $*	$: $1 $(macro {dcc_userdir} $@ _ARG2_/$4 $)''))
diff -r 000000000000 -r c7f6b056b673 misc/dccdnsbl.m4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/dccdnsbl.m4	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,55 @@
+divert(-1)
+
+############################################################################
+# NOTICE:  Unless your version of sendmail is ancient, use the standard
+#   sendmail `FEATURE(dnsbl)' or `FEATURE(enhdnsbl)' and the hackmc script 
+#   in the DCC source instead of this sendmail `FEATURE' file.
+############################################################################
+
+
+# Connect a DNS blacklist such as the RBL+ to the local DCCM daemon and so to
+# a DCC server to report spam to the DCC database.
+# The connection is by the sendmail "dcc_isspam" macro
+#
+# The first and required argument is the domain name of the DNS blacklist
+# The second, optional argument is a log and SMTP status/rejection message.
+#
+# For example, with the following, mail from SMTP clients on the
+#   relays.mail-abuse.org list would be rejected as well as reported as
+#   extremely bulky to the DCC server:
+# `FEATURE(dccdnsbl, `zen.spamhaus.org', `"Mail from " $`'&{client_addr} " reject to DCC - see http://www.spamhaus.org/zen/"')'
+#
+#   (Of course, the outer pair of `' quotes must be removed)
+#
+#   The `FEATURE(dccdnsbl)' line should be after the `FEATURE(dcc)' line
+#   if the latter is present.
+#
+# If you see error messages from sendmail like "map macro not found", check
+#   for the Kmacro definition in cf/m4/proto.m4 and consider the "dnl" comments
+#   below.
+
+divert(0)
+VERSIONID(`dccdnsbl.m4 Rhyolite Software DCC 1.3.103-1.18 $Revision$')
+divert(-1)
+
+define(`_DCCDNSBL_SRV_', ifelse(len(X`'_ARG_),`1',`blackholes.mail-abuse.org',`_ARG_'))
+define(`_DCCDNSBL_MSG_', ifelse(len(X`'_ARG2_),`1',`"Mail from " $`'&{client_addr} " refused by blackhole site '_DCCDNSBL_SRV_`"',`_ARG2_'))
+
+dnl Remove the "dnl " strings (including the blanks) from the following 5 lines
+dnl	up to the divert(8) line if your version of sendmail does not include
+dnl	the Kmacro line by default.
+dnl ifdef(`_DCCDNSBL_DEF_',`dnl',`dnl
+dnl define(_DCCDNSBL_DEF_, 1)dnl
+dnl LOCAL_CONFIG
+dnl `# define macros map to communicate DNS black list results to DCC via dccm'
+dnl Kmacro macro')
+divert(8)
+# DNS based IP address spam list _DCCDNSBL_SRV_ connected to DCCM
+R$*			$: $&{client_addr}
+R$-.$-.$-.$-		$: <?> $(host $4.$3.$2.$1._DCCDNSBL_SRV_. $: OK $)
+R<?>OK			$: OKSOFAR
+R<?>$+<TMP>		$: TMPOK
+R<?>$+			$@ $(macro {dcc_isspam} $@ _DCCDNSBL_MSG_ $) TODCC
+divert(-1)
+
+ifdef(`_DCC_DEF_',`',`FEATURE(`dcc')')dnl
diff -r 000000000000 -r c7f6b056b673 misc/dccman2html
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/dccman2html	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,112 @@
+#! /bin/sh -e
+
+# elaborate man2html and do not depend on whether perl is in /usr/bin or
+#   /usr/local/bin
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#   Rhyolite Software DCC 1.3.103-1.18 $Revision$
+
+MAN2HTML=misc/man2html
+OFILE=/dev/null
+TFILE=/tmp/dccman2html.$$
+
+USAGE="`basename $0`: [-x] [-m man2html] [-t name] [-o ofile]"
+while getopts "xm:t:o::" c; do
+    case $c in
+	x) set -x; DEBUG=-x;;
+	m) MAN2HTML=$OPTARG;;
+	t) Title="$OPTARG.8"; exec <"$OPTARG";;
+	o) OFILE=$OPTARG;;
+	*) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+
+trap "/bin/rm -f $TFILE" 0 1 2 15
+
+BURL=http://www.rhyolite.com/
+perl $MAN2HTML -botm 0 -topm 0 -Title "$Title"				\
+	-cgiurlexp '(($TITLE = ${title}) =~ tr/A-Z/a-z/, $TITLE).".html"' \
+    | sed								\
+	-e '# remove stray page header not deleted by man2html'		\
+	-e '/^FreeBSD [1-9]/d'						\
+	-e '# remove HTTP references to non-DCC man pages'		\
+	-e 's@<\(A HREF="cdcc.html"\)@<Z\1@g'				\
+	-e 's@<\(A HREF="dbclean.html"\)@<Z\1@g'			\
+	-e 's@<\(A HREF="dblist.html"\)@<Z\1@g'				\
+	-e 's@<\(A HREF="dcc[dm]*.html"\)@<Z\1@g'			\
+	-e 's@<\(A HREF="cdcc.html"\)@<Z\1@g'				\
+	-e 's@<\(A HREF="dccproc.html"\)@<Z\1@g'			\
+	-e 's@<\(A HREF="dccifd.html"\)@<Z\1@g'				\
+	-e 's@<\(A HREF="dccsight.html"\)@<Z\1@g'			\
+	-e 's@<A HREF="[^>]*>\([^<]*\)</A>@\1@g'			\
+	-e 's@<ZA HREF=@<A HREF=@g'					\
+	-e '# remove useless tags'					\
+	-e 's@</B> <B>@ @g'						\
+	-e '# add anchor names to sections'				\
+	-e 's@^\(   \)\(<B>\)\([^<]*\)</B>$@\1<A NAME="\3">\2\3</B></A>@' \
+	-e 's@\(<A NAME="[^"]*\)</*B>@\1@g'				\
+	-e 's@\(<A NAME="[^"]*\)</*B>@\1@g'				\
+	-e 's@^\(<H[0-9]>\)\([^<]*\)@\1<A NAME="\2">\2</A>@'		\
+	-e '# add anchor names to option definitions'			\
+	-e '/NAME="DESCRIPTION"/,/NAME="FILES""/s@^     <B>-\([-_a-zA-Z0-9]\)</B>@     <A NAME="OPTION-\1"><B>-\1</B></A>@' \
+	-e '# add anchor names to cdcc operations'			\
+	-e '/NAME="OPERATIONS"/,/NAME="FILES"/s@^     <B>\([-_a-zA-Z0-9 ]*\)</B>@     <A NAME="OPERATION-\1"><B>\1</B></A>@' \
+	-e '# add anchor names to file descriptions'			\
+	-e '/NAME="FILES"/,/^<H2>/s@^     \([a-zA-Z0-9_/.]\{1,\}\)@     <A NAME="FILE-\1">\1</A>@' \
+	-e '# convert blanks in anchor names to dashes'			\
+	-e 's/\(<A NAME="[^"]*\) /\1-/g'				\
+	-e 's/\(<A NAME="[^"]*\) /\1-/g'				\
+	-e 's/\(<A NAME="[^"]*\) /\1-/g'				\
+	-e 's/\(<A NAME="[^"]*\) /\1-/g'				\
+	-e 's/\(<A NAME="[^"]*\) /\1-/g'				\
+	-e 's/\(<A HREF="[^"]*\) /\1-/g'				\
+	-e 's/\(<A HREF="[^"]*\) /\1-/g'				\
+	-e 's/\(<A HREF="[^"]*\) /\1-/g'				\
+	-e 's/\(<A HREF="[^"]*\) /\1-/g'				\
+	-e 's/\(<A HREF="[^"]*\) /\1-/g'				\
+	-e '# replace reference the Rhyolite Software with URL'		\
+	-e 's@Rhyolite Software, LLC,@<A HREF="'$BURL'">&</A>@'		\
+	-e 's@'$BURL'\(">[^<]*</A>\)[, ]*\('$BURL'[^ ]*\)@\2\1@'	\
+	-e '# make references to HTML documents in .8 files into links'	\
+	-e 's@INSTALL.html@<A HREF="&">&</A>@'		\
+    > $TFILE
+
+cp $TFILE $OFILE
+
diff -r 000000000000 -r c7f6b056b673 misc/fetch-testmsg-whitelist.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/fetch-testmsg-whitelist.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,155 @@
+#!/bin/sh
+
+# Fetch a list of "empty" mail messages for whitelisting.  Many free mail
+#   service providers add HTML or other text to mail.  That causes empty
+#   and nearly empty mail messages to have valid DCC checksums and not be
+#   ignored by DCC clients.
+
+# The fetched file can be included in whiteclnt files.  For example, the
+#   following line in @prefix@whiteclnt would whitelist many common
+#   empty messages
+#   include @prefix@/testmsg-whitelist
+
+# By default the script fetches http://www.iecc.com/dcc-testmsg-whitelist.txt
+#   to @prefix@/testmsg-whitelist
+
+# The script should be run at most once a day.
+
+
+
+# Rhyolite Software DCC 1.3.103-1.31 $Revision$
+# @configure_input@
+
+exec </dev/null
+
+HTTP_REFERER=DCC-1.3.103-script; export HTTP_REFERER
+
+DCC_HOMEDIR=@prefix@
+URL=http://www.iecc.com/dcc-testmsg-whitelist.txt
+TGT=testmsg-whitelist
+LOG=$TGT.log
+PGM=@FETCH_CMD@
+FORCE=
+
+USAGE="`basename $0`: [-xf] [-h homedir] [-p fetch-pgm] [-s src-URL]"
+while getopts "xfh:p:s:" c; do
+    case $c in
+	x) set -x;;
+	f) FORCE=yes;;
+	h) DCC_HOMEDIR="$OPTARG";;
+	p) PGM="$OPTARG";;
+	s) URL="$OPTARG";;
+	*) echo 1>&2 "$USAGE"; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    echo 1>&2 "$USAGE"
+    exit 1
+fi
+
+
+FNAME=`expr "$URL" : '.*/\([^/][^/]*\)'`
+if test -z "$FNAME"; then
+    FNAME="$URL"
+    if test -z "$FNAME"; then
+	echo 1>&2 "source file not specified"
+	exit 1
+    fi
+fi
+
+cd $DCC_HOMEDIR
+
+# don't bother if the file exists and is no more than 2 weeks old
+if test -f "$TGT" -a -z "$FORCE"; then
+    if test "`find $TGT -mtime -7 -type f`"; then
+	date "+%n%x %X: $TGT is too recent to fetch again" >>$LOG
+	exit 0
+    fi
+fi
+
+# Delay for an arbitrary, somewhat random number of seconds to try to spread
+#   the load on the HTTP server for the list. Some versions of cksum yield
+#   10 digit numbers that some versions of expr think are negative.
+RND=`ps | cksum | sed -e 's/  */ + /' -e 's/\([0-9]\{6\}\)\([0-9]\)/\1 + \2/g'`
+RND=`expr \( $RND \) % 123`
+sleep $RND
+
+# use fetch, wget, curl, or ftp that understands URLs
+rm -f $FNAME
+PGM_B=`basename $PGM`
+if test "$PGM_B" = wget; then
+    PGM_B=
+    # Do not use --mirror because -r results in a 0 exit status
+    #   even on failure.
+    # Do not use --no-remove-listing, -nr, or --dont-remove-listing
+    #   because none of them are supported by all versions of wget.
+    # At least some versions of wget exit with 0 after having done
+    #   nothing but emitting a usage message.
+    if $PGM -nd --retr-symlinks -N --no-host-directories		\
+	    --passive-ftp @FETCH_WGET_OPTS@ $URL >$LOG 2>&1; then
+	if test -s $FNAME; then
+	    if test -n "`sed -n -e 2q					\
+		    -e 's/.*DOCTYPE.*/HTML/p'				\
+		    -e 's/<HEAD>/HTML/p' -e 's/<head>/HTML/p' $FNAME`"; then
+		rm $FNAME
+	    fi
+	fi
+    fi
+fi
+
+if test "$PGM_B" = fetch; then
+    PGM_B=
+    $PGM -p -q -m $URL >$LOG 2>&1
+fi
+
+if test "$PGM_B" = curl; then
+    PGM_B=
+    $PGM -s -S --connect-timeout 30 --max-time 600 \
+	@FETCH_CURL_OPTS@ $URL -o $FNAME >$LOG 2>&1
+    # --fail does not work on at least some versions of curl
+    if test -s $FNAME; then
+	if test -n "`sed -n -e 2q					\
+		    -e 's/.*DOCTYPE.*/HTML/p'				\
+		    -e 's/<HEAD>/HTML/p' -e 's/<head>/HTML/p' $FNAME`"; then
+	    rm $FNAME
+	fi
+    fi
+fi
+
+if test "$PGM_B" = ftp; then
+    PGM_B=
+    $PGM -p $URL  >$LOG 2>&1
+    # if that did not work, try ancient anonymous FTP
+    if test ! -s $FNAME; then
+	HOST=`expr "$URL" : "ftp://\([^/]*\)/"`
+	RFILE=`expr "$URL" : "ftp://[^/]*/\(.*\)"`
+	echo "try old anonymous FTP"
+	(echo "user anonymous `hostname`"; echo "get $RFILE $FNAME")	\
+	    | ftp -n $HOST
+    fi
+    # some versions of ftp like to leave empty files
+    if test ! -s $FNAME; then
+	rm -f $FNAME
+    fi
+fi
+# if some other program was specified, use it and hope it is simple enough
+if test -n "$PGM_B"; then
+    $PGM $URL
+fi
+
+if test ! -s "$FNAME"; then
+    echo 1>&2 "failed to fetch $FNAME with $PGM"
+    exit 1
+fi
+
+# work around wget timestamping
+#   We use the mtime of the file to reduce fetching of the file.
+#   We use `wget -N` to prevent .1 files
+touch $FNAME
+
+if test "$FNAME" != "$TGT"; then
+    mv -f "$FNAME" "$TGT"
+fi
+
+date "+%n%x %X: fetched $TGT" >>$LOG
diff -r 000000000000 -r c7f6b056b673 misc/fetchblack.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/fetchblack.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,211 @@
+#! /bin/sh
+
+# fetch a blacklist dcc.dcc-servers.net DCC servers
+
+# This script should be run about twice an hour at an arbitrary time
+#	instead of 0,30 to minimize pile-ups on the FTP server.
+
+# -h homedir	set DCC home directory
+
+# -o blklist	file of local blacklist entries
+
+# -p pgm	fetch, wget, curl, or ftp
+
+# -s srvr-host	host name of source of the common blacklist
+
+# Rhyolite Software DCC 1.3.103-1.50 $Revision$
+# @configure_input@
+
+
+# other, probably local blacklist files
+OTHER_BLS=
+
+exec </dev/null
+
+HTTP_REFERER=DCC-1.3.103-script; export HTTP_REFERER
+
+DCC_HOMEDIR=@prefix@
+SRVR_BL=dcc-servers-blacklist
+SRVRS=
+PGM=@FETCH_CMD@
+FORCE=
+MIRROR=
+
+USAGE="`basename $0`: [-xf] [-h homedir] [-o blklist] [-p pgm] [-s srvr-host]"
+while getopts "xfh:o:p:s:m:" c; do
+    case $c in
+	x) set -x;;
+	f) FORCE=yes;;
+	h) DCC_HOMEDIR="$OPTARG";;
+	o) OTHER_BLS="$OTHER_BLS $OPTARG";;
+	p) PGM=$OPTARG;;
+	s) SRVRS="$SRVRS $OPTARG";;
+	m) MIRROR=$OPTARG;;
+	*) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    echo "$USAGE" 1>&2; exit 1
+fi
+if test -z "$SRVRS"; then
+    SRVRS="http://www.dcc-servers.net/dcc http://www.rhyolite.com/dcc ftp://ftp.dcc-servers.net ftp://ftp.rhyolite.com"
+fi
+URLS=
+for SRVR in $SRVRS; do
+    if expr "$SRVR" : '.[hft]*tp://' >/dev/null; then
+	if expr "$SRVR" : ".*/$SRVR_BL"'$' >/dev/null; then
+	    URLS="$URLS $SRVR"
+	else
+	    URLS="$URLS $SRVR/$SRVR_BL"
+	fi
+    else
+	URLS="$URLS ftp://$SRVR/$SRVR_BL"
+    fi
+done
+
+# dccd expects this target
+TGT_BL=$DCC_HOMEDIR/blacklist
+NEW_BL=new-blacklist
+MARKER=fetch-failed
+BDIR=$DCC_HOMEDIR/$SRVR_BL
+FLOG=$BDIR/fetch-log
+
+if test ! -d $BDIR; then
+    mkdir $BDIR
+fi
+cd $BDIR
+
+# use fetch, wget, curl, or ftp that understands URLs
+if test ! -s $SRVR_BL; then
+    echo "# initial place keeper" >$SRVR_BL
+fi
+mv $SRVR_BL $SRVR_BL.old
+rm -f $FLOG
+BASE_PGM=`basename "$PGM"`
+if test "$BASE_PGM" = wget; then
+    BASE_PGM=
+    for URL in $URLS; do
+	echo "$URL:" >>$FLOG
+	# Do not use --mirror because -r results in a 0 exit status
+	#   even on failure.
+	# Do not use --no-remove-listing, -nr, or --dont-remove-listing
+	#   because none of them are supported by all versions of wget.
+	# At least some versions of wget exit with 0 after having done
+	#   nothing but emitting a usage message.
+	if $PGM -nd --retr-symlinks -N --no-host-directories		\
+		--passive-ftp @FETCH_WGET_OPTS@ $URL >>$FLOG 2>&1; then
+	    if test -s $SRVR_BL; then
+		if test -z "`sed -n -e 2q				\
+			    -e 's/.*DOCTYPE.*/HTML/p'			\
+			    -e 's/<HEAD>/HTML/p' -e 's/<head>/HTML/p'	\
+		    $SRVR_BL`"; then
+		    break;
+		fi
+		# do not leave a broken file
+		rm $SRVR_BL
+	    fi
+	fi
+	echo >>$FLOG
+    done
+fi
+
+if test "$BASE_PGM" = fetch; then
+    BASE_PGM=
+    for URL in $URLS; do
+	echo "$URL:" >>$FLOG
+	$PGM -p -m $URL >>$FLOG 2>&1
+	if test -s $SRVR_BL; then
+	    break;
+	fi
+	echo >>$FLOG
+    done
+fi
+
+if test "$BASE_PGM" = curl; then
+    BASE_PGM=
+    for URL in $URLS; do
+	echo "$URL:" >>$FLOG
+	$PGM @FETCH_CURL_OPTS@ --connect-timeout 30 --max-time 600\
+	    $URL -o $SRVR_BL >>$FLOG 2>&1
+	# --fail does not work on at least some versions of curl
+	if test -s $SRVR_BL; then
+		if test -z "`sed -n -e 2q				\
+			    -e 's/.*DOCTYPE.*/HTML/p'			\
+			    -e 's/<HEAD>/HTML/p' -e 's/<head>/HTML/p'	\
+			$SRVR_BL`"; then
+		break;
+	    fi
+	    # do not leave a broken file
+	    rm $SRVR_BL
+	fi
+	echo >>$FLOG
+    done
+fi
+
+if test "$BASE_PGM" = ftp; then
+    BASE_PGM=
+    for URL in $URLS; do
+	echo "$URL:" >>$FLOG
+	$PGM -p $URL >>$FLOG 2>&1
+	if test -s $SRVR_BL; then
+	    break;
+	fi
+	echo >>$FLOG
+    done
+    # if that did not work, try ancient FTP
+    if test ! -s $SRVR_BL; then
+	for URL in $URLS; do
+	    HOST=`expr "$URL" : "ftp://\([^/]*\)/"`
+	    RFILE=`expr "$URL" : "ftp://[^/]*/\(.*\)"`
+	    if test -z "$RFILE" -o -z "$HOST"; then
+		continue
+	    fi
+	    echo "$URL:" >>$FLOG
+	    (echo "user anonymous `hostname`"; echo "get $RFILE $SRVR_BL")    \
+		| ftp -n $HOST >>$FLOG 2>&1
+	    if test -s $SRVR_BL; then
+		break;
+	    fi
+	    # some versions of ftp like to leave empty files
+	    rm -f $SRVR_BL
+	    echo >>$FLOG
+	done
+    fi
+fi
+
+if test -s $SRVR_BL; then
+    rm -f $MARKER $SRVR_BL.old
+    if test -n "$MIRROR"; then
+	cp $SRVR_BL $MIRROR
+    fi
+else
+    # complain only when the list is more than a day old, and then only
+    #	once per day
+    OLD_MARKER="`find $MARKER -follow -mtime -1 2>/dev/null`"
+    if test -z "$OLD_MARKER"; then
+	echo "Unable to fetch blacklist; see $FLOG" > $MARKER
+	date >>$MARKER
+    fi
+    # continue so that we include new versions of local blacklists
+    mv $SRVR_BL.old $SRVR_BL
+fi
+
+# add the local lists last so that they can override
+rm -f $NEW_BL
+cat  $SRVR_BL >$NEW_BL
+for NM in $OTHER_BLS; do
+    if test -s "$NM"; then
+	echo >>$NEW_BL
+	echo >>$NEW_BL
+	echo >>$NEW_BL
+	echo "# local file $NM" >>$NEW_BL
+	cat $NM >>$NEW_BL
+    fi
+done
+if test -z "$FORCE" && cmp $NEW_BL ../blacklist 1>/dev/null 2>&1; then :
+else
+    # copy it in case the target is a symbolic link
+    cp $NEW_BL $TGT_BL
+fi
+rm $NEW_BL
diff -r 000000000000 -r c7f6b056b673 misc/fetchids.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/fetchids.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,42 @@
+#! /bin/sh
+
+# get the IDs of commercial and other DCC Reputation clients
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.6 $Revision$
+#	@configure_input@
+
+exec </dev/null
+
diff -r 000000000000 -r c7f6b056b673 misc/hackmc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/hackmc	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,201 @@
+#! /bin/sh -e
+
+# This kludge of a shell script warps a sendmail.cf produced from a .mc file
+#   to report some spam to the Distributed Checksum Clearinghouse (DCC)
+#   in addition to rejecting it.
+#
+# Mail that is rejected by a sendmail access_db is reported via dccm to
+#   a DCC server as extremely bulky.  Error messages in the access_db 
+#    file must start with "DCC:" or they will be ignored by this mechanism.
+
+# This script should be run in the sendmail cf/cf directory, and given a list
+#   of .mc files, as in
+#	cd cf/cf
+#	.../misc/hackmc -AROT ../m4/cf.m4 local.mc > local.cf
+
+# It seems to work on sendmail.cf generated for sendmail versions 8.11
+#   through 8.14.3.  There is no guarantee that it will work with other
+#   versions.  You must compare the result of this script with the unmodified
+#   sendmail.cf.
+
+
+# This script "denatures" RCS keywords in its output so that revisions of
+#   the resulting sendmail.cf can be archived with RCS without losing
+#   the original RCS lines from the Sendmail organization.
+
+# In addition to sending mail blacklisted by the sendmail access_db to
+#   the DCC, the following can also be turned on:
+
+#   -x	turn on debugging
+
+#   -A	send mail with bogus Mail_From domain names to the DCC instead of
+#	only rejecting it.
+
+#   -R	silently discard unauthorized relay attempts after reporting them
+#	to the DCC.  This mechanism also implies -f to ensure that relay
+#	attempts do not leak if dccm is not running.
+
+#   -r	reject unauthorized relay attempts after reporting them
+#	to the DCC.  This mechanism also implies -f to ensure that relayed
+#	attempts do not leak if dccm is not running.
+
+#   -D	add a local rule that rejects mail from SMTP clients without reverse 
+#	DNS and reports the mail as spam to the DCC.
+#	This has a fairly high false positive rate.
+
+#   -O	modify the sendmail rules to treat access_db "OK" and "RELAY"
+#	or "Spam:...FRIEND" entries as whitelisting the message.
+
+#   -M	modify the sendmail rules generated by FEATURE(badmx), FEATURE(dnsbl),
+#	and Feature(enhdnsbl) so that mail that is rejected by sendmail 
+#	is reported via dccm to a DCC server as extremely bulky.
+
+#   -T	modify the sendmail rules to trust (whitelist) mail from users
+#	authenticated with an SMTP AUTH TRUST_AUTH_MECH() mechanism or from
+#	SMTP clients with certificates verified with START TLS.
+#	If STMP-AUTH used, TRUST_AUTH_MECH must be set in the .mc file and
+#	sendmail must be built with SASL or otherwise have working SMTP auth.
+#	FEATURE(`delay_checks') must NOT be used.
+
+#   -f	if dccm fails, reject mail with a temporary failure status code
+#	instead of passing it.  This changes the default FEATURE(dcc)
+#	parameters.  See dcc.m4.
+
+#   -m m4
+#	specifies the path to the m4 program as well as any m4 args
+#	such as `hackmc -m4 "/usr/bin/m4 -D_CF_DIR_=/usr/share/sendmail/cf/"`
+
+
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+# Rhyolite Software DCC 1.3.103-1.43 $Revision$
+
+USAGE="`basename $0`: [-xfARrDOMT] [-m m4] file1.mc file2.mc ..."
+M4=m4
+DNS1='#'
+DNS2='#'
+RELAY='#'
+NOTSPAM='#'
+AUTH='#'
+# fail temporarily if dccm is not running.
+#	Add F=T to reject mail when dccm is dead,
+#	but only if there is not already an F=x setting
+TEMPFAIL='#'
+TEMPFAIL0='/F=/!s/S=[^ ,]*/&, F=T/'
+RDNS='#'
+DNSBL1='#'
+DNSBL2='#'
+
+while getopts "xm:fARrDOMT" c; do
+    case $c in
+	x) set -x;;
+	m) M4="$OPTARG";;
+	f) TEMPFAIL=$TEMPFAIL0;;
+	A)
+	    DNS1='s/$#error $@ \([.0-9]*\) $: "\(5.*[Dd]omain name required.*\)/$# $(macro {dcc_isspam} $@ "\1 \2"  Sent to DCC" $) TODCC/'
+	    DNS2='s/$#error $@ \([.0-9]*\) $: "\(5.*Domain of sender.*\)/$# $(macro {dcc_isspam} $@ "\1 \2"  Sent to DCC" $) TODCC/'
+	    ;;
+	R)
+	    RELAY='s/$#error $@ [.0-9]* $: "5[.0-9 ]*\(Relaying denied.*\)/$# $(macro {dcc_isspam} $@ "DISCARD: \1"  Sent to DCC" $) TODCC/'
+	    TEMPFAIL=$TEMPFAIL0
+	    ;;
+	r)
+	    RELAY='s/$#error $@ [.0-9]* $: "5[.0-9 ]*\(Relaying denied.*\)/$# $(macro {dcc_isspam} $@ "REJECT: \1"  Sent to DCC" $) TODCC/'
+	    TEMPFAIL=$TEMPFAIL0
+	    ;;
+	D) RDNS=
+	    ;;
+	M) 
+	    DNSBL1='/^# DNS based IP address spam list/,/^$/s/$#error .* $: *"\(.*\)/$@ $(macro {dcc_isspam} $@ "\1"  Sent to DCC" $) TODCC/'
+	    DNSBL2='s/$#error .* $: *"\(.*MX record.*\)/$@ $(macro {dcc_isspam} $@ "\1"  Sent to DCC" $) TODCC/'
+	    ;;
+	T) AUTH=
+	    ;;
+	O) NOTSPAM='s/^R<\$={Accept}> *<*\$\*>*		*[^	]*/& $(macro {dcc_notspam} $@ $1 $)/'
+	    ;;
+	*) echo 1>&2 "$USAGE"; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+
+(
+# work hard to have only one Local_check_mail or Local_check_relay definition
+#   by prepending our rules to the first definitions
+echo LOCAL_RULESETS
+if test -z "$RDNS"; then
+    if test `$M4 $* 2>/dev/null| grep '^SLocal_check_relay' | wc -l` -lt 2; then
+	echo SLocal_check_relay
+    fi
+fi
+if test -z "$AUTH"; then
+    if test `$M4 $* 2>/dev/null| grep '^SLocal_check_mail' | wc -l` -lt 2; then
+	echo SLocal_check_mail
+    fi
+fi
+) | $M4 $* -								\
+    | sed -e 's/\$\(Id:.*\)\$/\1/' -e 's/\$\(Revision:.*\)\$/\1/'	\
+	    -e "${DNS1}" -e "${DNS2}" -e "${RELAY}" -e "${NOTSPAM}"	\
+	    -e "${DNSBL1}" -e "${DNSBL2}"				\
+									\
+	    -e '/^Xdcc/{' -e "$TEMPFAIL" -e '}'				\
+									\
+	    -e '# add the access.db hook'				\
+	    -e '/^R<$={Accept}>/a\
+R<DCC:$*> $*		$# $(macro {dcc_isspam} $@ $1": Sent to DCC" $) TODCC' \
+									\
+	    -e "# remove extra quotes" -e'/TODCC/s/""//'		\
+									\
+	    -e "/^S${RDNS}check_relay/,/^SLocal_check_relay/{"		\
+	    -e '/^SLocal_check_relay/a\
+# reject mail from clients without reverse DNS and report it as spam to the DCC\
+R$*			$: <$&{client_resolve}> $1\
+R<FAIL> $*		$# $(macro {dcc_isspam} $@ "SMTP client "$&{client_addr}" has no reverse DNS name" $) TODCC\
+R<$*> $*		$: $2\
+\
+'									\
+	    -e '}'							\
+									\
+	    -e "/^S${AUTH}check_mail/,/^SLocal_check_mail/{"		\
+	    -e '/^SLocal_check_mail/a\
+# mail from an SMTP client with a verified TLS cert is not spam for dccm\
+R$*			$: <$&{verify}> $1\
+R<OK> $*		$: $(macro {dcc_notspam} $@ STARTTLS verified $) <> $1\
+# mail authenticated with SMTP AUTH for relaying is also not spam for dccm\
+R<$*> $*		$: <$&{auth_type}> $2\
+R<$={TrustAuthMech}> $*	$: $(macro {dcc_notspam} $@ authenticated $) <> $2\
+R<$*> $*		$: $2\
+\
+'									\
+	    -e '}'
diff -r 000000000000 -r c7f6b056b673 misc/list-clients.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/list-clients.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,238 @@
+#! /bin/sh
+
+# generate periodic list of a DCC server's clients
+
+# This script should be run at midnight UTC, which is rarely midnight local
+#   time.
+# It prefers to generates a new HTML file based on an existing file, but
+#   will start from scratch if the output file does not already exist.
+
+#   -A		    all, not just anonymous clients
+#   -T		    do not tell dccd to clear its counters
+#   -I		    do not wrap the result with <HTML></HTML> etc. so that
+#			it can be server-side included into a web page
+#   -Z timezone	    in case dccd is far away
+#   -O cdcc-op	    something else to tell cdcc to do
+#   -d ndays	    keep this many days of snapshots instead of 7
+#   -n clients	    number of clients in each snapshot
+#   -s server	    host running dccd instead of localhost
+#   -i server-ID    DCC server-ID of the server
+#   -o ofile	    output file
+
+# Rhyolite Software DCC 1.3.103-1.30 $Revision$
+# @configure_input@
+
+DCC_HOMEDIR=@prefix@
+DEBUG=
+ARGS="xATIh:Z:O:d:n:s:p:i:o:"
+# check the args once to get the home directory to get server name and ID
+while getopts ":$ARGS" c; do
+    case $c in
+	x) set -x;;
+	h) DCC_HOMEDIR="$OPTARG";;
+	*) ;;
+    esac
+done
+if test -s $DCC_HOMEDIR/dcc_conf; then
+    . $DCC_HOMEDIR/dcc_conf
+fi
+
+CDCC=@bindir@/cdcc
+ALL=As
+CLEAR=clear
+INCLUDE=no
+OPS=
+NDAYS=7
+NCLIENTS=100
+TZ=UTC
+SERVER=localhost
+TITLE=`hostname`
+ID=$SRVR_ID
+PASSWD=
+OUT=
+USAGE="`basename $0`: [-xATI] [-Z timezone] [-O cdcc-op] [-d ndays] [-n nclients]
+    [-s server] [-p password] -i server-ID -o ofile"
+OPTIND=1
+while getopts "$ARGS" c; do
+    case $c in
+	x) set -x;;
+	A) ALL=;;
+	T) CLEAR=;;
+	I) INCLUDE=yes;;
+	h) ;;
+	Z) TZ=$OPTARG;;
+	O) OPS="$OPS$OPTARG; ";;
+	d) NDAYS="$OPTARG";;
+	n) NCLIENTS="$OPTARG";;
+	s) SERVER="$OPTARG"; TITLE="$OPTARG";;
+	p) PASSWD="; password $OPTARG";;
+	i) ID="$OPTARG";;
+	o) OUT="$OPTARG";;
+	*) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+
+# check that the ID is not 1
+if expr "$ID" - 1 >/dev/null 2>&1; then :
+else
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+
+# we need an output file
+if test "$OUT" = ""; then
+    echo "use '-o ofile' to specify an output file" 1>&2
+    exit 1
+fi
+
+CMD="${OPS}server $SERVER; id $ID$PASSWD"
+
+NCLIENTS5=`expr $NCLIENTS + 5`
+
+export TZ
+
+set -e
+
+
+# prefer the target directory for temporary files
+OUTDIR=`dirname $OUT`
+if test -z "$OUTDIR" -o -w "$OUTDIR"; then
+    NEW=$OUT.new$$
+    TMP=$OUT.tmp$$
+else
+    NEW=/tmp/list-clients-new$$
+    TMP=/tmp/list-clients-tmp$$
+fi
+trap "set +e; /bin/rm -f $NEW $TMP; exit" 0 1 2 15
+
+
+# create or add to the HTML file
+makeout () {
+    if test -s $OUT; then
+	# start the new file with the head of the old file
+	sed -e '/<!--clients sample-->/,$d'			\
+	    -e '/<\/BODY>/,$d' -e '/<\/body>,$/d'		\
+	    -e '/<\/HTML>/,$d' -e '/<\/html>/,$d' $OUT >$NEW
+    else
+	# create a new ouptut file
+	if test "$INCLUDE" != yes; then
+	    cat >>$NEW <<EOF
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<HTML>
+<HEAD>
+    <TITLE>$TITLE Clients</TITLE>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+</HEAD>
+
+<BODY>
+
+<H1>Recent $TITLE Clients</H1>
+<P>
+EOF
+	fi
+    fi
+
+    if test -z "$HEADING"; then
+	HEADING=`date`
+    fi
+
+    cat >>$NEW <<EOF
+<!--clients sample-->
+<H3>$HEADING</H3>
+<P>
+<PRE>
+EOF
+    if test -s $TMP; then
+	sed -e '1,/^  *server-ID/d' -e '/^$/d' -e '/^version/,$d' $TMP >>$NEW
+    else
+	echo "$*" >>$NEW
+    fi
+
+    cat >>$NEW <<EOF
+</PRE>
+
+EOF
+
+    # save a limited number samples from the old file
+    if test -s $OUT; then
+	IGNORECASE=1 awk '/<!--clients sample-->/{ ++nsample; }
+/<!--end clients samples-->/{ nsample = 1; }
+/<\/body>/{ nsample = 1; }
+/<\/html>/{ nsample = 1; }
+{ if (nsample < '$NDAYS' && nsample > 0) print $0;
+}' $OUT >>$NEW
+    else
+	echo '<!--end clients samples-->' >>$NEW
+    fi
+
+    if test "$INCLUDE" != yes; then
+	if grep -i '</body>' $NEW >/dev/null; then :
+	else
+	    echo '</BODY>' >>$NEW
+	fi
+	if grep -i '</html>' $NEW >/dev/null; then :
+	else
+	    echo '</HTML>' >>$NEW
+	fi
+    fi
+
+    cp -f $NEW $OUT
+
+    if test ! -s $TMP; then
+	echo "$*" 2>&1
+	exit 1
+    fi
+}
+
+
+
+
+
+# See if the server knows about client versions
+eval `$CDCC "$CMD; stats" 2>&1						\
+    | sed -n -e 's/^ *version [1-9]\.\([0-9]\{1,\}\)\.\([0-9]\{1,\}\) .*/VERS1=\1 VERS2=\2/p'\
+	-e 's/.*no working DCC server.*/VERS1=dead/p'`
+if test "$VERS1" = dead; then
+    makeout "$SERVER is not responding"
+fi
+if test -z "$VERS1" -o -z "$VERS2"; then
+    makeout "$SERVER of an unrecognized version"
+fi
+SVERS=`expr "${VERS1}000" + $VERS2`
+# dccd versions starting with *.3.32 answer `cdcc "clients -V"`
+#   Starting with 3.67, dccd understands `cdcc "clients -A"`
+if test "$SVERS" -ge 3032; then
+    if test "$SVERS" -ge 3067; then
+	COPTS="-V$ALL"
+    else
+	COPTS="-Vs"
+    fi
+else
+    COPTS="-s"
+fi
+
+# Prime reverse DNS resolution with a dummy run and then do it for real
+$CDCC "$CMD; clients $COPTS $NCLIENTS5" >/dev/null
+$CDCC "$CMD; clients $COPTS $NCLIENTS5" >$TMP
+
+if test ! -s $TMP; then
+    makeout "obtained no data from $SERVER"
+fi
+
+HEADING=`$CDCC "$CMD; stats $CLEAR"					\
+    | sed -n								\
+	-e '/clients since/h'						\
+	-e 's@.* \([0-9]\{1,\}\) clients since.*@\1 Clients@p'		\
+	-e 's@.* \([0-9]\{1,\}\) clients in.*@\1 Clients@p'		\
+	-e '/reports added between/{'					\
+	    -e x -e G							\
+	    -e 's@.*clients since \(.*\)\n.*\( and .*\)@between \1\2@p'	\
+	    -e 's@.*ween \([^.]*\)\.[0-9]*\(.*\)@between \1\2@p'	\
+	    -e '}'`
+
+makeout "missing $TMP"
diff -r 000000000000 -r c7f6b056b673 misc/logger
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/logger	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,59 @@
+#! /bin/sh
+
+# write to stderr and the system log for systems such as SunOS that
+# have `logger` without -s
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.9 $Revision$
+
+exec 1>&2
+
+USAGE="`basename $0`: [-x] [-p log_facility] [-t tag] message"
+LOGGER_FACILITY=
+TAG=DCC
+while getopts "xp:t:" c; do
+    case $c in
+	x) set -x; DEBUG=yes;;
+	p) LOGGER_FACILITY="-p $OPTARG";;
+	t) TAG="$OPTARG";;
+	*) echo "$USAGE" 1>&2
+	    logger $LOGGER_FACILITY -t "$TAG" "$USAGE"
+	    ;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+
+echo "$*"
+logger $LOGGER_FACILITY -t "$TAG" "$*"
diff -r 000000000000 -r c7f6b056b673 misc/man2html
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/man2html	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,625 @@
+#! /usr/bin/perl
+##---------------------------------------------------------------------------##
+##  File:
+##      @(#) man2html 1.2 97/08/12 12:57:30 @(#)
+##  Author:
+##      Earl Hood, ehood@medusa.acs.uci.edu
+##  Description:
+##      man2html is a Perl program to convert formatted nroff output
+##	to HTML.
+##
+##	Recommend command-line options based on platform:
+##
+##	Platform		Options
+##	---------------------------------------------------------------------
+##	c2mp			<None, the defaults should be okay>
+##	hp9000s700/800		-leftm 1 -topm 8
+##	sun4			-sun
+##	---------------------------------------------------------------------
+##
+##---------------------------------------------------------------------------##
+##  Copyright (C) 1995-1997	Earl Hood, ehood@medusa.acs.uci.edu
+##
+##  This program is free software; you can redistribute it and/or modify
+##  it under the terms of the GNU General Public License as published by
+##  the Free Software Foundation; either version 2 of the License, or
+##  (at your option) any later version.
+##
+##  This program is distributed in the hope that it will be useful,
+##  but WITHOUT ANY WARRANTY; without even the implied warranty of
+##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+##  GNU General Public License for more details.
+##
+##  You should have received a copy of the GNU General Public License
+##  along with this program; if not, write to the Free Software
+##  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+##  02111-1307, USA
+##---------------------------------------------------------------------------##
+
+package Man2Html;
+
+use Getopt::Long;
+
+($PROG = $0) =~ s/.*\///;
+$VERSION = "3.0.1";
+
+## Input and outputs filehandles
+$InFH	= \*STDIN   unless $InFH;
+$OutFH	= \*STDOUT  unless $OutFH;
+
+## Backspace character:  Used in overstriking detection
+*bs = \"\b";
+
+##	Hash of section titles and their HTML tag wrapper.
+##	This list allows customization of what HTML tag is used for
+##	a given section head.
+##
+##	The section title can be a regular expression.  Therefore, one must
+##	be careful about quoting special characters.
+##
+%SectionHead = (
+
+    '\S.*OPTIONS.*'		=> '<H2>',
+    'AUTHORS?'			=> '<H2>',
+    'BUGS'			=> '<H2>',
+    'COMPATIBILITY'		=> '<H2>',
+    'DEPENDENCIES'		=> '<H2>',
+    'DESCRIPTION'		=> '<H2>',
+    'DIAGNOSTICS'		=> '<H2>',
+    'ENVIRONMENT'		=> '<H2>',
+    'ERRORS'			=> '<H2>',
+    'EXAMPLES'			=> '<H2>',
+    'EXTERNAL INFLUENCES'	=> '<H2>',
+    'FILES'			=> '<H2>',
+    'LIMITATIONS'		=> '<H2>',
+    'NAME'			=> '<H2>',
+    'NOTES?'			=> '<H2>',
+    'OPTIONS'			=> '<H2>',
+    'REFERENCES'		=> '<H2>',
+    'RETURN VALUE'		=> '<H2>',
+    'SECTION.*:'		=> '<H2>',
+    'SEE ALSO'			=> '<H2>',
+    'STANDARDS CONFORMANCE'	=> '<H2>',
+    'STYLE CONVENTION'		=> '<H2>',
+    'SYNOPSIS'			=> '<H2>',
+    'SYNTAX'			=> '<H2>',
+    'WARNINGS'			=> '<H2>',
+    '\s+Section.*:'		=> '<H3>',
+
+);
+
+## Fallback tag if above is not found
+$HeadFallback = '<H2>';
+
+## Other gobals
+
+$Bare      = 0;		# Skip printing HTML head/foot flag
+$BTag	   = 'B';	# Overstrike tag
+$CgiUrl    = '';	# CGI URL expression
+$Compress  = 0;		# Do blank line compression flag
+$K	   = 0;		# Do keyword search processing flag
+$NoDepage  = 0;		# Do not strip page information
+$NoHeads   = 0;		# Do no header detection flag
+$SeeAlso   = 0;		# Do only SEE ALSO xrefs flag
+$Solaris   = 0;		# Solaris keyword search processing flag
+$Sun       = 0;		# Headers not overstriken flag
+$Title     = 'FIX ME';	# Title
+$UTag	   = 'I';	# Underline tag
+$ftsz	   = 7;		# Bottome margin size
+$hdsz	   = 7;		# Top margin size
+$leftm     = '';	# Left margin pad
+$leftmsz   = 0;		# Left margin size
+$pgsz	   = 66;	# Size of page size
+$txsz      = 52;	# Text body length size
+
+#############################################################################
+##	Main Block
+#############################################################################
+{
+    if (get_cli_opts()) {
+	if ($K) {
+	    man_k();
+	} else {
+	    do_it();
+	}
+    } else {
+	usage();
+    }
+}
+
+#############################################################################
+##	Subroutines
+#############################################################################
+
+sub do_it {
+
+    ##	Define while loop and then eval it when used.  The reason
+    ##	is to avoid the regular expression reevaulation in the
+    ##	section head detection code.
+
+    $doitcode =<<'EndOfDoItCode';
+
+    my($line, $tmp, $i, $head, $preindent, $see_also, $do);
+
+    $see_also = !$SeeAlso;
+    print $OutFH "<!-- Manpage converted by man2html $VERSION -->\n";
+    LOOP: while(!eof($InFH)) {
+	$blank = 0;
+	for ($i=0; $i < $hdsz; $i++) {
+	    last LOOP  unless defined($_ = <$InFH>);
+	}
+	for ($i=0; $i < $txsz; $i++) {
+	    last LOOP  unless defined($_ = <$InFH>);
+
+	    ## Check if compress consecutive blank lines
+	    if ($Compress and !/\S/) {
+		if ($blank) { next; } else { $blank = 1; }
+	    } else {
+		$blank = 0;
+	    }
+
+	    ## Try to check if line space is needed at page boundaries ##
+	    if (!$NoDepage && ($i==0 || $i==($txsz-1)) && !/^\s*$/) {
+		/^(\s*)/;  $tmp = length($1);
+		if ($do) {
+		    if ($tmp < $preindent) { print $OutFH "\n"; }
+		} else {
+		    $do = 1;
+		}
+		$preindent = $tmp;
+	    } else {
+		$do = 0;  $preindent = 0;
+	    }
+
+	    ## Interpret line
+	    $line = $_;
+	    entitize(\$_);		# Convert [$<>] to entity references
+
+	    ## Check for 'SEE ALSO' link only
+	    if (!$see_also && $CgiUrl && $SeeAlso) {
+		($tmp = $line) =~ s/.\010//go;
+		if ($tmp =~ /^\s*SEE\s+ALSO\s*$/o) { $see_also = 1; }
+		else { $see_also = 0; }
+	    }
+
+	    ## Create anchor links for manpage references
+	    s/((((.\010)+)?[\+_\.\w-])+\(((.\010)+)?
+	      \d((.\010)+)?\w?\))
+	     /make_xref($1)
+	     /geox  if $see_also;
+
+	    ## Emphasize underlined words
+	    # s/((_\010[^_])+[\.\(\)_]?(_\010[^_])+\)?)/emphasize($1)/oge;
+	    # s/((_\010[^_])+([\.\(\)_]?(_\010[^_])+)?)/emphasize($1)/oge;
+	    #
+	    # The previous expressions were trying to be clever about
+	    # detecting underlined text which contain non-alphanumeric
+	    # characters.  nroff will not underline non-alphanumeric
+	    # characters in an underlined phrase, and the above was trying
+	    # to detect that.  It does not work all the time, and it
+	    # screws up other text, so a simplified expression is used.
+
+	    s/((_\010[^_])+)/emphasize($1)/oge;
+
+	    $secth = 0;
+	    ## Check for strong text and headings
+	    if ($Sun || /.\010./o) {
+		if (!$NoHeads) {
+		    $line =~ s/.\010//go;
+		    $tmp = $HeadFallback;
+EndOfDoItCode
+
+    ##  Create switch statement for detecting a heading
+    ##
+    $doitcode .= "HEADSW: {\n";
+    foreach $head (keys %SectionHead) {
+	$doitcode .= join("", "\$tmp = '$SectionHead{$head}', ",
+			      "\$secth = 1, last HEADSW  ",
+			      "if \$line =~ /^$leftm$head/o;\n");
+    }
+    $doitcode .= "}\n";
+
+    ##  Rest of routine
+    ##
+    $doitcode .=<<'EndOfDoItCode';
+		    if ($secth || $line =~ /^$leftm\S/o) {
+			chop $line;
+			$_ = $tmp . $line . $tmp;
+			s%<([^>]*)>$%</$1>%;
+			$_ = "\n</PRE>\n" . $_ . "<PRE>\n";
+		    } else {
+			s/(((.\010)+.)+)/strongize($1)/oge;
+		    }
+		} else {
+		    s/(((.\010)+.)+)/strongize($1)/oge;
+		}
+	    }
+	    print $OutFH $_;
+	}
+
+	for ($i=0; $i < $ftsz; $i++) {
+	    last LOOP  unless defined($_ = <$InFH>);
+	}
+    }
+EndOfDoItCode
+
+
+    ##	Perform processing.
+
+    printhead()  unless $Bare;
+    print $OutFH "<PRE>\n";
+    eval $doitcode;			# $doitcode defined above
+    print $OutFH "</PRE>\n";
+    printtail()  unless $Bare;
+}
+
+##---------------------------------------------------------------------------
+##
+sub get_cli_opts {
+    return 0  unless
+    GetOptions(
+	"bare",		# Leave out HTML, HEAD, BODY tags.
+	"belem=s",	# HTML Element for overstriked text (def: "B")
+	"botm=i",	# Number of lines for bottom margin (def: 7)
+	"cgiurl=s",	# CGI URL for linking to other manpages
+	"cgiurlexp=s",	# CGI URL Perl expr for linking to other manpages
+	"compress",	# Compress consecutive blank lines
+	"headmap=s",	# Filename of user section head map file
+	"k",		# Process input from 'man -k' output.
+	"leftm=i",	# Character width of left margin (def: 0)
+	"nodepage",	# Do not remove pagination lines
+	"noheads",	# Do not detect for section heads
+	"pgsize=i",	# Number of lines in a page (def: 66)
+	"seealso",	# Link to other manpages only in the SEE ALSO section
+	"solaris",	# Parse 'man -k' output from a solaris system
+	"sun",		# Section heads are not overstriked in input
+	"title=s",	# Title of manpage (def: Not defined)
+	"topm=i",	# Number of lines for top margin (def: 7)
+	"uelem=s",	# HTML Element for underlined text (def: "I")
+
+	"help"		# Short usage message
+    );
+    return 0  if defined($opt_help);
+
+    $pgsz = $opt_pgsize || $pgsz;
+    if (defined($opt_nodepage)) {
+	$hdsz   = 0;
+	$ftsz   = 0;
+    } else {
+	$hdsz   = $opt_topm  if defined($opt_topm);
+	$ftsz   = $opt_botm  if defined($opt_botm);
+    }
+    $txsz       = $pgsz - ($hdsz + $ftsz);
+    $leftmsz    = $opt_leftm  if defined($opt_leftm);
+    $leftm      = ' ' x $leftmsz;
+
+    $Bare       = defined($opt_bare);
+    $Compress   = defined($opt_compress);
+    $K		= defined($opt_k);
+    $NoDepage   = defined($opt_nodepage);
+    $NoHeads    = defined($opt_noheads);
+    $SeeAlso    = defined($opt_seealso);
+    $Solaris    = defined($opt_solaris);
+    $Sun	= defined($opt_sun);
+
+    $Title      = $opt_title || $Title;
+    $CgiUrl     = $opt_cgiurlexp ||
+			($opt_cgiurl ? qq{return "$opt_cgiurl"} : '');
+
+    $BTag	= $opt_belem || $BTag;
+    $UTag	= $opt_uelem || $UTag;
+    $BTag	=~ s/[<>]//g;
+    $UTag	=~ s/[<>]//g;
+
+    if (defined($opt_headmap)) {
+	require $opt_headmap or warn "Unable to read $opt_headmap\n";
+    }
+    1;
+}
+
+##---------------------------------------------------------------------------
+sub printhead {
+    print $OutFH <<EndOfMeta;
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<HTML>
+<HEAD>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+    <TITLE>$Title</TITLE>
+    <META http-equiv="Content-Style-Type" content="text/css">
+    <STYLE type="text/css">
+	BODY {background-color:white; color:black}
+	ADDRESS {font-size:smaller}
+        IMG.logo {width:6em; vertical-align:middle}
+    </STYLE>
+</HEAD>
+<BODY>
+EndOfMeta
+}
+
+##---------------------------------------------------------------------------
+sub printtail {
+    print $OutFH <<\EndOfRef;
+<HR>
+<ADDRESS>
+Man(1) output converted with
+<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
+modified for the DCC $Date 2001/04/29 03:22:18 $
+<BR>
+<A HREF="http://www.dcc-servers.net/dcc/">
+    <IMG SRC="http://logos.dcc-servers.net/border.png"
+            class=logo ALT="DCC logo">
+    </A>
+<A HREF="http://validator.w3.org/check?uri=referer">
+    <IMG class=logo ALT="Valid HTML 4.01 Strict"
+        SRC="http://www.w3.org/Icons/valid-html401">
+    </A>
+</ADDRESS>
+</BODY>
+</HTML>
+EndOfRef
+}
+
+##---------------------------------------------------------------------------
+sub emphasize {
+    my($txt) = shift;
+    $txt =~ s/.\010//go;
+    $txt = "<$UTag>$txt</$UTag>";
+    $txt;
+}
+
+##---------------------------------------------------------------------------
+sub strongize {
+    my($txt) = shift;
+    $txt =~ s/.\010//go;
+    $txt = "<$BTag>$txt</$BTag>";
+    $txt;
+}
+
+##---------------------------------------------------------------------------
+sub entitize {
+    my($txt) = shift;
+
+    ## Check for special characters in overstrike text ##
+    $$txt =~ s/_\010\&/strike('_', '&')/geo;
+    $$txt =~ s/_\010</strike('_', '<')/geo;
+    $$txt =~ s/_\010>/strike('_', '>')/geo;
+
+    $$txt =~ s/(\&\010)+\&/strike('&', '&')/geo;
+    $$txt =~ s/(<\010)+</strike('<', '<')/geo;
+    $$txt =~ s/(>\010)+>/strike('>', '>')/geo;
+
+    ## Check for special characters in regular text.  Must be careful
+    ## to check before/after character in expression because it might be
+    ## a special character.
+    $$txt =~ s/([^\010]\&[^\010])/htmlize2($1)/geo;
+    $$txt =~ s/([^\010]<[^\010])/htmlize2($1)/geo;
+    $$txt =~ s/([^\010]>[^\010])/htmlize2($1)/geo;
+}
+
+##---------------------------------------------------------------------------
+##	escape special characters in a string, in-place
+##
+sub htmlize {
+    my($str) = shift;
+    $$str =~ s/&/\&amp;/g;
+    $$str =~ s/</\&lt;/g;
+    $$str =~ s/>/\&gt;/g;
+    $$str;
+}
+
+##---------------------------------------------------------------------------
+##	htmlize2() is used by entitize.
+##
+sub htmlize2 {
+    my($str) = shift;
+    $str =~ s/&/\&amp;/g;
+    $str =~ s/</\&lt;/g;
+    $str =~ s/>/\&gt;/g;
+    $str;
+}
+
+##---------------------------------------------------------------------------
+##	strike converts HTML special characters in overstriked text
+##	into entity references.  The entities are overstriked so
+##	strongize() and emphasize() will recognize the entity to be
+##	wrapped in tags.
+##
+sub strike {
+    my($w, $char) = @_;
+    my($ret);
+    if ($w eq '_') {
+	if ($char eq '&') {
+	    $ret = "_$bs\&_${bs}a_${bs}m_${bs}p_${bs};";
+	} elsif ($char eq '<') {
+	    $ret = "_$bs\&_${bs}l_${bs}t_${bs};";
+	} elsif ($char eq '>') {
+	    $ret = "_$bs\&_${bs}g_${bs}t_${bs};";
+	} else {
+	    warn qq|Unrecognized character, "$char", passed to strike()\n|;
+	}
+    } else {
+	if ($char eq '&') {
+	    $ret = "\&$bs\&a${bs}am${bs}mp${bs}p;${bs};";
+	} elsif ($char eq '<') {
+	    $ret = "\&$bs\&l${bs}lt${bs}t;${bs};";
+	} elsif ($char eq '>') {
+	    $ret = "\&$bs\&g${bs}gt${bs}t;${bs};";
+	} else {
+	    warn qq|Unrecognized character, "$char", passed to strike()\n|;
+	}
+    }
+    $ret;
+}
+
+##---------------------------------------------------------------------------
+##	make_xref() converts a manpage crossreference into a hyperlink.
+##
+sub make_xref {
+    my $str = shift;
+    $str =~ s/.\010//go;			# Remove overstriking
+
+    if ($CgiUrl) {
+	my($title,$section,$subsection) =
+	    ($str =~ /([\+_\.\w-]+)\((\d)(\w?)\)/);
+
+	$title =~ s/\+/%2B/g;
+	my($href) = (eval $CgiUrl);
+	qq|<B><A HREF="$href">$str</A></B>|;
+    } else {
+	qq|<B>$str</B>|;
+    }
+}
+
+##---------------------------------------------------------------------------
+##	man_k() process a keyword search.  The problem we have is there
+##	is no standard for keyword search results from man.  Solaris
+##	systems have a different enough format to warrent dealing
+##	with it as a special case.  For other cases, we try our best.
+##	Unfortunately, there are some lines of results that may be
+##	skipped.
+##
+sub man_k {
+    my($line,$refs,$section,$subsection,$desc,$i,
+       %Sec1, %Sec1sub, %Sec2, %Sec2sub, %Sec3, %Sec3sub,
+       %Sec4, %Sec4sub, %Sec5, %Sec5sub, %Sec6, %Sec6sub,
+       %Sec7, %Sec7sub, %Sec8, %Sec8sub, %Sec9, %Sec9sub,
+       %SecN, %SecNsub, %SecNsec);
+
+    printhead()  unless $Bare;
+    print $OutFH "<!-- Man keyword results converted by ",
+		      "man2html $VERSION -->\n";
+
+    while ($line = <$InFH>) {
+	next if $line !~ /\(\d\w?\)\s+-\s/; # check if line can be handled
+	($refs,$section,$subsection,$desc) =
+	    $line =~ /^\s*(.*)\((\d)(\w?)\)\s*-\s*(.*)$/;
+
+	if ($Solaris) {
+	    $refs =~ s/^\s*([\+_\.\w-]+)\s+([\+_\.\w-]+)\s*$/$1/;
+					#  <topic> <manpage>
+	} else {
+	    $refs =~ s/\s(and|or)\s/,/gi; # Convert and/or to commas
+	    $refs =~ s/^[^:\s]:\s*//;	# Remove prefixed whatis path
+	}
+	$refs =~ s/\s//g;		# Remove all whitespace
+	$refs =~ s/,/, /g;		# Put space after comma
+	htmlize(\$desc);		# Check for special chars in desc
+	$desc =~ s/^(.)/\U$1/;		# Uppercase first letter in desc
+
+	if ($section eq '1') {
+	    $Sec1{$refs} = $desc; $Sec1sub{$refs} = $subsection;
+	} elsif ($section eq '2') {
+	    $Sec2{$refs} = $desc; $Sec2sub{$refs} = $subsection;
+	} elsif ($section eq '3') {
+	    $Sec3{$refs} = $desc; $Sec3sub{$refs} = $subsection;
+	} elsif ($section eq '4') {
+	    $Sec4{$refs} = $desc; $Sec4sub{$refs} = $subsection;
+	} elsif ($section eq '5') {
+	    $Sec5{$refs} = $desc; $Sec5sub{$refs} = $subsection;
+	} elsif ($section eq '6') {
+	    $Sec6{$refs} = $desc; $Sec6sub{$refs} = $subsection;
+	} elsif ($section eq '7') {
+	    $Sec7{$refs} = $desc; $Sec7sub{$refs} = $subsection;
+	} elsif ($section eq '8') {
+	    $Sec8{$refs} = $desc; $Sec8sub{$refs} = $subsection;
+	} elsif ($section eq '9') {
+	    $Sec9{$refs} = $desc; $Sec9sub{$refs} = $subsection;
+	} else {			# Catch all
+	    $SecN{$refs} = $desc; $SecNsec{$refs} = $section;
+	    $SecNsub{$refs} = $subsection;
+	}
+    }
+    print_mank_sec(\%Sec1, 1, \%Sec1sub);
+    print_mank_sec(\%Sec2, 2, \%Sec2sub);
+    print_mank_sec(\%Sec3, 3, \%Sec3sub);
+    print_mank_sec(\%Sec4, 4, \%Sec4sub);
+    print_mank_sec(\%Sec5, 5, \%Sec5sub);
+    print_mank_sec(\%Sec6, 6, \%Sec6sub);
+    print_mank_sec(\%Sec7, 7, \%Sec7sub);
+    print_mank_sec(\%Sec8, 8, \%Sec8sub);
+    print_mank_sec(\%Sec9, 9, \%Sec9sub);
+    print_mank_sec(\%SecN, 'N', \%SecNsub, \%SecNsec);
+
+    printtail()  unless $Bare;
+}
+##---------------------------------------------------------------------------
+##	print_mank_sec() prints out manpage cross-refs of a specific section.
+##
+sub print_mank_sec {
+    my($sec, $sect, $secsub, $secsec) = @_;
+    my(@array, @refs, $href, $item, $title, $subsection, $i, $section,
+       $xref);
+    $section = $sect;
+
+    @array = sort keys %$sec;
+    if ($#array >= 0) {
+	print $OutFH "<H2>Section $section</H2>\n",
+		     "<DL COMPACT>\n";
+	foreach $item (@array) {
+	    @refs = split(/,/, $item);
+	    $section = $secsec->{$item}  if $sect eq 'N';
+	    $subsection = $secsub->{$item};
+	    if ($CgiUrl) {
+		($title = $refs[0]) =~ s/\(\)//g;  # watch out for extra ()'s
+		$xref = eval $CgiUrl;
+	    }
+	    print $OutFH "<DT>\n";
+	    $i = 0;
+	    foreach (@refs) {
+		if ($CgiUrl) {
+		    print $OutFH qq|<B><A HREF="$xref">$_</A></B>|;
+		} else {
+		    print $OutFH $_;
+		}
+		print $OutFH ", "  if $i < $#refs;
+		$i++;
+	    }
+	    print $OutFH " ($section$subsection)\n",
+			 "</DT><DD>\n",
+			 $sec->{$item}, "</DD>\n";
+	}
+	print $OutFH "</DL>\n";
+    }
+}
+
+##---------------------------------------------------------------------------
+##
+sub usage {
+    print $OutFH <<EndOfUsage;
+Usage: $PROG [ options ] < infile > outfile
+Options:
+  -bare		   : Do not put in HTML, HEAD, BODY tags
+  -belem <elem>    : HTML Element for overstriked text (def: "B")
+  -botm <#>	   : Number of lines for bottom margin (def: 7)
+  -cgiurl <url>    : URL for linking to other manpages
+  -cgiurlexp <url> : Perl expression URL for linking to other manpages
+  -compress	   : Compress consective blank lines
+  -headmap <file>  : Filename of user section head map file
+  -help		   : This message
+  -k		   : Process a keyword search result
+  -leftm <#>       : Character width of left margin (def: 0)
+  -nodepage	   : Do not remove pagination lines
+  -noheads	   : Turn off section head detection
+  -pgsize <#>      : Number of lines in a page (def: 66)
+  -seealso	   : Link to other manpages only in the SEE ALSO section
+  -solaris	   : Process keyword search result in Solaris format
+  -sun		   : Section heads are not overstriked in input
+  -title <string>  : Title of manpage (def: Not defined)
+  -topm <#>	   : Number of lines for top margin (def: 7)
+  -uelem <elem>    : HTML Element for underlined text (def: "I")
+
+Description:
+  $PROG takes formatted manpages from STDIN and converts it to HTML sent
+  to STDOUT.  The -topm and -botm arguments are the number of lines to the
+  main body text and NOT to the running headers/footers.
+
+Version:
+  $VERSION
+  Copyright (C) 1995-1997  Earl Hood, ehood\@medusa.acs.uci.edu
+  $PROG comes with ABSOLUTELY NO WARRANTY and $PROG may be copied only
+  under the terms of the GNU General Public License, which may be found in
+  the $PROG distribution.
+
+EndOfUsage
+    exit 0;
+}
diff -r 000000000000 -r c7f6b056b673 misc/newwebuser.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/newwebuser.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,157 @@
+#! /bin/sh -e
+
+# create a per-user whitelist target directory, password, and so forth.
+
+# This script creates the directories needed for per-addressee white
+#   lists and log directories.  It also runs htpasswd to create an entry
+#   in the Apache password file for the userdirs directory.
+
+# The files and directories must be writable by both the httpd and dccm
+#   processes.  That is assumed to be arranged by having the processes share
+#   a group such as "www" and using a umask of 007.
+#   The dccm log directories and files should not be globally readable
+#   to protect the privacy of mail.
+#   If dccm is run by a "user" such as "dcc", you might be able to
+#   use suEXEC.  You might need to make a symbolic of ~dcc/public_html"
+#   to ~dcc/userdirs.
+
+# The web "usernames" are related the per-user white list directory names
+#   see in DCC log files.  The white list and log directory in
+#   "userdirs/local/xxx" is accessed with the user name "xxx".
+#   "userdirs/esmtp/xxx@example.com" uses the user name "esmtp/xxx@example.com
+
+# One additional directory named @prefix@/userdirs/tmp is created for the
+#   CGI scripts that manage the per-user white lists and logs.
+
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.11 $Revision$
+#	@configure_input@
+
+
+exec 1>&2 </dev/null
+
+# Since the CGI scripts cannot accept args and so must rely on the
+#   DCC parameter file in the built-in homedir, do the same here.
+DCC_HOMEDIR=@prefix@
+. $DCC_HOMEDIR/dcc_conf
+
+USAGE="`basename $0`: [-x] [-p htpasswd-pgm] [-g group] [-P whitelist-prototype] username"
+HTPASSWD=@HTPASSWD@
+GROUP=www
+while getopts "xp:g:P:" c; do
+    case $c in
+	x) set -x;;
+	p) HTPASSWD=$OPTARG;;
+	g) GROUP="$OPTARG";;	    #GID shared with httpd
+	P) PROTO="$OPTARG";;
+	*) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 1 -o -z "$1"; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+USER=$1
+
+if test -z "$DCCM_USERDIRS"; then
+    cat <<EOF 1>&2
+Per-user white lists require DCCM_USERDIRS defined in$DCC_HOMEDIR/dcc_conf.
+Please consider installing a new version of dcc_conf.
+EOF
+    exit 1
+fi
+
+if test ! -r $DCC_HOMEDIR/ids; then
+    if test -n "$DCCUID"; then
+	echo "`basename $0: must be run by root or $DCCUID" 1>&2
+    else
+	echo "`basename $0: must be run by root" 1>&2
+    fi
+    exit 1
+fi
+
+
+USERDIRS=$DCC_HOMEDIR/$DCCM_USERDIRS
+WEBUSERS=$USERDIRS/webusers
+TMP_DIR=$USERDIRS/tmp
+USER=`expr "$USER" : "$DCCM_USERDIRS/\(.*\)" \| "$USER"`
+LOCAL_DIR=`expr "$USER" : '\(..*\)/..*' || true`
+if test -z "$LOCAL_DIR"; then
+    LOCAL_DIR="$USERDIRS/local"
+    PER_USER="$USERDIRS/local/$USER"
+else
+    USER=`expr "$USER" : 'local/\(.*\)' \| "$USER"`
+    LOCAL_DIR="$USERDIRS/$LOCAL_DIR"
+    PER_USER="$USERDIRS/$USER"
+fi
+
+# create the directories
+#   (some systems don't have `mkdir -p`)
+for nm in "$USERDIRS" "$TMP_DIR" "$LOCAL_DIR" "$PER_USER" "$PER_USER/log"; do
+    if test ! -d "$nm"; then
+	mkdir "$nm"
+    fi
+done
+
+# create the initial whiteclnt file
+#   copy the prototype
+if test ! -s $PER_USER/whiteclnt; then
+    date "+# white list for $USER%n#%n#%n#webuser created %x %X %Z%n" \
+	> "$PER_USER/whiteclnt"
+    if test "$PROTO" != ""; then
+	cat "$PROTO" >> "$PER_USER/whiteclnt"
+    fi
+fi
+
+# Add the username and password to the htpasswd file
+#   Create the htpasswd file for the first user.
+if test ! -f $WEBUSERS; then
+    $HTPASSWD -c $WEBUSERS "$USER"
+else
+    $HTPASSWD $WEBUSERS "$USER"
+fi
+
+# set permissions of existing files and directories in case they're wrong
+chgrp $GROUP "$USERDIRS" "$TMP_DIR" "$PER_USER" "$PER_USER/log"
+chgrp $GROUP "$PER_USER/whiteclnt" $WEBUSERS
+if test -n "$DCCUID"; then
+    chown $DCCUID $WEBUSERS "$USERDIRS" "$TMP_DIR" "$LOCAL_DIR"
+    chown $DCCUID "$PER_USER" "$PER_USER/log" "$PER_USER/whiteclnt"
+fi
+chmod g=rwx "$USERDIRS" "$TMP_DIR" "$PER_USER" "$PER_USER/log"
+chmod g=rw "$PER_USER/whiteclnt" $WEBUSERS
+chmod o= "$PER_USER/log" $WEBUSERS
diff -r 000000000000 -r c7f6b056b673 misc/rcDCC.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/rcDCC.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,185 @@
+#!/bin/sh
+
+# chkconfig: 2345 40 80
+# description: rcDCC is a startup script for DCC  http://www.rhyolite.com/dcc/
+# processname: dccd
+# processname: dccm
+# processname: dccifd
+
+### BEGIN INIT INFO
+# Provides:	    dccd dccm dccifd
+# Required-Start:   $network $named $syslog $time
+# Should-Start:	    ypbind sendmail
+# Required-Stop:
+# Should-Stop:
+# Default-Start:     3 5
+# Default-Stop:
+# Short-Description: DCC daemons providing mail filtering
+# Description:       Start dccm, dccifd, and/or dccd to support DCC mail
+#	filtering via sendmail, postfix, or SpamAssassin
+### END INIT INFO
+
+
+
+# start or stop dccd, dccifd, and dccm for Solaris, System V, IRIX,
+#   Redhat Linux, or FreeBSD
+
+# dccm must be started before sendmail and stopped after sendmail to avoid
+#   complaints from sendmail
+# dccd must be should before the first mail message arrives
+# Thus on some Solaris systems, this script can be installed as
+#   rc0.d/K37DCC  rc1.d/K37DCC  rc2.d/S88DCC  rcS.d/K37DCC
+# On some Linux systems it can be installed as rc.d/init.d/DCC and linked
+#   to rc.d/rc0.d/K31DCC rc.d/rc1.d/K31DCC rc.d/rc2.d/S80DCC rc.d/rc3.d/S80DCC
+#   rc.d/rc4.d/S80DCC rc.d/rc5.d/S80DCC and rc.d/rc6.d/K31DCC
+# On other Linux systems such as SUSE, link this script to /etc/init.d/DCC and
+#   run `insserv DCC`
+# On FreeBSD 5.* and 6.* systems, link this file to
+#	/usr/local/etc/rc.d/rcDCC.sh
+
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.36 $Revision$
+#	@configure_input@
+
+
+DCC_HOMEDIR=@prefix@
+DCC_LIBEXEC=@libexecdir@	    # in case dcc_conf has disappeared
+. $DCC_HOMEDIR/dcc_conf
+if test -z "$DCC_RUNDIR" -a -n "$DCCM_RUNDIR"; then
+    # deal with old dcc_conf files
+    DCC_RUNDIR="$DCCM_RUNDIR"
+fi
+
+USAGE="`basename $0`: [-x] [-m dccd|dccifd|dccm|grey] start|stop"
+
+DEBUG=
+DAEMON=
+while getopts "xm:" c; do
+    case $c in
+	x) set -x; DEBUG=-x;;
+	m) if test "$OPTARG" = dccd -o "$OPTARG" = dccifd		\
+			-o "$OPTARG" = dccm -o "$OPTARG" = grey; then
+		DAEMON=$OPTARG
+	    else
+		echo "unrecognized daemon $OPTARG" 1>&2
+		DAEMON=
+	    fi
+	    ;;
+	*) echo "$USAGE" 1>&2;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+
+
+MODULE=
+if test `uname` = Linux; then
+    # Convince the Redhat shutdown mechanism to invoke us at shutdown
+    # by including this string:
+    # . /etc/rc.d/init.d/functions
+
+    # Some Linux systems need a module name in /var/lock/subsys that matches
+    # the name to which this script is linked to /etc/rc.d/rc?.d for the
+    # shutdown or Kxx script.
+    MODULE=`basename $0 | sed -n -e 's/^[SK][0-9][0-9]\(.*\)/\1/p'`
+    # If @libexecdir@/updatedcc runs @libexecdir@/rcDCC, then the running name
+    # of this program will not be the needed module name
+    if test -z "$MODULE" -a -d /etc/rc.d; then
+	for NM in `find /etc/rc.d -follow -name 'K[0-9][0-9]*dcc*'	\
+				-o -name 'K[0-9][0-9]*DCC*'`; do
+	  if test -n "`grep ddb7d6d95cbf42fd4fdb $NM 2>/dev/null`"; then
+	      MODULE=`basename $NM | sed -e 's/^K[0-9][0-9]//'`
+	      break
+	  fi
+	done
+    fi
+fi
+
+
+case "$1" in
+start|restart|reload)
+	if test -z "$DAEMON" -o "$DAEMON" = dccd; then
+	    $DCC_LIBEXEC/start-dccd $DEBUG
+	fi
+	if test -z "$DAEMON" -o "$DAEMON" = grey; then
+	    $DCC_LIBEXEC/start-grey $DEBUG
+	fi
+	if test -z "$DAEMON" -o "$DAEMON" = dccm; then
+	    $DCC_LIBEXEC/start-dccm $DEBUG
+	fi
+	if test -z "$DAEMON" -o "$DAEMON" = dccifd; then
+	    $DCC_LIBEXEC/start-dccifd $DEBUG
+	fi
+	if test -n "$MODULE" -a -d /var/lock/subsys; then
+	    touch /var/lock/subsys/$MODULE
+	fi
+	;;
+
+stop)
+	# stop dccm and dccifd
+	PIDS=
+	if test -z "$DAEMON" -o "$DAEMON" = dccm; then
+	    PIDS="$PIDS `cat $DCC_RUNDIR/dccm.pid 2>/dev/null`"
+	    PIDS="$PIDS `cat $DCC_HOMEDIR/dccm.pid 2>/dev/null`"
+	fi
+	if test -z "$DAEMON" -o "$DAEMON" = dccifd 2>/dev/null; then
+	    PIDS="$PIDS `cat $DCC_RUNDIR/dccifd.pid 2>/dev/null`"
+	    PIDS="$PIDS `cat $DCC_HOMEDIR/dccifd.pid 2>/dev/null`"
+	fi
+	if expr "$PIDS" : '.*[0-9]' >/dev/null; then
+	    # signal the process group because on Linux systems signaling the
+	    # main process for a threaded application does not work.
+	      /bin/kill -15 $PIDS
+	fi
+	# Tell dccd about operating system shutdown, including deleting
+	# dcc_db.hash, which will be rebuilt by dbclean when dccd is restarted.
+	if test -z "$DAEMON" -o "$DAEMON" = dccd; then
+	    $DCC_LIBEXEC/stop-dccd -S $DEBUG
+	fi
+	if test -z "$DAEMON" -o "$DAEMON" = grey; then
+	    $DCC_LIBEXEC/stop-dccd -G -S $DEBUG
+	fi
+	if test -n "$MODULE" -a -f /var/lock/subsys/$MODULE; then
+	    rm /var/lock/subsys/$MODULE
+	fi
+	;;
+
+*)
+	echo "$USAGE" 1>&2
+	exit 1
+	;;
+
+esac
+exit 0
diff -r 000000000000 -r c7f6b056b673 misc/site.config.m4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/site.config.m4	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,12 @@
+dnl Milter
+dnl	add Milter for DCC Rhyolite Software DCC 1.3.103-1.4 $Revision$
+dnl
+dnl	For version 8.11 of sendmail, use something like this in a file in
+dnl	the sendmail*/devtools/Site directory in a file named site.config.m4,
+dnl	site.OS.$SENDMAIL_SUFFIX.m4, or site.OS.m4, as described in
+dnl	devtools/Site/README
+APPENDDEF(`conf_sendmail_ENVDEF', `-D_FFR_MILTER=1')
+APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_MILTER=1')
+dnl
+dnl	For version 8.12 of sendmail, use something like the following:
+APPENDDEF(`conf_sendmail_ENVDEF', `-DMILTER')
diff -r 000000000000 -r c7f6b056b673 misc/start-dccd.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/start-dccd.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,133 @@
+#! /bin/sh
+
+# start dccd
+# This script assumes that it is being run by root or the DCC user, probably
+# while the system is starting.  If start-dccm or start-dccifd,
+# This script should be run before both of them to # avoid complaints.
+# See the misc/rcDCC script.
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.55 $Revision$
+#	@configure_input@
+
+exec 1>&2 </dev/null
+
+LOGGER_TAG=start-dccd
+DCC_LOGGER="@DCC_LOGGER@"
+DCC_HOMEDIR=@prefix@
+VERBOSE=
+DEBUG=
+STOP_ARGS=
+# check the args once to get the home directory
+while getopts ":xvh:u:a:i:n:" c; do
+    case $c in
+	x) set -x; DEBUG=-x; VERBOSE=-v;;
+	v) VERBOSE=-v;;
+	h) CWD=`pwd`
+	    DCC_HOMEDIR=`echo "$OPTARG" | sed -e "s@^[^/]@$CWD/&@"`;;
+	*) ;;
+    esac
+done
+if test ! -s $DCC_HOMEDIR/dcc_conf; then
+    eval $DCC_LOGGER "cannot find $DCC_HOMEDIR/dcc_conf"
+fi
+. $DCC_HOMEDIR/dcc_conf
+# deal with bash reserved $UID and old versions of dcc_conf
+if test 0"$DCC_CONF_VERSION" -lt 2 -a -z "$DCCUID" -a -n "$UID"; then
+    DCCUID="$UID"
+fi
+DCCD_ARGS="$DCC_LOG_ARGS $DCCD_ARGS"
+
+USAGE="`basename $0`: [-xv] [-h homedir] [-u UID] [-a args] [-i id] [-n brand]"
+OPTIND=1
+while getopts "xvh:u:a:i:n:" c; do
+    case $c in
+	x) ;;
+	v) ;;
+	h) ;;
+	u) DCCUID="$OPTARG";;
+	a) DCCD_ARGS="$DCCD_ARGS $OPTARG";;
+	i) SRVR_ID="$OPTARG"; STOP_ARGS="$STOP_ARGS -i $SRVR_ID";;
+	n) BRAND="$OPTARG";;
+	*) eval $DCC_LOGGER "$USAGE"; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    eval $DCC_LOGGER "$USAGE"
+    exit 1
+fi
+
+if test "$DCC_HOMEDIR" != @prefix@; then
+    DCCD_ARGS="-h$DCC_HOMEDIR $DCCD_ARGS"
+    STOP_ARGS="-h$DCC_HOMEDIR $STOP_ARGS"
+fi
+if test "$BRAND" != ""; then
+    BRAND="-n $BRAND"
+fi
+DCCD_ARGS="-i$SRVR_ID $BRAND $DCCD_ARGS"
+
+# stop the current daemon, including giving it time to shut down gracefully
+$DCC_LIBEXEC/stop-dccd $DEBUG $VERBOSE $STOP_ARGS
+
+# tell dccd where to find dbclean if it is not where dccd was built to look.
+if test "$DCC_LIBEXEC" != @libexecdir@; then
+    DCCD_ARGS="-C $DCC_LIBEXEC/dbclean $DCCD_ARGS"
+fi
+
+# assume unset DCCD_ENABLE is "on" for upward compatibility
+case "$DCCD_ENABLE" in
+    [oO][fF][fF]) exit 0;;
+esac
+if test ! -x $DCC_LIBEXEC/dccd; then
+    eval $DCC_LOGGER "cannot start dcccd because dccd has not been installed"
+    exit 1
+fi
+if test -z "$SRVR_ID"; then
+    eval $DCC_LOGGER "'cannot start dccd; SRVR_ID not set in $DCC_HOMEDIR/dcc_conf'"
+    exit 1
+fi
+
+# first try to use `su -` to set limits
+#   fall back to -I for systems where su requires a valid shell in
+#	/etc/passwd but where the dcc user does not have one
+if test -n "$DCCUID" -a X"$DCCUID" != X"$USER"; then
+    if @DCC_SU@ $DCCUID -c date >/dev/null 2>&1; then
+	@DCC_SU@ $DCCUID -c "$DCC_LIBEXEC/dccd $DCCD_ARGS"
+	exit
+    fi
+    DCCD_ARGS="-I,$DCCUID $DCCD_ARGS"
+fi
+eval $DCC_LIBEXEC/dccd $DCCD_ARGS
diff -r 000000000000 -r c7f6b056b673 misc/start-dccifd.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/start-dccifd.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,199 @@
+#! /bin/sh
+
+# start dccifd
+
+# This script assumes that it is being run by root or the DCC user, probably
+# while the system is starting.  See the misc/rcDCC script.
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.31 $Revision$
+#	@configure_input@
+
+exec 1>&2 </dev/null
+
+LOGGER_TAG=start-dccifd
+DCC_LOGGER="@DCC_LOGGER@"
+DCC_HOMEDIR=@prefix@
+QUIET=yes
+AUTOSTART=
+# check the args once to get the home directory
+while getopts ":xvAh:r:a:" c; do
+    case $c in
+	x) set -x;;
+	v) QUIET=;;
+	A) AUTOSTART=y;;
+	h) DCC_HOMEDIR="$OPTARG";;
+	*) ;;
+    esac
+done
+if test ! -s $DCC_HOMEDIR/dcc_conf; then
+    eval $DCC_LOGGER "cannot find $DCC_HOMEDIR/dcc_conf"
+fi
+. $DCC_HOMEDIR/dcc_conf
+
+if test "$DCC_HOMEDIR" != @prefix@; then
+    DCCIFD_ARGS="-h$DCC_HOMEDIR $DCCIFD_ARGS"
+fi
+
+if test -n "$GREY_CLIENT_ARGS"; then
+    if test X`expr "X$GREY_CLIENT_ARGS" : '.*\(-G\)'` = X; then
+	GREY_CLIENT_ARGS="-G$GREY_CLIENT_ARGS"
+    fi
+    DCCIFD_ARGS="$GREY_CLIENT_ARGS $DCCIFD_ARGS"
+fi
+
+if test -n "$DCCIFD_USERDIRS"; then
+    DCCIFD_ARGS="-U$DCCIFD_USERDIRS $DCCIFD_ARGS"
+fi
+if test -n "$DCCIFD_WHITECLNT"; then
+    DCCIFD_ARGS="-w$DCCIFD_WHITECLNT $DCCIFD_ARGS"
+fi
+if test -n "$DCCIFD_LOGDIR"; then
+    # add extra quotes for '?' not present in dcc_conf
+    if expr "$DCCIFD_LOGDIR" : '[DHM]?' >/dev/null; then
+	DCCIFD_ARGS="'-l$DCCIFD_LOGDIR' $DCCIFD_ARGS"
+    else
+	DCCIFD_ARGS="-l$DCCIFD_LOGDIR $DCCIFD_ARGS"
+    fi
+fi
+
+CKS="$REP_ARGS"
+if test -n "$DCCIFD_LOG_AT$DCCIFD_REJECT_AT"; then
+    for CK in ${DCCIFD_CKSUMS:=CMN} $DCCIFD_XTRA_CKSUMS; do
+	CKS="$CKS -t$CK,$DCCIFD_LOG_AT,$DCCIFD_REJECT_AT"
+    done
+fi
+
+DCCIFD_ARGS="$CKS $DNSBL_ARGS $DCC_LOG_ARGS $DCCIFD_ARGS"
+
+
+
+USAGE="`basename $0`: [-xv] [-h homedir] [-r rundir] [-a args]"
+OPTIND=1
+while getopts "xvAh:r:a:" c; do
+    case $c in
+	x) ;;
+	v) ;;
+	A) ;;
+	h) ;;
+	r) DCC_RUNDIR="$OPTARG";;
+	a) DCCIFD_ARGS="$DCCIFD_ARGS $OPTARG";;
+	*) eval $DCC_LOGGER "$USAGE"; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    eval $DCC_LOGGER "$USAGE"
+    exit 1
+fi
+
+if test -n "$DCC_RUNDIR" -a "$DCC_RUNDIR" != @dcc_rundir@; then
+    DCCIFD_ARGS="-R$DCC_RUNDIR $DCCIFD_ARGS"
+fi
+
+
+# stop an existing dccifd
+#   signal the process group because on Linux systems signaling the main
+#   process does not work.  See `man sigwait` on a Linux system.
+PID=`cat $DCC_RUNDIR/dccifd.pid 2>/dev/null`
+if test -n "$PID"; then
+    if test -n "$AUTOSTART"; then
+	if kill -0 "$PID" 2>/dev/null; then
+	    eval $DCC_LOGGER "dccifd already running"
+	    exit 0
+	fi
+    else
+	if `kill -15 $PID 2>/dev/null`; then
+	    CNT=0
+	    # try for 20 seconds to let Solaris finish waiting for DNSBL answers
+	    while test $CNT -lt 20; do
+		if `/bin/kill -15 $PID 2>/dev/null`; then
+		    if test -z "$QUIET"; then
+			if test $CNT -eq 0; then
+			    echo "    stopping dccifd PID $PID " | tr -d '\012'
+			else
+			    echo "." | tr -d '\012'
+			fi
+		    fi
+		    sleep 1
+		    CNT=`expr $CNT + 1`
+		else
+		    break
+		fi
+	    done
+	    /bin/kill -9 $PID 2>/dev/null
+	    if test $CNT -ne 0 -a -z "$QUIET"; then
+		echo
+	    fi
+	fi
+    fi
+fi
+
+
+if test -z "$AUTOSTART"; then
+    case "$DCCIFD_ENABLE" in
+	[oO][nN]);;
+	*) exit 0 ;;
+    esac
+fi
+
+if test ! -x $DCC_LIBEXEC/dccifd; then
+    if test -z "$AUTOSTART"; then
+	eval $DCC_LOGGER "cannot start dccifd because it has not been installed"
+    fi
+    exit 0
+fi
+
+if test ! -d $DCC_RUNDIR; then
+    mkdir $DCC_RUNDIR
+fi
+
+# first try to use `su -` to set limits
+#   fall back to -I for systems where su requires a valid shell in
+#	/etc/passwd but where the dcc user does not have one
+if test -n "$DCCUID" -a X"$DCCUID" != X"$USER"; then
+    if test -d "$DCC_HOMEDIR"; then
+	chown $DCCUID "$DCC_HOMEDIR"
+    fi
+    if test -d "$DCC_RUNDIR"; then
+	chown $DCCUID "$DCC_RUNDIR"
+    fi
+    if @DCC_SU@ $DCCUID -c date >/dev/null 2>&1; then
+	@DCC_SU@ $DCCUID -c "$DCC_LIBEXEC/dccifd $DCCIFD_ARGS"
+	exit
+    fi
+    DCCIFD_ARGS="-I$DCCUID $DCCIFD_ARGS"
+fi
+eval $DCC_LIBEXEC/dccifd $DCCIFD_ARGS
diff -r 000000000000 -r c7f6b056b673 misc/start-dccm.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/start-dccm.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,209 @@
+#! /bin/sh
+
+# start dccm
+
+# This script assumes that it is being run by root or the DCC user, probably
+# while the system is starting.  It should be run before sendmail is started
+# to avoid complaints from sendmail about the socket not existing.
+# See the misc/rcDCC script.
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.72 $Revision$
+#	@configure_input@
+
+exec 1>&2 </dev/null
+
+ARGV0=dccm
+LOGGER_TAG=start-dccm
+DCC_LOGGER="@DCC_LOGGER@"
+DCC_HOMEDIR=@prefix@
+QUIET=yes
+# check the args once to get the home directory
+while getopts ":xvh:r:u:c:a:l:L:R:w:" c; do
+    case $c in
+	x) set -x;;
+	v) QUIET=;;
+	h) DCC_HOMEDIR="$OPTARG";;
+	*) ;;
+    esac
+done
+if test ! -s $DCC_HOMEDIR/dcc_conf; then
+    eval $DCC_LOGGER "cannot find $DCC_HOMEDIR/dcc_conf"
+fi
+. $DCC_HOMEDIR/dcc_conf
+if test 0"$DCC_CONF_VERSION" -lt 2; then
+    # deal with bash reserved $UID and old versions of dcc_conf
+    if test -z "$DCCUID" -a -n "$UID"; then
+	DCCUID="$UID"
+    fi
+    if test -z "$DCC_RUNDIR" -a -n "$DCCM_RUNDIR"; then
+	DCC_RUNDIR="$DCCM_RUNDIR"
+    fi
+fi
+
+if test "$DCC_HOMEDIR" != @prefix@; then
+    DCCM_ARGS="-h$DCC_HOMEDIR $DCCM_ARGS"
+fi
+
+# convert old `dccm -G` to `dccm -Gon`
+DCCM_ARGS=`echo "$DCCM_ARGS"						\
+	| sed -e 's/-\([VdbxANQW]\{1,\}\)G/-\1 -G/g'			\
+	    -e 's/-G\([^- ]\)/-G -\1/g'					\
+	    -e 's/-G/-Gon/g'  -e 's/-G[	 ]*on[	 ]*on/-Gon/g'`
+if test -n "$GREY_CLIENT_ARGS"; then
+    if test X`expr "X$GREY_CLIENT_ARGS" : '.*\(-G\)'` = X; then
+	GREY_CLIENT_ARGS="-G$GREY_CLIENT_ARGS"
+    fi
+    DCCM_ARGS="$GREY_CLIENT_ARGS $DCCM_ARGS"
+fi
+
+if test -n "$DCCM_USERDIRS"; then
+    DCCM_ARGS="-U$DCCM_USERDIRS $DCCM_ARGS"
+fi
+
+
+USAGE="`basename $0`: [-xv] [-h homedir] [-r rundir] [-u UID] [-a args] \
+	[-l logdir] [-L log-at] [-R reject_at] [-w whiteclnt]"
+OPTIND=1
+while getopts "xvh:r:u:c:a:l:L:R:w:" c; do
+    case $c in
+	x) ;;
+	v) ;;
+	h) ;;
+	r) DCC_RUNDIR="$OPTARG";;
+	u) DCCUID="$OPTARG";;
+	c) X=`expr "X$OPTARG" : '\(X[-._a-zA-Z0-9]*\)'`
+	    if test "$X" = X -o "$X" != "X$OPTARG"; then
+		eval $DCC_LOGGER "illegal -c $OPTARG"
+	    else
+		ARGV0="$OPTARG"
+	    fi;;
+	a) DCCM_ARGS="$DCCM_ARGS $OPTARG";;
+	l) DCCM_LOGDIR="$OPTARG";;
+	L) DCCM_LOG_AT="$OPTARG";;
+	R) DCCM_REJECT_AT="$OPTARG";;
+	w) DCCM_WHITECLNT="$OPTARG";;
+	*) eval $DCC_LOGGER "$USAGE"; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    eval $DCC_LOGGER "$USAGE"
+    exit 1
+fi
+
+if test -n "$DCC_RUNDIR" -a "$DCC_RUNDIR" != @dcc_rundir@; then
+    DCCM_ARGS="-R$DCC_RUNDIR $DCCM_ARGS"
+fi
+if test -n "$DCCM_LOGDIR"; then
+    # add extra quotes for '?' not present in dcc_conf
+    if expr "$DCCM_LOGDIR" : '[DHM]?' >/dev/null; then
+	DCCM_ARGS="'-l$DCCM_LOGDIR' $DCCM_ARGS"
+    else
+	DCCM_ARGS="-l$DCCM_LOGDIR $DCCM_ARGS"
+    fi
+fi
+CKS="$REP_ARGS"
+if test -n "$DCCM_LOG_AT$DCCM_REJECT_AT"; then
+    for CK in ${DCCM_CKSUMS:=CMN} $DCCM_XTRA_CKSUMS; do
+	CKS="$CKS -t$CK,$DCCM_LOG_AT,$DCCM_REJECT_AT"
+    done
+fi
+if test -n "$DCCM_WHITECLNT"; then
+    CKS="$CKS -w$DCCM_WHITECLNT"
+fi
+DCCM_ARGS="$CKS $DNSBL_ARGS $DCC_LOG_ARGS $DCCM_ARGS"
+
+
+# stop an existing dccm
+#   signal the process group because on Linux systems it does not work
+#   to signal the main process.  See `man sigwait` on a Linux system.
+PID=`cat $DCC_RUNDIR/$ARGV0.pid 2>/dev/null`
+if test -n "$PID"; then
+    if `kill -15 $PID 2>/dev/null`; then
+	CNT=0
+	while test $CNT -lt 6; do
+	    if `/bin/kill -15 $PID 2>/dev/null`; then
+		if test -z "$QUIET"; then
+		    if test $CNT -eq 0; then
+			echo "    stopping dccm PID $PID "  | tr -d '\012'
+		    else
+			echo "."  | tr -d '\012'
+		    fi
+		fi
+		sleep 1
+		CNT=`expr $CNT + 1`
+	    else
+		break
+	    fi
+	done
+	/bin/kill -9 $PID 2>/dev/null
+	if test $CNT -ne 0 -a -z "$QUIET"; then
+	    echo
+	fi
+    fi
+fi
+
+# assume unset DCCD_ENABLE is "on" for upward compatibility
+case "$DCCM_ENABLE" in
+    [oO][fF][fF]) exit 0;;
+esac
+if test ! -x $DCC_LIBEXEC/dccm; then
+    eval $DCC_LOGGER "cannot start dccm because it has not been installed"
+    exit 1
+fi
+
+if test ! -d $DCC_RUNDIR; then
+    mkdir $DCC_RUNDIR
+fi
+
+if test "$ARGV0" != dccm -a ! -s $DCC_LIBEXEC/$ARGV0; then
+    ln -s dccm $DCC_LIBEXEC/$ARGV0
+fi
+
+# first try to use `su -` to set limits
+#   fall back to -I for systems where su requires a valid shell in
+#	/etc/passwd but where the dcc user does not have one
+if test -n "$DCCUID" -a X"$DCCUID" != X"$USER"; then
+    if test -d $DCC_RUNDIR; then
+	chown $DCCUID $DCC_RUNDIR
+    fi
+    if @DCC_SU@ $DCCUID -c date >/dev/null 2>&1; then
+	@DCC_SU@ $DCCUID -c "$DCC_LIBEXEC/$ARGV0 $DCCM_ARGS"
+	exit
+    fi
+    DCCM_ARGS="-I$DCCUID $DCCM_ARGS"
+fi
+eval $DCC_LIBEXEC/$ARGV0 $DCCM_ARGS
diff -r 000000000000 -r c7f6b056b673 misc/start-grey.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/start-grey.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,149 @@
+#! /bin/sh
+
+# start dccd for greylisting
+# This script assumes that it is being run by root or the DCC user, probably
+# while the system is starting.  If start-dccm or start-dccifd,
+# This script should be run before both of them to # avoid complaints.
+# See the misc/rcDCC script.
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.18 $Revision$
+#	@configure_input@
+
+exec 1>&2 </dev/null
+
+LOGGER_TAG=start-grey-dccd
+DCC_LOGGER="@DCC_LOGGER@"
+DCC_HOMEDIR=@prefix@
+VERBOSE=
+DEBUG=
+# check the args once to get the home directory
+while getopts ":xvh:u:a:i:" c; do
+    case $c in
+	x) set -x; DEBUG=-x; VERBOSE=-v;;
+	v) VERBOSE=-v;;
+	h) CWD=`pwd`
+	    DCC_HOMEDIR=`echo "$OPTARG" | sed -e "s@^[^/]@$CWD/&@"`;;
+	*) ;;
+    esac
+done
+if test ! -s $DCC_HOMEDIR/dcc_conf; then
+    eval $DCC_LOGGER "cannot find $DCC_HOMEDIR/dcc_conf"
+fi
+. $DCC_HOMEDIR/dcc_conf
+# deal with bash reserved $UID
+if test -z "$DCCUID" -a -n "$UID"; then
+    DCCUID="$UID"
+fi
+if test "$BRAND" != ""; then
+    BRAND="-n $BRAND"
+fi
+GREY_DCCD_ARGS="$GREY_DCCD_ARGS $BRAND $DCC_LOG_ARGS"
+
+STOP_ARGS=
+USAGE="`basename $0`: [-xv] [-h homedir] [-u UID] [-a args] [-i id]"
+OPTIND=1
+while getopts "xvh:u:a:i:" c; do
+    case $c in
+	x) ;;
+	v) ;;
+	h) ;;
+	u) DCCUID="$OPTARG";;
+	a) GREY_DCCD_ARGS="$GREY_DCCD_ARGS $OPTARG";;
+	i) $GREY_SRVR_ID="$OPTARG"; STOP_ARGS="$STOP_ARGS -i $GREY_SRVR_ID";;
+	*) eval $DCC_LOGGER "$USAGE"; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    eval $DCC_LOGGER "$USAGE"
+    exit 1
+fi
+
+if test "$DCC_HOMEDIR" != @prefix@; then
+    GREY_DCCD_ARGS="-h $DCC_HOMEDIR $GREY_DCCD_ARGS"
+    STOP_ARGS="-h $DCC_HOMEDIR $STOP_ARGS"
+fi
+
+
+# stop the current daemon, including giving it time to shut down gracefully
+$DCC_LIBEXEC/stop-dccd -G $DEBUG $VERBOSE $STOP_ARGS
+
+case X"$GREY_ENABLE" in
+    X[oO][nN])
+	# start the greylist server
+	;;
+    X)
+	# if $GREY_CLIENT_ARGS contains "-G" and $GREY_ENABLE is not set
+	#   then start the greylist server
+	if test -z "$GREY_CLIENT_ARGS"; then
+	    exit 0
+	fi
+	;;
+    *) exit 0;;
+esac
+
+# tell dccd where to find dbclean if it is not where dccd was built to look.
+if test "$DCC_LIBEXEC" != @libexecdir@; then
+    GREY_DCCD_ARGS="-C $DCC_LIBEXEC/dbclean $GREY_DCCD_ARGS"
+fi
+
+if test ! -x $DCC_LIBEXEC/dccd; then
+    eval $DCC_LOGGER "cannot start greylist dcccd because dccd has not been installed"
+    exit 1
+fi
+
+if test -z "$GREY_SRVR_ID"; then
+    if test -n "`grep '# auto local greylist server-ID' $DCC_HOMEDIR/ids`" \
+	    -a -n "`grep '^32702' $DCC_HOMEDIR/ids`"; then
+	GREY_SRVR_ID=32702
+    else
+	eval $DCC_LOGGER "'cannot start greylist dccd; GREY_SRVR_ID not set in $DCC_HOMEDIR/dcc_conf'"
+	exit 1
+    fi
+fi
+GREY_DCCD_ARGS="-i $GREY_SRVR_ID $GREY_DCCD_ARGS"
+
+# first try to use `su -` to set limits
+#   fall back to -I for systems where su requires a valid shell in
+#	/etc/passwd but where the dcc user does not have one
+if test -n "$DCCUID" -a X"$DCCUID" != X"$USER"; then
+    if @DCC_SU@ $DCCUID -c date >/dev/null 2>&1; then
+	@DCC_SU@ $DCCUID -c "$DCC_LIBEXEC/dccd -Gon $GREY_DCCD_ARGS"
+	exit
+    fi
+    GREY_DCCD_ARGS="-I,$DCCUID $GREY_DCCD_ARGS"
+fi
+eval $DCC_LIBEXEC/dccd -Gon $GREY_DCCD_ARGS
diff -r 000000000000 -r c7f6b056b673 misc/stats-get.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/stats-get.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,147 @@
+#! /bin/sh
+
+# get statistics from a DCC server
+#   [-x]	    debugging
+#   [-q]	    quiet
+#   [-S]	    read `cdcc stats` from stdin
+#   [-s stats-file] save raw `cdcc stats` output in stats-file
+#   [-i client-ID]  that DCC server will accept
+#   [-p password]   that DCC server will accept
+#   host	    server to ask for numbers
+
+# A single line of 5 decimal numbers separated by colons is sent to stdout:
+#   For example,
+#	115492:71318:44694:2462909:155924
+#   means the DCC server has
+#	115492  received reports of checksums from DCC clients
+#	71318	received reports where the target count was >= 10
+#	44694	received reports where the target count was "many"
+#	2462909	entries in its hash table
+#	155924	received reports by flooding
+
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.20 $Revision$
+#	@configure_input@
+
+DEBUG=
+QUIET=
+STATSFILE=
+STATSFILE_NEW=/dev/null
+CLNT_ID="1"
+PASSWD=
+USAGE="`basename $0`: [-xqS] [-s stats-file] [-i client-ID] [-p password] [host]"
+while getopts "xqSs:i:p:" c; do
+    case $c in
+	x) set -x; DEBUG=-x;;
+	q) QUIET="-q";;
+	S) USE_STDIN=yes;;
+	s) if test $OPTARG != /dev/null; then
+		STATSFILE=$OPTARG
+		STATSFILE_NEW=$STATSFILE.$$
+		trap "/bin/rm -f $STATSFILE_NEW" 0 1 2 15
+	    fi
+	    ;;
+	i) CLNT_ID="$OPTARG";;
+	p) if test "$OPTARG" != ""; then
+		PASSWD="password $OPTARG;"
+	    fi
+	    ;;
+	*) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 1; then
+    if test "$#" -ne 0 -o -z "$USE_STDIN"; then
+	echo "$USAGE" 1>&2
+	exit 1
+    fi
+fi
+
+HOST=$1
+
+if test "$USE_STDIN" = yes; then
+    cat
+else
+    exec </dev/null
+    if test -n "$QUIET"; then
+	@bindir@/cdcc "id $CLNT_ID; $PASSWD host $HOST; stats" 2>/dev/null
+    else
+	@bindir@/cdcc "id $CLNT_ID; $PASSWD host $HOST; stats"
+    fi
+fi								\
+    | tee $STATSFILE_NEW					\
+    | awk '
+	/hash entries/{
+	    hashes = $4;
+	}
+	/query/{
+	    queries = $8;
+	    if (queries ~ /^-/) {
+		# negative numbers are 32-bit overflows
+		queries = 4294967296 + queries;
+	    }
+	}
+	/reports.*many/{
+	    reports = $1;
+	    if (reports ~ /^-/) {
+		reports = 4294967296 + reports;
+	    }
+	}
+	/answers.*>10/{
+	    split($2,bulk,">");
+	    if (bulk[1] ~ /^-/) {
+		bulk[1] = 4294967296 + bulk[1];
+	    }
+	    spam=$5;
+	    if (spam ~ /^-/) {
+		spam = 4294967296 + spam;
+	    }
+	}
+	/accepted/{
+	    flooded=$1;
+	    if (flooded ~ /^-/) {
+		flooded = 4294967296 + flooded;
+	    }
+	}
+	END {
+	    printf "%s:%s:%s:%s:%s\n",
+		    reports+queries, bulk[1], spam, hashes, flooded;
+	}'
+
+if test $STATSFILE_NEW != /dev/null -a -s $STATSFILE_NEW; then
+    rm -f $STATSFILE
+    mv $STATSFILE_NEW $STATSFILE
+fi
diff -r 000000000000 -r c7f6b056b673 misc/stop-dccd.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/stop-dccd.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,156 @@
+#! /bin/sh
+
+# stop dccd
+
+# This script assumes that it is being run by root or the DCC user, probably
+# while the system is stopping.
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.28 $Revision$
+#	@configure_input@
+
+exec 1>&2 </dev/null
+
+LOGGER_TAG=stop-dccd
+DCC_LOGGER="@DCC_LOGGER@"
+DCC_HOMEDIR=@prefix@
+CDCC_QUIET='>/dev/null 2>&1'
+VERBOSE=
+DEBUG=
+GREY=
+SYSSTOP=stop
+DB_NM=dcc_db
+# check the args once to get the home directory
+while getopts ":xvGSCh:u:i:n:" c; do
+    case $c in
+	x) set -x; DEBUG=-x; VERBOSE=-v; CDCC_QUIET=;;
+	v) VERBOSE=-v;;
+	h) DCC_HOMEDIR="$OPTARG";;
+	*) ;;
+    esac
+done
+. $DCC_HOMEDIR/dcc_conf
+
+USAGE="`basename $0`: [-xvGSC] [-h homedir] [-u UID] [-i id]"
+OPTIND=1
+while getopts "xvGSCh:u:i:n:" c; do
+    case $c in
+	x) ;;
+	v) ;;
+	G) GREY=yes DB_NM=grey_db;;
+	S) SYSSTOP="system stop";;
+	C) SYSSTOP="clean stop";;
+	h) ;;
+	u) DCCUID="$OPTARG";;
+	i) SRVR_ID="$OPTARG"; GREY_SRVR_ID="$OPTARG";;
+	n) ;;						# obsolete
+	*) eval $DCC_LOGGER "$USAGE"; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    eval $DCC_LOGGER "$USAGE"
+    exit 1
+fi
+
+if test "$DCC_HOMEDIR" != @prefix@; then
+    CDCC_ARGS="-h $DCC_HOMEDIR"
+fi
+
+if test -n "$GREY"; then
+    if test -z "$GREY_SRVR_ID"						    \
+	-a -n "`grep '# auto local greylist server-ID' $DCC_HOMEDIR/ids`"   \
+	-a -n "`grep '^32702' $DCC_HOMEDIR/ids`"; then
+	    GREY_SRVR_ID=32702
+    fi
+    SRVR_ID="$GREY_SRVR_ID"
+    DCCD_ARGS="$GREY_DCCD_ARGS"
+    CDCC_ARGS=" $CDCC_ARGS 'grey on'"
+fi
+
+if test -z "$SRVR_ID"; then
+    if test X"$DEBUG" = "X-x"; then
+	echo "SRVR_ID not set in $DCC_HOMEDIR/dcc_conf"
+    fi
+    exit
+fi
+
+# talk where the current server should be listening
+SRVR_ADDR=`expr X"$DCCD_ARGS" : '.*-a[	 ]*\([^	, ]*\)'`
+SRVR_PORT=`expr X"$DCCD_ARGS" : '.*-a[	 ]*[^	, ]*,\([^	, ]*\)'`
+if test -n "$SRVR_ADDR"; then
+    CDCC_ARGS="$CDCC_ARGS 'server $SRVR_ADDR'"
+fi
+if test -n "$SRVR_PORT"; then
+    CDCC_ARGS="$CDCC_ARGS 'port $SRVR_PORT'"
+fi
+
+CDCC0="@bindir@/cdcc $CDCC_ARGS 'id $SRVR_ID'"
+CDCC="$CDCC0 $CDCC_QUIET"
+
+# stop a current dbclean
+if test -s $DCC_HOMEDIR/$DB_NM.lock; then
+    kill `cat $DCC_HOMEDIR/$DB_NM.lock` >/dev/null 2>&1
+fi
+
+# stop the current daemon, including giving it time to shut down gracefully
+#   After telling the daemon to stop, keep telling the daemon to politely
+#   stop flooding until it stops answering or we run out of patience.
+#   Then tell it to pull the plug.
+if eval "$CDCC '$SYSSTOP' 'sleep 0.5'"; then
+    LCNT=0
+    while eval "$CDCC 'flood list' 'sleep 0.5'" ; do
+	LCNT=`expr $LCNT + 1`
+	# Pull the plug on the floods after 5 seconds
+	if test "$LCNT" -ge 10; then
+	    if test "$VERBOSE" = "-v"; then
+		if test "$LCNT" -eq 10; then
+		    eval "$CDCC0 'flood list'"
+		fi
+	    fi
+	    eval "$CDCC 'flood halt' 'sleep 0.5'"
+	fi
+	# give up after another second to let the daemon close the file
+	if test "$LCNT" -gt 12; then
+	    break;
+	fi
+    done
+fi
+
+set +x
+if test X"$DEBUG" = "X-x"; then
+    echo "The final cdcc command should have failed, since that is how"
+    echo "this script knows that dccd has stopped."
+fi
diff -r 000000000000 -r c7f6b056b673 misc/uninstalldcc.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/uninstalldcc.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,101 @@
+#! /bin/sh
+
+# Delete most DCC files.  
+#   Some logs configuration files with locally chosen parameters in @prefix@
+#   are not deleted.
+#   Manual changes such as links to @libexecdir@/rcDCC or the 
+#   installation of the cron job, @libexecdir@/cron-dccd, are not reversed.
+
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.3 $Revision$
+# @configure_input@
+
+DCC_HOMEDIR=@prefix@
+DCC_LIBEXEC=@libexecdir@	    # in case dcc_conf has disappeared
+. $DCC_HOMEDIR/dcc_conf
+BDIR=$DCC_HOMEDIR/build
+DCCBDIR=$BDIR/dcc
+
+USAGE=`basename $0`': [-x]'
+while getopts "x" c; do
+    case $c in
+    x) set -x; DEBUG=-x;;
+    *) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    echo "$USAGE" 1>&2; exit 1
+fi
+
+
+echo "This script will delete most of a DCC client and server installed"
+echo "with the official DCC Makefiles in $DCCBDIR."
+echo "Are you sure you want to proceed?  " | tr -d '\012'
+read OK
+case $OK in
+    [yY][eE][sS]) ;;
+    *) echo "ok, nothing deleted"; exit;;
+esac
+
+if test ! -f $DCCBDIR/Makefile; then
+    $DCC_LIBEXEC/updatedcc -U
+    if test ! -f $DCCBDIR/Makefile; then
+	echo "This script can only be run after $DCC_LIBEXEC/updatedcc has"
+	echo "installed the offical Makefiles in $DCCBDIR"
+	exit 1
+    fi
+fi
+
+$DCC_LIBEXEC/rcDCC $DEBUG stop
+cd $DCCBDIR
+@DCCMAKE@ uninstall purge
+rm -f $BDIR/dcc.tar.Z \
+    $DCC_HOMEDIR/flod.map $DCC_HOMEDIR/grey_flod.map			    \
+    $DCC_HOMEDIR/dcc_db $DCC_HOMEDIR/dcc_db.hash $DCC_HOMEDIR/dcc_db-old    \
+    $DCC_HOMEDIR/grey_db $DCC_HOMEDIR/grey_db.hash $DCC_HOMEDIR/grey_db-old \
+    $DCC_HOMEDIR/testmsg-whitelist $DCC_HOMEDIR/testmsg-whitelist.log	    \
+    $DCC_HOMEDIR/*.dccw $DCC_HOMEDIR/*.dccx
+for DIR in `find $DCC_HOMEDIR -depth -type d` $DCC_LIBEXEC $DCC_RUNDIR; do
+    if test -d $DIR; then
+	find $DIR -depth -type d | @DCC_XARGS@ rmdir
+    fi
+done
+
+echo
+echo "Some of your configuration and log files remain in $DCC_HOMEDIR"
+
diff -r 000000000000 -r c7f6b056b673 misc/updatedcc.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/updatedcc.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,597 @@
+#! /bin/sh
+
+# Fetch, build, install, and start a new version of the DCC just
+#   as it was configured last time.
+
+# This script should only be used after the DCC has been correctly configured.
+
+# If this script fails in its efforts to use wget, curl, fetch, or ftp
+#   to download a new tarball, then manually download a tarball to
+#   build subdirectory in the DCC home directory, probably @prefix@/build.
+#   This script unpacks the newest DCC tarball it finds in that directory,
+#   which will probably be the manually download file.
+
+
+# This script should usually not be used if large configuration changes are
+#   needed.  Instead use `./configure ...; make install` as usual.  That will
+#   create a new version of this script with the ./configure settings captured.
+#   However, -eENV=VAL and -cCONF=VAL can be used to tweak an installation,
+#	as in `updatedcc -c--with-uid=dcc` `updatedcc -c--without-uid`
+#	or `updatedcc -e DBGFLAGS=-g`
+
+#   -e ENV=VAL	set environment variable ENV to VAL before running ./configure
+
+#   -c CONF=VAL	add "CONF=VAL" to the end of the settings given ./configure
+
+#   -s base-URL	fetch the tarball using base-URL instead of the default.
+#		    The name of the tarball is appended to base-URL.
+
+#   -V x.y.z	try to fetch version x.y.z of the tarball instead of the
+#		    the most recent verison
+
+#   -C pfile	fetch theDCC source using parameters in pfile
+
+#   -T tgt	target instead of "install" for `make`
+
+#   -U		only fetch, unpack, and configure the tarball.  Do not compile
+#		    or install the software.
+
+#   -K		cron (kron) mode; configure, compile, and install only
+#		    if the fetched version differs, and be silent when
+#		    nothing is done
+
+#   -x		debug
+#   -v		verbose
+
+
+# This script should be run as root, but can be run as the non-root
+# user that runs dccm and dccd.  If not run as root, it cannot install
+# man pages or user commands including cdcc and dccproc.
+
+# The following environment variables are set by this script to their values
+#   when ./configure or this script was last run:
+#   Unsetting variables that are not set often yields a non-zero command status
+#   so do this before watching for error from mkdir and so forth
+export @UPDATEDCC_ENV@
+unset @UPDATEDCC_ENV@
+ENV_SET="@UPDATEDCC_ENV_SET@"
+@UPDATEDCC_ENV_SET@
+@UPDATEDCC_ENV_EXPORT@
+
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+#	Rhyolite Software DCC 1.3.103-1.111 $Revision$
+#	@configure_input@
+
+# English messages so grep can suppress them;
+#   simple collating sequence for sort
+#   sane gcc error messages
+LC_ALL=C; export LC_ALL
+
+
+RELEASE=1.3.103
+HTTP_REFERER=DCC-$RELEASE-script; export HTTP_REFERER
+
+DCC_HOMEDIR=@prefix@
+DCC_LIBEXEC=@libexecdir@	    # in case dcc_conf has disappeared
+. $DCC_HOMEDIR/dcc_conf
+BDIR=$DCC_HOMEDIR/build
+DCCDIR=$BDIR/dcc
+OK_MARK=$BDIR/.updatedcc_ok
+BURLS=
+VERSION=
+
+ME=`basename $0`
+USAGE="$ME: [-xvUK] [-e ENV=VAL] [-c CONF=VAL] [-s base-URL] [-V x.y.z]\\
+	[-C pfile] [-T make-tgt]"
+VERBOSE=
+DEBUG=
+UNPACK=
+CRON=
+CONFS=
+PFILE="@UPDATEDCC_PFILE@"
+MAKE_TGT=install
+while getopts "xvUKe:c:s:V:p:C:T:" c; do
+    case $c in
+    x) set -x; DEBUG=-x VERBOSE=yes;;
+    v) VERBOSE=yes;;
+    U) UNPACK=yes;;
+    K) CRON=yes;;
+    e) # quote any odd characters
+	SETTING=`echo "$OPTARG" | sed -e 's/\([^-_/:=+,.a-zA-Z0-9]\)/\\\\\1/g'`
+	eval "$SETTING"
+	export `expr "$OPTARG" : '\([^=]*\).*' || true`
+	ENV_SET="$ENV_SET $SETTING"
+	;;
+    c) CONFS="$CONFS $OPTARG";;
+    s) BURLS="$BURLS $OPTARG";;
+    V) VERSION=`expr "X$OPTARG" : 'X\([1-9]\.[1-9][0-9]*\.[1-9][0-9]*\)'`
+	if test "$VERSION" != "$OPTARG"; then
+	    echo "invalid -V $OPTARG" 1>&2; exit 1;
+	fi
+	;;
+    C) PFILE="$OPTARG";;
+    T) MAKE_TGT="$OPTARG";;
+    p) echo "All 3 DCC tarballs are now the same";;
+    *) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    echo "$USAGE" 1>&2; exit 1
+fi
+
+if test -z "$VERBOSE"; then
+    WGETQUIET='-nv'
+    CURLQUIET=
+    CONFQUIET='--quiet'
+    MQUIET=
+else
+    WGETQUIET=
+    CURLQUIET=
+    CONFQUIET=
+    MQUIET='#'
+fi
+
+# assume cron mode if stdin is not a tty
+if test -z "$CRON" && test -n "`tty 2>&1 | grep 'not a tty'`"; then
+    echo "assume -K because stdin is not a tty"
+    CRON=assumed
+fi
+exec </dev/null
+if test -n "$CRON"; then
+    # be quite in cron mode unless told otherwise
+    if test -z "$VERBOSE" -a "$CRON" != assumed; then
+	WGETQUIET='-q'
+	CURLQUIET='-s'
+	exec >/dev/null
+    fi
+    exec 3>&1			# preserve stdout in case cron mode does work
+
+    if test -n "$UNPACK"; then
+	echo "-u (unpack only) and -K (cron mode) are incompatible" 1>&2
+	exit 1
+    fi
+    if test -n "$VERSION"; then
+	echo "-V$VERSION and -K (cron mode) are incompatible" 1>&2
+	exit 1
+    fi
+    # Do nothing if we have run recently.
+    #	Pick a somewhat random number of days to reduce the crowds that
+    #	otherwise happen on anniversaries of releases.
+    DAYS=`ps | cksum | sed -e 's/ /9/g' -e 's/[^0-9]*\([0-9]\{1,6\}\).*/\1/'`
+    DAYS=`expr '(' $DAYS % 3 ')' + 6`
+    if test -d $DCCDIR							\
+	    && test -n "`find $OK_MARK -mtime -$DAYS 2>/dev/null`"; then
+	echo "less than $DAYS days since the last updatedcc -K; stop"
+	exit 0
+    fi
+fi
+
+
+# see if we have permission
+set -e
+if test ! -d $BDIR; then
+    rm -f $BDIR || true
+    mkdir $BDIR
+fi
+cd $BDIR
+if test ! -d $DCCDIR; then
+    rm -f $DCCDIR || true
+    mkdir $DCCDIR
+fi
+set +e
+for NM in "$DCC_HOMEDIR" "$DCC_LIBEXEC"; do
+    if test -n "$NM" -a ! -w "$NM"; then
+	echo "cannot write $NM; must $ME be run as root?" 1>&2
+	exit 1
+    fi
+done
+find $DCCDIR -type d | while read SUBDIR; do
+	if test ! -w $SUBDIR; then
+	    echo "cannot write $SUBDIR; must $ME be run as root?" 1>&2
+	    # but maybe this is ok
+	    break
+	fi
+    done
+
+if test -z "$PFILE"; then
+    if test -n "@UPDATEDCC_PFILE@"; then
+	CONFS="--without-updatedcc-pfile $CONFS"
+    fi
+    # quietly use $DCC_HOMEDIR/.updatedcc_pfile if it exists
+    if test -s "$DCC_HOMEDIR/.updatedcc_pfile"; then
+	PFILE="$DCC_HOMEDIR/.updatedcc_pfile"
+    fi
+else
+    PFILE=`echo $PFILE | sed "s@^[^/]@$DCC_HOMEDIR/&@"`
+    # add it to the configuration if it is not the default
+    if test "$PFILE" != "$DCC_HOMEDIR/.updatedcc_pfile"; then
+	CONFS="--with-updatedcc-pfile=$PFILE $CONFS"
+    fi
+fi
+if test -f "$PFILE" -a ! -r "$PFILE"; then
+    echo "cannot read $PFILE; must $ME be run as root?" 1>&2
+fi
+if test -n "$PFILE"; then
+    . $PFILE
+fi
+
+if test -z "$BURLS"; then
+    # $SERVER is set from $DCC_HOMEDIR/.updatedcc_pfile
+    if test -n "$SERVER"; then
+	BURLS="$SERVER"
+    else
+	BURLS="http://www.dcc-servers.net/dcc/source http://www.rhyolite.com/dcc/source ftp://ftp.dcc-servers.net/src/dcc ftp://ftp.rhyolite.com/src/dcc"
+    fi
+fi
+
+if test -z "$VERSION"; then
+    DLTBALL="dcc.tar.Z"
+    TBALL=
+    TBALL_PATH="$DLTBALL"
+else
+    DLTBALL="dcc-$VERSION.tar.Z"
+    TBALL="$DLTBALL"
+    TBALL_PATH="old/$DLTBALL"
+fi
+# don't blab the password needlessly
+print_url () {
+    PURL=`echo "$2" | sed -e 's%^\([fhtp]*://\)[^/@:]\{1,\}:[^/@:]\{1,\}@%\1%'`
+    echo "using $1 to get $PURL"
+}
+
+
+TRAPS="0 1 2 15"
+stop_dccd () {
+    if test "$DCC_UPDATEDCC_FAST" = yes; then
+	return
+    fi
+    DCC_UPDATEDCC_FAST= export DCC_UPDATEDCC_FAST
+
+    # trapped signals on some systems must start by turning themselves off,
+    #	especially #0
+    trap "trap $TRAPS; echo 'stopping update; please wait'; $DCC_RESTART" $TRAPS
+
+    # start stopping dccd now in the hope that it will have released the
+    #	sockets and database files by the time we need to restart it.
+    $DCC_LIBEXEC/stop-dccd $DEBUG
+    # switch to a working server
+    @bindir@/cdcc -q rtt >/dev/null 2>&1 || true
+}
+
+if test -n "$UNPACK"; then
+    DCC_RESTART=
+else
+    DCC_RESTART="$DCC_LIBEXEC/rcDCC $DEBUG start"
+    if test -z "$CRON"; then
+	# let compiling be slow instead of stopping the server in cron mode
+	stop_dccd
+    fi
+fi
+
+
+# use fetch, wget, curl, or ftp that understands URLs
+#	notice '-c --with-fetch_cmd=X'
+PGM=`echo "X $confs" | sed -n -e 's/.*--with-fetch[-_]cmd=\([^ ]*\)/\1/p'`
+if test -z "$PGM"; then
+    PGM=@FETCH_CMD@
+fi
+PGM_B=`basename $PGM`
+SUCCESS=
+oldTZ=$TZ; TZ=UTC; export TZ		# Solaris FTP is confused by dates
+if test "$PGM_B" = wget; then
+    for BURL in $BURLS; do
+	URL="$BURL/$TBALL_PATH"
+	print_url "$PGM_B" "$URL"
+	# Do not use --mirror because -r results in a 0 exit status
+	#   even on failure.
+	# Do not use --no-remove-listing, -nr, or --dont-remove-listing
+	#   because none of them are supported by all versions of wget.
+	# At least some versions of wget exit with 0 after having done
+	#   nothing but emitting a usage message.
+	if $PGM $WGETQUIET -nd --no-host-directories -N \
+		--retr-symlinks --passive-ftp @FETCH_WGET_OPTS@ "$URL"; then
+	    if test -s $DLTBALL; then
+		# notice if we get an HTTP error document with or without
+		# HTTP headers
+		if test -z "`sed -n -e 2q				\
+				-e 's/.*DOCTYPE.*/HTML/p'		\
+				-e 's/<HEAD>/HTML/p' -e 's/<head>/HTML/p' \
+			$DLTBALL`"; then
+		    SUCCESS="yes"
+		    break
+		fi
+		# do not leave a broken file
+		rm -f $DLTBALL
+	    fi
+	fi
+	echo
+    done
+    PGM_B=
+fi
+
+if test "$PGM_B" = fetch; then
+    for BURL in $BURLS; do
+	URL="$BURL/$TBALL_PATH"
+	print_url "$PGM_B" "$URL"
+	if $PGM -p -q -m "$URL"; then
+	    if test -s $DLTBALL; then
+		SUCCESS="yes"
+		break
+	    fi
+	fi
+	echo
+    done
+    PGM_B=
+fi
+
+if test "$PGM_B" = curl; then
+    for BURL in $BURLS; do
+	URL="$BURL/$TBALL_PATH"
+	print_url "$PGM_B" "$URL"
+	if $PGM @FETCH_CURL_OPTS@ -R --connect-timeout 30 --max-time 600 \
+		    $CURLQUIET "$URL" -o $DLTBALL; then
+	    # --fail does not work on at least some versions of curl
+	    if test -s $DLTBALL; then
+		# notice if we get an HTTP error document with or without
+		# HTTP headers
+		if test -z "`sed -n -e 2q				\
+				-e 's/.*DOCTYPE.*/HTML/p'		\
+				-e 's/<HEAD>/HTML/p' -e 's/<head>/HTML/p' \
+			$DLTBALL`"; then
+		    SUCCESS="yes"
+		    break
+		fi
+		# do not leave a broken file
+		rm -f $DLTBALL
+	    fi
+	fi
+	echo
+    done
+    PGM_B=
+fi
+
+# try FTP if it is the right choice or the right choice has not worked
+if test "$PGM_B" = ftp -o ! -s $DLTBALL; then
+    if test "$PGM_B" != ftp ; then
+	PGM_B=ftp
+	PGM=ftp
+    fi
+    for BURL in $BURLS; do
+	URL="$BURL/$TBALL_PATH"
+	print_url "$PGM_B" "$URL"
+	if $PGM -p "$URL"; then
+	    if test -s $DLTBALL; then
+		SUCCESS="yes"
+		break
+	    fi
+	fi
+	echo
+    done
+    # if that did not work, try ancient anonymous FTP
+    if test ! -s $DLTBALL; then
+	for URL in $BURLS; do
+	    HOST=`expr "$URL" : "ftp://\([^/]*\)/"`
+	    SRC=`expr "$URL" : "ftp://[^/]*/\(.*\)"`
+	    if test -z "$HOST" -o -z "$SRC"; then
+		continue
+	    fi
+	    USR=`expr "$HOST" : "\([^/:]*\):"`
+	    if test -z "$USR"; then
+		USR=anonymous
+		PASSWD=`hostname`
+	    else
+		PASSWD=`expr "$HOST" : "$USR:\(.*\)@.*"`
+		HOST=`expr "$HOST" : ".*@\(.*\)"`
+	    fi
+	    echo "try old anonymous FTP with $HOST and $SRC"
+	    (echo "user $USR $PASSWD ";				\
+			echo "binary";				\
+			echo "get $SRC/$DLTBALL $DLTBALL")	\
+		| ftp -n $HOST
+	    if test -s $DLTBALL; then
+		SUCCESS="yes"
+		break
+	    fi
+	    # some versions of ftp like to leave empty files
+	    rm -f $DLTBALL
+	done
+    fi
+    PGM_B=
+fi
+
+if test -n "$PGM_B"; then
+    echo "unrecognized fetch program $PGM_B" 1>&2; exit 1
+fi
+if test -z "$oldTZ"; then
+    unset TZ
+else
+    TZ=$oldTZ
+fi
+if test -z "$SUCCESS" -o ! -s $DLTBALL; then
+    echo
+    echo "failed to fetch a new copy of $DLTBALL" 1>&2
+    if test -n "$PFILE"; then
+	echo "is $PFILE correct?" 1>&2
+    fi
+    # try to install an existing tar ball if not in cron mode
+    if test -n "$CRON"; then
+	exit 1
+    fi
+    echo
+fi
+set -e
+
+# pick the newest tarball, possibly one fetched manually and already present
+#   instead of the official tarball fetched by this script.
+if test -z "$TBALL"; then
+    TBALL=`ls -Lt dcc*.tar.Z | head -1`
+fi
+if test ! -s "$TBALL"; then
+    echo "failed to fetch tarball; nothing to install" 1>&2
+    exit 1
+fi
+
+NEW_DCC=`zcat "$TBALL" | tar -tf -					\
+	    | sed -n -e '2q' -e 's@\(dcc-[0-9.]*\)/.*@\1@p'`
+if test -z "$NEW_DCC"; then
+    echo "failed to find version in $TBALL" 1>&2
+    exit 1
+fi
+
+# finished if we should only install new versions
+if test -n "$CRON"; then
+    exec 1>&3				# restore stdout
+    if test "$NEW_DCC" = "dcc-$RELEASE"	\
+	&& test -z "`find $TBALL -follow -newer $DCCDIR`"; then
+	echo "new $TBALL is the same version $RELEASE; stop"
+	touch $OK_MARK
+	exit 0
+    fi
+    stop_dccd
+fi
+
+# Move the unpacked tarball into the build directory named dcc
+#	while preserving any foreign files.
+#   Empty the target build directory of old DCC source and generated files.
+MNM=.manifest
+if test -d dcc; then
+    if test ! -f dcc/$MNM; then
+	if test `find dcc -type f -print | wc -l` -gt 50; then
+	    # deal with old versions without .manifest files
+	    echo delete $DCCDIR
+	    rm -rf dcc
+	fi
+    else
+	cd dcc
+	set +e
+	@DCCMAKE@ purge >/dev/null 2>&1
+	cd $BDIR
+	rmdir dcc 2>/dev/null
+	set -e
+	if test -d dcc; then
+	    find dcc -type f -print		\
+		| sed -e '1s/./preserving &/' -e '2,$s/./	&/'
+	    cd $BDIR
+	fi
+    fi
+fi
+
+echo "unpack $TBALL into $DCCDIR"
+rm -rf $NEW_DCC
+zcat $TBALL | tar -xf - 2>&1 | head
+
+if test -n "$UNPACK"; then
+    echo "will configure but *NOT* build or install $NEW_DCC"
+else
+    echo "will configure, build, and install $NEW_DCC"
+fi
+if test ! -d dcc; then
+    rm -rf dcc
+    mv $NEW_DCC dcc
+else
+    # install top directories for preserved files first
+    find $NEW_DCC -name $MNM -print | sort | while read NM; do
+	DIR=`expr $NM : "$NEW_DCC/\(.*\)/$MNM" || true`
+	if test -z "$DIR"; then
+	    DIR=.
+	fi
+	if test ! -d $NEW_DCC/$DIR; then
+	    # we already moved this directory
+	    continue
+	fi
+	# if we can, move the entire directory from the directory in the
+	#	unpacked tarball to the corresponding build directory
+	if test ! -d $DCCDIR/$DIR; then
+	    # first remove a possible stray file with the target name
+	    rm -f $DCCDIR/$DIR || true
+	    mv $NEW_DCC/$DIR $DCCDIR/$DIR
+	    continue
+	fi
+	# otherwise move individual files from the unpacked tarball directory
+	cd $NEW_DCC/$DIR
+	mv `cat $MNM` $DCCDIR/$DIR
+	cd $BDIR
+	done
+    rm -rf $NEW_DCC
+fi
+
+cd $DCCDIR
+
+# turn off FreeBSD blather about object directories
+if test -z "$MAKEOBJDIRPREFIX"; then
+    MAKEOBJDIRPREFIX=; export MAKEOBJDIRPREFIX
+fi
+
+if test -n "$ENV_SET"; then
+    echo "+ $ENV_SET"
+fi
+echo "+ ./configure @UPDATEDCC_PARMS@ $CONFQUIET $CONFS"
+./configure @UPDATEDCC_PARMS@ $CONFQUIET $CONFS
+
+if test -n "$UNPACK"; then
+    exit
+fi
+trap "trap $TRAPS; echo 'stopping; please wait'; \
+    $DCC_RESTART; @DCCMAKE@ purge >/dev/null 2>&1" $TRAPS
+
+@DCCMAKE@ $MAKE_TGT							\
+    | sed -e "$MQUIET/^[a-z0-9_]*.c: In function .*/d"			\
+	-e "$MQUIET/ warning: .%.. yields only last 2 digits of year/d"	\
+	-e "$MQUIET/^make.*is up to date/d"				\
+	-e "$MQUIET/^make.*Nothing to be done for/d"			\
+	-e "$MQUIET/^[a-z\/]*cc/s/-W[-a-z]*//g"				\
+	-e "$MQUIET/^[a-z\/]*cc/s@-m[0-9a-z=]*@@g"			\
+	-e "$MQUIET/^[a-z\/]*cc/s@-D_FILE_OFFSET_BITS=64@@g"		\
+	-e "$MQUIET/^[a-z\/]*cc/s@-D_LARGEFILE_SOURCE@@g"		\
+	-e "$MQUIET/^[a-z\/]*cc/s@-D_REENTRANT@@g"			\
+	-e "$MQUIET/^[a-z\/]*cc/s@-I[-a-zA-Z0-9/.]*@@g"			\
+	-e "$MQUIET/^[a-z\/]*cc/s@-I[-a-zA-Z0-9/.]*@@g"			\
+	-e "$MQUIET/^[a-z\/]*cc/s@-fno-strict-aliasing@@g"		\
+	-e "$MQUIET/^[a-z\/]*cc/s@-pipe@@g"				\
+	-e "$MQUIET/^building static .* library/d"			\
+	-e "$MQUIET/^ranlib/d"						\
+	-e "$MQUIET/^rm -f \.depend/d"					\
+	-e "$MQUIET/^mkdep/d"						\
+	-e "$MQUIET/^echo .* >> \.depend/d"				\
+	-e "$MQUIET/^g*cc/s@[   ]\{1,\}@ @g"
+
+touch $OK_MARK
+
+# finally start the new version and switch back to the local server
+trap $TRAPS
+$DCC_RESTART
+@bindir@/cdcc -q rtt >/dev/null 2>&1
diff -r 000000000000 -r c7f6b056b673 misc/wlist.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/wlist.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,405 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * whitelist lister
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.67 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include "dcc_ck.h"
+#include <arpa/inet.h>
+
+static DCC_EMSG dcc_emsg;
+static const char *homedir;
+
+static u_char quiet;
+
+static const char *ts2buf(time_t);
+
+
+static void NRATTRIB
+usage(void)
+{
+	dcc_logbad(EX_USAGE, "usage: [-VPQ] [-h homedir] [-t env_to] file");
+}
+
+
+
+int
+main(int argc, char **argv)
+{
+	const char *to = 0;
+	u_char print_version = 0;
+	u_char first;
+	DCC_WHITE_INX inx, inx2;
+	const char *nm;
+	DCC_SUM sum;
+	struct stat ht_sb, ascii_sb, inc_sb;
+	char tgts_buf[20];
+	int col;
+	u_char heading;
+	DCC_TGTS tgts;
+	DCC_WHITE_LISTING listing;
+	DCC_CK_TYPES type;
+	int i;
+
+	dcc_syslog_init(0, argv[0], 0);
+	dcc_clnt_debug = 99;
+
+	dup2(1, 2);			/* put error messages in context */
+
+	dcc_wf_init(&cmn_wf, DCC_WF_WLIST | DCC_WF_EITHER);
+
+	while ((i = getopt(argc, argv, "VPQqh:t:")) != EOF) {
+		switch (i) {
+		case 'V':
+			fprintf(stderr, DCC_VERSION"\n");
+			print_version = 1;
+			break;
+
+		case 'P':		/* parse or rebuild hash table */
+			cmn_wf.wf_flags &= ~DCC_WF_WLIST_RO;
+			cmn_wf.wf_flags |= DCC_WF_WLIST_RW;
+			break;
+
+		case 'Q':		/* query; don't rebuild hash table */
+			cmn_wf.wf_flags &= ~DCC_WF_WLIST_RW;
+			cmn_wf.wf_flags |= DCC_WF_WLIST_RO;
+			break;
+
+		case 'q':
+			quiet = 1;
+			break;
+
+		case 'h':
+			homedir = optarg;
+			break;
+
+		case 't':
+			to = optarg;
+			break;
+
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	if (argc == 0) {
+		if (print_version)
+			exit(EX_OK);
+		usage();
+	}
+
+	dcc_cdhome(0, homedir, 1);
+
+	first = 1;
+	while ((nm = *argv++) != 0) {
+		if (first) {
+			first = 0;
+		} else {
+			printf("\n\n--------------------------------\n");
+		}
+		if (cmn_wf.wf_flags & DCC_WF_WLIST_RO)
+			cmn_wf.wf_flags |= DCC_WF_RO;
+		else
+			cmn_wf.wf_flags &= ~DCC_WF_RO;
+		if (!dcc_new_white_nm(dcc_emsg, &cmn_wf, nm)) {
+			dcc_error_msg("%s", dcc_emsg);
+			exit(EX_DATAERR);
+		}
+		printf("%s\n", cmn_wf.ascii_nm);
+
+		switch (dcc_rdy_white(dcc_emsg, &cmn_wf, &cmn_tmp_wf)) {
+		case DCC_WHITE_OK:
+			break;
+		case DCC_WHITE_NOFILE:
+			dcc_error_msg("does %s exist?", nm);
+			exit(EX_DATAERR);
+		case DCC_WHITE_CONTINUE:
+			dcc_error_msg("%s", dcc_emsg);
+			break;
+		case DCC_WHITE_SILENT:
+		case DCC_WHITE_COMPLAIN:
+			dcc_error_msg("%s", dcc_emsg);
+			exit(EX_DATAERR);
+			break;
+		}
+		printf("%s\n", cmn_wf.wtbl->magic);
+
+		if (to) {
+			dcc_str2ck(sum, 0, 0, to);
+			printf("%s\n%8s %s\t",
+			       to,
+			       dcc_type2str_err(DCC_CK_ENV_TO, 0, 0, 0),
+			       dcc_ck2str_err(DCC_CK_ENV_TO, sum, 0));
+			if (DCC_WHITE_OK != dcc_white_sum(dcc_emsg, &cmn_wf,
+							DCC_CK_ENV_TO, sum,
+							&tgts, &listing)) {
+				dcc_error_msg("%s", dcc_emsg);
+			}
+			if (listing == DCC_WHITE_UNLISTED) {
+				printf("unlisted\n");
+			} else {
+				printf("%s\n",
+				       dcc_tgts2str(tgts_buf, sizeof(tgts_buf),
+						    tgts, 0));
+			}
+			continue;
+		}
+
+		printf("  %s whitelist  %d entries\n",
+		       (cmn_wf.wtbl_flags & DCC_WHITE_FG_PER_USER)
+		       ? "per-user" : "global",
+		       cmn_wf.wtbl->hdr.entries);
+
+		if (0 > fstat(cmn_wf.ht_fd, &ht_sb)) {
+			dcc_error_msg("stat(%s): %s",
+				      cmn_wf.ht_nm, ERROR_STR());
+			exit(EX_DATAERR);
+		}
+
+		if (0 > stat(cmn_wf.ascii_nm, &ascii_sb)) {
+			dcc_error_msg("stat(%s): %s",
+				      cmn_wf.ascii_nm, ERROR_STR());
+		} else if (ht_sb.st_mtime < ascii_sb.st_mtime) {
+			printf("    %s is older than %s\n",
+			       cmn_wf.ht_nm, cmn_wf.ascii_nm);
+		}
+
+		if (cmn_wf.wtbl->hdr.ascii_mtime == 0) {
+			printf("    %s broken\n", cmn_wf.ht_nm);
+		} else if (cmn_wf.wtbl->hdr.ascii_mtime != ascii_sb.st_mtime) {
+			printf("    %s has timestamp %s\n"
+			       "\tfor %s which has mtime %s\n",
+			       cmn_wf.ht_nm,
+			       ts2buf(cmn_wf.wtbl->hdr.ascii_mtime),
+			       cmn_wf.ascii_nm,
+			       ts2buf(ascii_sb.st_mtime));
+		}
+
+		if (cmn_wf.wtbl->hdr.broken != 0)
+			printf("    %s broken until %s\n",
+			       cmn_wf.ht_nm,
+			       ts2buf(cmn_wf.wtbl->hdr.broken));
+		if (cmn_wf.wtbl->hdr.reparse != 0)
+			printf("    re-parse %s for errors after %s\n",
+			       cmn_wf.ascii_nm,
+			       ts2buf(cmn_wf.wtbl->hdr.reparse));
+
+		if (cmn_wf.wtbl->hdr.flags & DCC_WHITE_FG_HOSTNAMES) {
+			printf("    resolve hostnames after %s\n",
+			       ts2buf(ht_sb.st_mtime + DCC_WHITECLNT_RESOLVE));
+		} else if (!(cmn_wf.wtbl->hdr.flags & DCC_WHITE_FG_PER_USER)) {
+			printf("    contains no hostnames\n");
+		}
+
+		for (i = 0; i < DIM(cmn_wf.wtbl->hdr.white_incs); ++i) {
+			if (cmn_wf.wtbl->hdr.white_incs[i].nm[0] == '\0')
+				break;
+			if (!i)
+				printf("    includes\n");
+			printf("      %s\n",
+			       fnm2abs_err(0,
+					   cmn_wf.wtbl->hdr.white_incs[i].nm));
+			if (0 > stat(cmn_wf.wtbl->hdr.white_incs[i].nm,
+				     &inc_sb)) {
+				dcc_error_msg("stat(%s): %s",
+					      cmn_wf.ascii_nm, ERROR_STR());
+			} else if (ht_sb.st_mtime < inc_sb.st_mtime) {
+				printf("    %s is older than %s"
+				       " and needs rebuilding\n",
+				       cmn_wf.ht_nm,
+				       path2fnm(cmn_wf.wtbl->hdr.white_incs[i
+							].nm));
+			}
+		}
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_DCC_ON)
+			printf("    option DCC-on\n");
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_DCC_OFF)
+			printf("    option DCC-off\n");
+
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_REP_ON)
+			printf("    option dcc-rep-on\n");
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_REP_OFF)
+			printf("    option dcc-rep-off\n");
+
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_GREY_ON)
+			printf("    option greylist-on\n");
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_GREY_OFF)
+			printf("    option greylist-off\n");
+
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_LOG_ALL)
+			printf("    option log-all\n");
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_LOG_NORMAL)
+			printf("    option log-normal\n");
+
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_LOG_D)
+			printf("    option log-subdirectory-day\n");
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_LOG_H)
+			printf("    option log-subdirectory-hour\n");
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_LOG_M)
+			printf("    option log-subdirectory-minute\n");
+
+
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_GREY_LOG_ON)
+			printf("    option greylist-log-on\n");
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_GREY_LOG_OFF)
+			printf("    option greylist-log-off\n");
+
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_MTA_FIRST)
+			printf("    option MTA-first\n");
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_MTA_LAST)
+			printf("    option MTA-last\n");
+
+		for (i = 0; i < MAX_DNSBL_GROUPS; ++i) {
+			if (cmn_wf.wtbl_flags & DCC_WHITE_FG_DNSBL_ON(i))
+				printf("    option DNSBL%d-on\n", i+1);
+			if (cmn_wf.wtbl_flags & DCC_WHITE_FG_DNSBL_OFF(i))
+				printf("    option DNSBL%d-off\n", i+1);
+		}
+
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_DISCARD_OK)
+			printf("    option forced-discard-ok\n");
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_NO_DISCARD)
+			printf("    option no-forced-discard\n");
+
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_TRAP_ACC)
+			printf("    option spam-trap-accept\n");
+		if (cmn_wf.wtbl_flags & DCC_WHITE_FG_TRAP_REJ)
+			printf("    option spam-trap_rej\n");
+
+		for (type = DCC_CK_TYPE_FIRST;
+		     type <= DCC_CK_TYPE_LAST;
+		     ++type) {
+			tgts = cmn_wf.wtbl->hdr.tholds_rej[type];
+			if (tgts == DCC_THOLD_UNSET)
+				continue;
+			printf("    option threshold %s,%s\n",
+			       dcc_type2str_err(type, 0, 0, 0),
+			       dcc_thold2str(tgts_buf, sizeof(tgts_buf),
+					     type, tgts));
+		}
+
+		printf("\n    file checksum %s\n",
+		       dcc_ck2str_err(0, cmn_wf.wtbl->hdr.ck_sum, 0));
+
+		if (quiet)
+			continue;
+
+		heading = 0;
+		for (i = 0; i < cmn_wf.wtbl->hdr.cidr.len; ++i) {
+			struct in_addr addr4;
+			char cidr_buf[INET6_ADDRSTRLEN];
+
+			DCC_WHITE_CIDR_ENTRY *e=&cmn_wf.wtbl->hdr.cidr.e[i];
+			if (!heading) {
+				heading = 1;
+				fputs("\n CIDR blocks\n", stdout);
+			}
+			printf("%6s    %s/%d\n",
+			       dcc_tgts2str(tgts_buf, sizeof(tgts_buf),
+					    e->tgts,0),
+			       dcc_ipv6tostr2(cidr_buf, sizeof(cidr_buf),
+					      &e->addr),
+			       dcc_ipv6toipv4(&addr4, &e->addr)
+			       ? e->bits-96 : e->bits);
+		}
+		if (heading)
+			putchar('\n');
+
+		/* first the hash table */
+		fputs("\n hash table\n", stdout);
+		col = 0;
+		for (inx = 0; inx < DIM(cmn_wf.wtbl->bins); ++inx) {
+			if (!cmn_wf.wtbl->bins[inx]
+			    && col == 0
+			    && inx != 0) {
+				inx2 = inx;
+				while (inx2 < DIM(cmn_wf.wtbl->bins)
+				       && !cmn_wf.wtbl->bins[inx2])
+					++inx2;
+				i = inx2 - inx;
+				i -= i % 4;
+				if (i != 0) {
+					printf(" ...\n");
+					inx += i;
+				}
+			}
+			printf("%4d:", inx);
+			if (cmn_wf.wtbl->bins[inx]) {
+				printf("%-4d", cmn_wf.wtbl->bins[inx]);
+			} else {
+				printf("    ");
+			}
+			col = (col + 1) % 4;
+			putchar(col == 0 ? '\n' : '\t');
+		}
+
+		/* then the entries */
+		printf("\n\n%4s->%-4s  %12s  %6s\n",
+		       "slot", "next", "type", "count");
+		for (inx = 0; inx < cmn_wf.wtbl_entries; ++inx) {
+			DCC_WHITE_ENTRY *e = &cmn_wf.wtbl->tbl[inx];
+			if (e->type == DCC_CK_INVALID)
+				continue;
+			printf("%4d->%-4d  %12s  %6s  %s\n",
+			       inx, e->fwd,
+			       dcc_type2str_err(e->type, 0, 0, 0),
+			       dcc_tgts2str(tgts_buf, sizeof(tgts_buf),
+					    e->tgts, 0),
+			       dcc_ck2str_err(e->type, e->sum, 0));
+		}
+	}
+	exit(EX_OK);
+}
+
+
+
+static const char *
+ts2buf(time_t ts)
+{
+	static struct {
+		char buf[26];
+	} times[4];
+	static int nbuf;
+
+	nbuf = (nbuf+1) % DIM(times);
+	return dcc_time2str(times[nbuf].buf, sizeof(times[nbuf].buf),
+			    "%b %d %X %Z",
+			    ts);
+}
diff -r 000000000000 -r c7f6b056b673 rrd-combine/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rrd-combine/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,3 @@
+Makefile.in
+rrd-combine.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 rrd-combine/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rrd-combine/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,46 @@
+# install the Distributed Checksum Clearinghouse miscellaneous files
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.1 $Revision$
+# @configure_input@
+
+DEPTH	=..
+PROG	=rrd-combine
+SRCS	=$(PROG).c
+
+DCC_BINDIR=@installroot@@libexecdir@
+@MAKE_PROG@
+@MAKE_INC2@
diff -r 000000000000 -r c7f6b056b673 rrd-combine/rrd-combine.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rrd-combine/rrd-combine.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,476 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * combine RRD databases
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.8 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include <math.h>
+
+#define PROGNAME "rrd-combine: "
+
+/* *********************************************************************** */
+/* copied from rrd.h and rrd_format.h */
+typedef double       rrd_value_t;
+
+typedef union unival {
+    unsigned long u_cnt;
+    rrd_value_t   u_val;
+} unival;
+
+typedef struct stat_head_t {
+    char	    cookie[4];
+    char	    version[5];
+    double	    float_cookie;
+    unsigned long   ds_cnt;
+    unsigned long   rra_cnt;
+    unsigned long   pdp_step;
+    unival	    par[10];
+} stat_head_t;
+
+#define DS_NAM_SIZE   20
+#define DST_SIZE   20
+typedef struct ds_def_t {
+    char	    ds_nam[DS_NAM_SIZE];
+    char	    dst[DST_SIZE];
+    unival	    par[10];
+} ds_def_t;
+
+#define CF_NAM_SIZE   20
+#define MAX_RRA_PAR_EN 10
+typedef struct rra_def_t {
+    char	    cf_nam[CF_NAM_SIZE];
+    unsigned long   row_cnt;
+    unsigned long   pdp_cnt;
+    unival	    par[MAX_RRA_PAR_EN];
+} rra_def_t;
+
+
+#define LAST_DS_LEN 30
+typedef struct pdp_prep_t{
+    char	    last_ds[LAST_DS_LEN];
+    unival	    scratch[10];
+} pdp_prep_t;
+
+#define MAX_CDP_PAR_EN 10
+typedef struct cdp_prep_t{
+    unival	    scratch[MAX_CDP_PAR_EN];
+} cdp_prep_t;
+
+typedef struct rra_ptr_t {
+    unsigned long   cur_row;
+} rra_ptr_t;
+
+typedef struct rrd_t {
+    const char      *fnm;
+    int		    fd;
+    struct stat     sb;
+    stat_head_t     *stat_head;		/* the static header */
+    ds_def_t	    *ds_def;		/* list of data source definitions */
+    rra_def_t       *rra_def;		/* list of round robin archive def */
+    struct timeval  last_update;
+    pdp_prep_t      *pdp_prep;		/* pdp data prep area */
+    cdp_prep_t      *cdp_prep;		/* cdp prep area */
+    rra_ptr_t       *rra_ptr;		/* list of rra pointers */
+    rrd_value_t     *rrd_value;		/* list of rrd values */
+} rrd_t;
+
+/* *********************************************************************** */
+
+
+static DCC_EMSG dcc_emsg;
+
+static const char *ofile;
+static u_char force;
+static u_char keep_mtime;
+static u_char verbose;
+
+
+static void NRATTRIB
+usage(void)
+{
+	dcc_logbad(EX_USAGE,
+		   "usage: [-fkv] [-d dir] -o ofile ifile1 ifile2 ...");
+}
+
+
+
+static void
+unmap_rrd(rrd_t *rrd)
+{
+	if (rrd->stat_head) {
+		if (munmap(rrd->stat_head, rrd->sb.st_size) < 0)
+			dcc_logbad(EX_IOERR,
+				   PROGNAME"munmap(%s, "OFF_DPAT"): %s",
+				   rrd->fnm, rrd->sb.st_size,
+				   strerror(errno));
+	}
+}
+
+
+
+static void
+rrd_close(rrd_t *rrd)
+{
+	unmap_rrd(rrd);
+
+	if (rrd->fd >= 0) {
+		if (close(rrd->fd) < 0)
+			dcc_logbad(EX_IOERR, PROGNAME"close(%s): %s",
+				   rrd->fnm, strerror(errno));
+		rrd->fd = -1;
+	}
+}
+
+
+
+static void
+rrd_stat(rrd_t *rrd)
+{
+	if (0 > fstat(rrd->fd, &rrd->sb))
+		dcc_logbad(EX_IOERR, PROGNAME"stat(%s): %s",
+			   rrd->fnm, strerror(errno));
+
+	if (rrd->sb.st_size <= ISZ(rrd->stat_head)) {
+		dcc_logbad(EX_DATAERR,
+			   PROGNAME"%s has too small size "OFF_DPAT"",
+			   rrd->fnm, rrd->sb.st_size);
+	}
+}
+
+
+
+static void
+map_rrd(rrd_t *rrd, u_char in)
+{
+	struct timeval *tv;
+	unmap_rrd(rrd);
+
+	rrd_stat(rrd);
+
+	rrd->stat_head = (stat_head_t *)mmap(0, rrd->sb.st_size,
+					     in
+					     ? PROT_READ
+					     :(PROT_READ | PROT_WRITE),
+					     MAP_SHARED,
+					     rrd->fd, 0);
+	if (rrd->stat_head == (stat_head_t *)MAP_FAILED)
+		dcc_logbad(EX_IOERR, PROGNAME"mmap(%s): %s",
+			   ofile, strerror(errno));
+
+	rrd->ds_def = (ds_def_t *)&rrd->stat_head[1];
+	rrd->rra_def = (rra_def_t *)&rrd->ds_def[rrd->stat_head->ds_cnt];
+	tv = (struct timeval *)&rrd->rra_def[rrd->stat_head->rra_cnt];
+	rrd->last_update.tv_sec = tv->tv_sec;
+	if (!strcmp(rrd->stat_head->version, "0001")) {
+		rrd->last_update.tv_usec = 0;
+		rrd->pdp_prep = (pdp_prep_t *)(&tv->tv_sec + 1);
+	} else {
+		rrd->last_update.tv_usec = tv->tv_usec;
+		rrd->pdp_prep = (pdp_prep_t *)(tv + 1);
+	}
+	rrd->cdp_prep = (cdp_prep_t *)&rrd->pdp_prep[rrd->stat_head->ds_cnt];
+	rrd->rra_ptr = (rra_ptr_t *)&rrd->cdp_prep[rrd->stat_head->rra_cnt
+						   * rrd->stat_head->ds_cnt];
+	rrd->rrd_value =(rrd_value_t*)&rrd->rra_ptr[rrd->stat_head->rra_cnt];
+}
+
+
+
+static void
+map_ifile(rrd_t *rrd, const char *new_fnm, const rrd_t *o_rrd)
+{
+#       define PAD 128
+	const rra_def_t *i_def, *o_def;
+	const char *old_fnm;
+	off_t old_len;
+	u_long l;
+	int i;
+
+	old_fnm = rrd->fnm;
+	old_len = rrd->sb.st_size;
+
+
+	if (rrd->fd >= 0)
+		rrd_close(rrd);
+
+	rrd->fnm = new_fnm;
+	rrd->fd = open(rrd->fnm, O_RDONLY, 0);
+	if (rrd->fd < 0)
+		dcc_logbad(EX_USAGE, PROGNAME"open(%s): %s",
+			   rrd->fnm, strerror(errno));
+
+	rrd_stat(rrd);
+
+	if (old_fnm && rrd->sb.st_size >= old_len + PAD)
+		dcc_logbad(EX_DATAERR,
+			   PROGNAME"%s has "OFF_DPAT" bytes, more than "
+			   OFF_DPAT" in %s",
+			   new_fnm, rrd->sb.st_size, old_len, old_fnm);
+
+	map_rrd(rrd, 1);
+
+	if (o_rrd) {
+		if (rrd->stat_head->rra_cnt != o_rrd->stat_head->rra_cnt)
+			dcc_logbad(EX_DATAERR,
+				   PROGNAME"%ld instead of %ld RRAs in %s",
+				   rrd->stat_head->rra_cnt,
+				   o_rrd->stat_head->rra_cnt,
+				   rrd->fnm);
+		if (rrd->stat_head->ds_cnt != o_rrd->stat_head->ds_cnt)
+			dcc_logbad(EX_DATAERR,
+				   PROGNAME"%ld instead of %ld DSs in %s",
+				   rrd->stat_head->ds_cnt,
+				   o_rrd->stat_head->ds_cnt,
+				   rrd->fnm);
+		if (rrd->stat_head->pdp_step != o_rrd->stat_head->pdp_step)
+			dcc_logbad(EX_DATAERR,
+				   PROGNAME"%ld instead of %ld step in %s",
+				   rrd->stat_head->pdp_step,
+				   o_rrd->stat_head->pdp_step,
+				   rrd->fnm);
+		for (l = 0, i_def = rrd->rra_def, o_def = o_rrd->rra_def;
+		     l < o_rrd->stat_head->rra_cnt;
+		     ++l, ++i_def, ++o_def) {
+			if (o_def->row_cnt != i_def->row_cnt)
+				dcc_logbad(EX_DATAERR,
+					   PROGNAME"%ld instead of %ld"
+					   " rows in RRA #%d in %s",
+					   i_def->row_cnt, o_def->row_cnt,
+					   i, rrd->fnm);
+			if (o_def->pdp_cnt != i_def->pdp_cnt)
+				dcc_logbad(EX_DATAERR,
+					   PROGNAME"%ld instead of %ld"
+					   " data points in RRA #%d in %s",
+					   i_def->pdp_cnt, o_def->pdp_cnt,
+					   i, rrd->fnm);
+		}
+	}
+}
+
+
+
+int NRATTRIB
+main(int argc, char **argv)
+{
+	rrd_t o_rrd, i_rrd;
+	int fno, len;
+	rrd_value_t *i_base, *o_base;
+	u_long rrd;
+	int rows, cols, row, i_row, o_row, col;
+	rrd_value_t i_val, o_val;
+	struct timeval tv;
+	int newest;
+	char tbuf[30];
+	int i;
+
+	memset(&i_rrd, 0, sizeof(i_rrd));
+	i_rrd.fd = -1;
+	memset(&o_rrd, 0, sizeof(o_rrd));
+	o_rrd.fd = -1;
+
+	while ((i = getopt(argc, argv, "fkvd:o:")) != -1) {
+		switch (i) {
+		case 'f':
+			force = 1;
+			break;
+		case 'k':
+			keep_mtime = 1;
+			break;
+		case 'v':
+			++verbose;
+			break;
+		case 'd':
+			if (0 > chdir(optarg))
+				dcc_logbad(EX_USAGE, "chdir(%s): %s",
+					   optarg, strerror(errno));
+			break;
+		case 'o':
+			ofile = optarg;
+			break;
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	if (argc < 2 || !ofile)
+		usage();
+
+	/* find the newest file */
+	map_ifile(&i_rrd, argv[0], 0);
+	tv = i_rrd.last_update;
+	newest = 0;
+	if (verbose > 1)
+		dcc_trace_msg(PROGNAME"%s last updated %s.%06d",
+			      argv[0],
+			      dcc_time2str(tbuf, sizeof(tbuf), "%X", tv.tv_sec),
+			      (int)tv.tv_usec);
+	for (fno = 1; fno < argc; ++fno) {
+		map_ifile(&i_rrd, argv[fno], 0);
+		if (tv.tv_sec > i_rrd.last_update.tv_sec
+		    || (tv.tv_sec == i_rrd.last_update.tv_sec
+			&& tv.tv_usec > i_rrd.last_update.tv_usec))
+			continue;
+
+		if (verbose > 1)
+			dcc_trace_msg("%40s last updated %s.%06d",
+				      argv[fno],
+				      dcc_time2str(tbuf, sizeof(tbuf),
+						   "%X",
+						   i_rrd.last_update.tv_sec),
+				      (int)i_rrd.last_update.tv_usec);
+		tv = i_rrd.last_update;
+		newest = fno;
+	}
+	if (verbose > 1)
+		dcc_trace_msg("    %s is newest", argv[newest]);
+
+	/* create and mmap() the output file */
+	o_rrd.fd = open(ofile,
+			O_RDWR | O_CREAT | (force ? O_TRUNC : O_EXCL),
+			0666);
+	if (o_rrd.fd < 0)
+		dcc_logbad(EX_IOERR, PROGNAME"open(%s): %s",
+			   ofile, strerror(errno));
+
+	/* copy the newest input file to the output file */
+	map_ifile(&i_rrd, argv[newest], 0);
+	len = write(o_rrd.fd, i_rrd.stat_head, i_rrd.sb.st_size);
+	if (len != i_rrd.sb.st_size)
+		dcc_logbad(EX_IOERR, PROGNAME"write(%s, "OFF_DPAT") = %d: %s",
+			   o_rrd.fnm, i_rrd.sb.st_size, len, strerror(errno));
+
+	map_rrd(&o_rrd, 0);
+
+	for (fno = 0; fno < argc; ++fno) {
+		if (fno == newest)
+			continue;
+
+		map_ifile(&i_rrd, argv[fno], &o_rrd);
+
+		i_base = i_rrd.rrd_value;
+		o_base = o_rrd.rrd_value;
+		for (rrd = 0; rrd < o_rrd.stat_head->rra_cnt; ++rrd) {
+			rows = o_rrd.rra_def[rrd].row_cnt;
+			cols = i_rrd.stat_head->ds_cnt;
+
+			/* find last row in the two RRDs numbered as
+			 * data consolidation moments since the UNIX epoch */
+			i_row = (i_rrd.last_update.tv_sec
+				 / (i_rrd.rra_def[rrd].pdp_cnt
+				    * i_rrd.stat_head->pdp_step));
+
+			o_row = (o_rrd.last_update.tv_sec
+				 / (o_rrd.rra_def[rrd].pdp_cnt
+				    * o_rrd.stat_head->pdp_step));
+
+			/* Find the number of rows to combine. */
+			i = o_row - i_row;
+			if (i >= 0) {
+				/* If the output RRD is newer than the input,
+				 * then we will add only some of the input
+				 * rows. */
+				row = rows - i;
+			} else {
+				/* we have problems if the output is older */
+				dcc_error_msg(PROGNAME
+					      "%s newer than %s",
+					      argv[fno], argv[1]);
+				dcc_error_msg("    i_rrd.last_update.tv_sec"
+					      " / (rra_def[%lu].pdp_cnt"
+					      " * stat_head->pdp_step)"
+					      "\n\t= %d / (%lu * %lu) = %d",
+					      rrd,
+					      i_rrd.last_update.tv_sec,
+					      i_rrd.rra_def[rrd].pdp_cnt,
+					      i_rrd.stat_head->pdp_step,
+					      i_row);
+				dcc_logbad(EX_DATAERR,
+					   "    o_rrd.last_update.tv_sec"
+					   " / (rra_def[%lu].pdp_cnt"
+					   " * stat_head->pdp_step)"
+					   "\n\t= %d / (%lu * %lu) = %d",
+					   rrd,
+					   o_rrd.last_update.tv_sec,
+					   o_rrd.rra_def[rrd].pdp_cnt,
+					   o_rrd.stat_head->pdp_step,
+					   o_row);
+			}
+
+			i_row = (i_rrd.rra_ptr[rrd].cur_row + 1) * cols;
+			o_row = (o_rrd.rra_ptr[rrd].cur_row + 1) * cols;
+			do {
+				/* wrap to the start at the last row */
+				if (i_row >= rows*cols)
+					i_row = 0;
+				if (o_row >= rows*cols)
+					o_row = 0;
+
+				for (col = 0;
+				     col < cols;
+				     ++col, ++i_row, ++o_row) {
+					i_val = i_base[i_row];
+					if (isnan(i_val))
+					    continue;
+					o_val = o_base[o_row];
+					if (isnan(o_val)) {
+					    o_val = i_val;
+					} else {
+					    o_val += i_val;
+					}
+					o_base[o_row] = o_val;
+				}
+			} while (--row > 0);
+
+			i_base += rows * cols;
+			o_base += rows * cols;
+		}
+	}
+
+
+	unmap_rrd(&o_rrd);
+	fsync(o_rrd.fd);
+	if (!keep_mtime
+	    && !dcc_set_mtime(dcc_emsg, ofile, o_rrd.fd, &o_rrd.last_update)) {
+		dcc_logbad(EX_IOERR, PROGNAME"%s", dcc_emsg);
+		exit(1);
+	}
+
+	exit(0);
+}
diff -r 000000000000 -r c7f6b056b673 srvrlib/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srvrlib/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,8 @@
+Makefile.in
+db.c
+db.h
+flod.c
+read_rcd.c
+srvr_defs.h
+ts2str.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 srvrlib/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srvrlib/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,51 @@
+# make DCC common code
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.16 $Revision$
+# @configure_input@
+
+DEPTH	=..
+LIB	=srvr
+SRCS	=db.c flod.c read_rcd.c ts2str.c
+
+@MAKE_DOT@ifdef DCCD_MAX_FLOODS
+CFLAGS	+=-DDCCD_MAX_FLOODS=$(DCCD_MAX_FLOODS)
+@MAKE_DOT@endif
+
+install:
+	@:
+
+@MAKE_LIB@
diff -r 000000000000 -r c7f6b056b673 srvrlib/db.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srvrlib/db.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,3710 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * server database functions
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.214 $Revision$
+ */
+
+#include "srvr_defs.h"
+#include <syslog.h>
+#include <sys/resource.h>
+#if defined(HAVE_HW_PHYSMEM) || defined(HAVE_BOOTTIME)
+#include <sys/sysctl.h>
+#endif
+#ifdef HAVE_PSTAT_GETSTATIC /* HP-UX */
+#include <sys/pstat.h>
+#endif
+
+DB_STATS db_stats;
+
+DB_STATES db_sts;
+
+DCC_PATH db_path_buf;
+
+int db_fd = -1;
+DCC_PATH db_nm;
+int db_hash_fd = -1;
+DCC_PATH db_hash_nm;
+struct timeval db_locked;		/* 1=database not locked */
+
+struct timeval db_time;
+
+int db_debug;
+
+u_char grey_on;
+static u_char db_use_write;		/* 0=no 1=if RAM big enough 2=always */
+static u_char db_dirty;
+static u_char db_rdonly;
+int db_failed_line;			/* bad happened at this line # */
+const char *db_failed_file;		/*	in this file */
+static u_char db_invalidate;		/* do not write to the files */
+
+/* Without mmap(MAP_NOSYNC) as on Solaris or a good msync() as on BSD/OS,
+ * we must rely on the kernel's update/syncer/bufdaemon/etc.  So in this
+ * case just fondle the mmap()'ed pages and hope things work out.
+ *
+ * With a msync() and with mmap(MAP_NOSYNC), use MAP_NOSYNC if we can because
+ * some systems flush too quickly while others such as FreeBSD 6.1 stall
+ * for seconds while thinking about flushing the database.
+ * But with mmap(MAP_NOSYNC) we leave large amounts of data in RAM that take
+ * too long time to be pushed to the disk when the system is shutting down.
+ * So
+ *	- hit only those chunks of memory with real data or changes to data
+ *	    with msync().  Trust dbclean to rebuild everything else at need.
+ *
+ *	- when it seems the system is being shut down, delete the hash table
+ *	    and let it be rebuilt when the system is rebooted.  When the
+ *	    hash table is rebuilt, "obsolete" markings in the data file that
+ *	    might have been lost will be remade.
+ *
+ * A third case involves dccd -F.  It requires that all changes be pushed to
+ * the disk whenever dccd unlocks the database so that dbclean can see changes
+ * dccd makes.  It also requires that dbclean write all of its changes so
+ * that dccd will find them when it reopens the database.
+ */
+
+#if !defined(MAP_NOSYNC) || defined(HAVE_OLD_MSYNC) || !defined(HAVE_BOOTTIME)
+#undef USE_MAP_NOSYNC
+#else
+#define USE_MAP_NOSYNC
+#endif
+
+static u_char db_not_synced;		/* database unsynchronized with disk */
+
+
+#define DCC_MADV_WILLNEED(p) 0
+#ifdef MADV_WILLNEED
+#undef DCC_MADV_WILLNEED
+#define DCC_MADV_WILLNEED(p) madvise(p, db_pagesize, MADV_WILLNEED)
+#endif
+#ifdef POSIX_MADV_WILLNEED
+#undef DCC_MADV_WILLNEED
+#define DCC_MADV_WILLNEED(p) posix_madvise(p, db_pagesize, POSIX_MADV_WILLNEED)
+#endif
+
+#define DCC_MADV_RANDOM(p) 0
+#ifdef MADV_RANDOM
+#undef DCC_MADV_RANDOM
+#define DCC_MADV_RANDOM(p) madvise(p, db_pagesize, MADV_RANDOM)
+#endif
+#ifdef POSIX_MADV_RANDOM
+#undef DCC_MADV_RANDOM
+#define DCC_MADV_RANDOM(p) posix_madvise(p, db_pagesize, POSIX_MADV_RANDOM)
+#endif
+
+#define DCC_MADV_DONTNEED(p) 0
+/* The Linux people claim that it is just fine that their notion of
+ * MADV_DONTNEED implies discarding changes to data.  Worse, some versions of
+ * Linux/GNU libc define POSIX_MADV_DONTNEED as the data-corrupting Linux
+ * MADV_DONTNEED.  This seems to be because they cannot admit their mistake of
+ * not distinguishing between the functions of MADV_FREE and MADV_DONTNEED and
+ * their misreading of other systems' documentation for MADV_DONTNEED */
+#ifndef linux
+#ifdef MADV_DONTNEED
+#undef DCC_MADV_DONTNEED
+#define DCC_MADV_DONTNEED(p) madvise(p, db_pagesize, MADV_DONTNEED)
+#endif
+#ifdef POSIX_MADV_DONTNEED
+#undef DCC_MADV_DONTNEED
+#define DCC_MADV_DONTNEED(p) posix_madvise(p, db_pagesize, POSIX_MADV_DONTNEED)
+#endif
+#endif /* !linux */
+
+#define DCC_MADV_FREE(p) 0
+#ifdef MADV_FREE
+#undef DCC_MADV_FREE
+#define DCC_MADV_FREE(p) madvise(p, db_pagesize, MADV_FREE)
+#endif
+#ifdef POSIX_MADV_FREE
+#undef DCC_MADV_FREE
+#define DCC_MADV_FREE(p) posix_madvise(p, db_pagesize, POSIX_MADV_FREE)
+#endif
+
+
+u_char db_minimum_map;			/* this is dccd & dbclean is running */
+
+int db_buf_total;			/* total # of db buffers */
+DB_PTR db_max_rss;			/* maximum db resident set size */
+DB_PTR db_max_byte;			/* maximum db bytes in both files */
+
+static u_int system_pagesize;		/* kernel page size */
+
+static DB_BUF db_bufs[DB_BUF_MAX];	/* control mmap()'ed blocks */
+static DB_BUF *buf_oldest, *buf_newest;
+
+#define DB_HASH_TOTAL DB_BUF_MAX
+static DB_BUF *db_buf_hash[DB_HASH_TOTAL];
+/* fancy 16-bit multiplicative hash assumes multiplication needs 1 cycle
+ * and so the hash is faster than dealing with a collision */
+#define DB_BUF_HASH(pnum,t) (&db_buf_hash[((((pnum)*(t)*0x9ccf) & 0xffff)   \
+					   * DB_BUF_MAX) >> 16])
+
+time_t db_need_flush_secs;
+static time_t db_urgent_need_flush_secs;
+
+const DB_VERSION_BUF db_version_buf = DB_VERSION_STR;
+DB_PARMS db_parms;
+static DB_PARMS db_parms_stored;
+
+DCC_TGTS db_tholds[DCC_DIM_CKS];
+
+u_int db_pagesize;			/* size of 1 mmap()'ed buffer */
+static u_int db_pagesize_part;
+
+DB_HOFF db_hash_fsize;			/* size of hash table file */
+static u_int hash_clear_pg_num;
+DB_HADDR db_hash_len;			/* # of hash table entries */
+DB_HADDR db_hash_divisor;		/* modulus */
+DB_HADDR db_hash_used;			/* # of hash table entries in use */
+u_int db_hash_page_len;			/* # of HASH_ENTRY's per buffer */
+DB_HADDR db_max_hash_entries = 0;	/* after db_buf_init()*/
+DB_PTR db_fsize;				/* size of database file */
+DB_PTR db_csize;			/* size of database contents in bytes */
+static DB_PTR db_csize_stored_hash;	/* DB size stored in hash file */
+static DB_HADDR db_hash_used_stored_hash;
+u_int db_page_max;			/* only padding after this in DB buf */
+static DB_PTR db_window_size;		/* size of mmap() window */
+char db_window_size_str[128];
+static char db_physmem_str[80];
+
+static const u_char dcc_ck_fuzziness[DCC_DIM_CKS] = {
+	0,				/* DCC_CK_INVALID */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_IP */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_ENV_FROM */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_FROM */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_SUB */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_MESSAGE_ID */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_RECEIVED */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_BODY */
+	DCC_CK_FUZ_LVL1,		/* DCC_CK_FUZ1 */
+	DCC_CK_FUZ_LVL2,		/* DCC_CK_FUZ2 */
+	DCC_CK_FUZ_LVL_REP,		/* DCC_CK_REP_TOTAL */
+	DCC_CK_FUZ_LVL_REP,		/* DCC_CK_REP_BULK */
+	DCC_CK_FUZ_LVL2,		/* DCC_CK_SRVR_ID */
+	DCC_CK_FUZ_LVL2			/* DCC_CK_ENV_TO */
+};
+static const u_char grey_ck_fuzziness[DCC_DIM_CKS] = {
+	0,				/* DCC_CK_INVALID */
+	DCC_CK_FUZ_LVL2,		/* DCC_CK_IP */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_ENV_FROM */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_FROM */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_SUB */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_MESSAGE_ID */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_RECEIVED */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_BODY */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_FUZ1 */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_FUZ2 */
+	DCC_CK_FUZ_LVL_NO,		/* DCC_CK_GREY_MSG */
+	DCC_CK_FUZ_LVL1,		/* DCC_CK_GREY_TRIPLE */
+	DCC_CK_FUZ_LVL1,		/* DCC_CK_SRVR_ID */
+	DCC_CK_FUZ_LVL1			/* DCC_CK_ENV_TO */
+};
+const u_char *db_ck_fuzziness = dcc_ck_fuzziness;
+
+
+static u_char buf_flush(DCC_EMSG, DB_BUF *, u_char);
+static u_char buf_munmap(DCC_EMSG, DB_BUF *);
+static DB_BUF *find_buf(DCC_EMSG, DB_BUF_TYPE, DB_PG_NUM);
+static u_char map_hash(DCC_EMSG, DB_HADDR, DB_STATE *, u_char);
+static u_char map_hash_ctl(DCC_EMSG, u_char);
+static u_char map_db(DCC_EMSG, DB_PTR, u_int, DB_STATE *, u_char);
+static u_char db_set_sizes(DCC_EMSG);
+
+
+/* compute the least common multiple of two numbers */
+static u_int
+lcm(u_int n, u_int m)
+{
+	u_int r, x, gcd;
+
+	/* first get the gcd of the two numbers */
+	if (n >= m) {
+		x = n;
+		gcd = m;
+	} else {
+		x = m;
+		gcd = n;
+	}
+	for (;;) {
+		r = x % gcd;
+		if (r == 0)
+			return n * (m / gcd);
+		x = gcd;
+		gcd = r;
+	}
+}
+
+
+
+const char *
+db_ptr2str(DB_PTR val)
+{
+	static int bufno;
+	static struct {
+	    char    str[16];
+	} bufs[4];
+	char *s;
+	const char *units;
+
+	if (val == 0)
+		return "0";
+
+	s = bufs[bufno].str;
+	bufno = (bufno+1) % DIM(bufs);
+
+	if (val % (1024*1024*1024) == 0) {
+		val /= (1024*1024*1024);
+		units = "GB";
+	} else if (val % (1024*1024) == 0) {
+		val /= (1024*1024);
+		units = "MB";
+	} else if (val % 1024 == 0) {
+		val /= 1024;
+		units = "KB";
+	} else {
+		units = "";
+	}
+	if (val > 1000*1000*1000)
+		snprintf(s, sizeof(bufs[0].str), "%d,%03d,%03d,%03d%s",
+			 (int)(val / (1000*1000*1000)),
+			 (int)(val / (1000*1000)) % 1000,
+			 (int)(val / 1000) % 1000,
+			 (int)(val % 1000),
+			 units);
+	else if (val > 1000*1000)
+		snprintf(s, sizeof(bufs[0].str), "%d,%03d,%03d%s",
+			 (int)(val / (1000*1000)),
+			 (int)(val / 1000) % 1000,
+			 (int)(val % 1000),
+			 units);
+	else if (val > 1000*10)
+		snprintf(s, sizeof(bufs[0].str), "%d,%03d%s",
+			 (int)(val / 1000),
+			 (int)(val % 1000),
+			 units);
+	else
+		snprintf(s, sizeof(bufs[0].str), "%d%s",
+			 (int)val,
+			 units);
+	return s;
+}
+
+
+
+const char *
+size2str(char *buf, u_int buf_len,
+	 double num, u_char bytes_or_entries)	/* 0=number 1=bytes */
+{
+	const char *units;
+	double k;
+
+	k = bytes_or_entries ? 1024.0 : 1000.0;
+
+	if (num < k) {
+		units = "";
+	} else if (num < k*k) {
+		num /= k;
+		units = "K";
+	} else if (num < k*k*k) {
+		num /= k*k;
+		units = "M";
+	} else {
+		num /= k*k*k;
+		units = "G";
+	}
+
+	if ((int)num >= 100)
+		snprintf(buf, buf_len, "%.0f%s", num, units);
+	else
+		snprintf(buf, buf_len, "%.2g%s", num, units);
+	return buf;
+}
+
+
+
+void PATTRIB(5,6)
+db_failure(int linenum, const char *file, int ex_code, DCC_EMSG emsg,
+	   const char *p, ...)
+{
+	va_list args;
+
+	if (!db_failed_line) {
+		db_failed_line = linenum;
+		db_failed_file = file;
+	}
+	va_start(args, p);
+	dcc_vpemsg(ex_code, emsg, p, args);
+	va_end(args);
+}
+
+
+
+void PATTRIB(3,4)
+db_error_msg(int linenum, const char *file, const char *p, ...)
+{
+	va_list args;
+
+	if (!db_failed_line) {
+		db_failed_line = linenum;
+		db_failed_file = file;
+	}
+	va_start(args, p);
+	dcc_verror_msg(p, args);
+	va_end(args);
+}
+
+
+
+double					/* hashes or bytes/second */
+db_add_rate(const DB_PARMS *parms,
+	    u_char hash_or_db)		/* 1=hash */
+{
+	struct timeval sn;
+	time_t new_rate_secs;
+	time_t total_secs;
+	double added, cur, prev;
+
+	total_secs = parms->rate_secs;
+	if (hash_or_db) {
+		added = parms->hash_added;
+		cur = parms->hash_used;
+		prev = parms->old_hash_used;
+	} else {
+		added = parms->db_added;
+		cur = parms->db_csize;
+		prev = parms->old_db_csize;
+	}
+
+	if (total_secs <= 0 || total_secs > DB_MAX_RATE_SECS
+	    || added <= 0.0) {
+		added = 0.0;
+		total_secs = 0;
+	}
+
+	dcc_ts2timeval(&sn, &parms->sn);
+	new_rate_secs = parms->last_rate_sec - sn.tv_sec;
+	if (new_rate_secs > 0 && new_rate_secs <= DB_MAX_RATE_SECS
+	    && cur > prev) {
+		total_secs += new_rate_secs;
+		added += cur - prev;
+	}
+
+	if (total_secs <= DB_MIN_RATE_SECS)
+		return -1.0;
+	return added / total_secs;
+}
+
+
+
+DB_NOKEEP_CKS
+def_nokeep_cks(void)
+{
+	DCC_CK_TYPES type;
+	DB_NOKEEP_CKS nokeep = 0;
+
+	for (type = DCC_CK_TYPE_FIRST; type <= DCC_CK_TYPE_LAST; ++type) {
+		if (DB_GLOBAL_NOKEEP(grey_on, type))
+			DB_SET_NOKEEP(nokeep, type);
+	}
+	DB_SET_NOKEEP(nokeep, DCC_CK_INVALID);
+	DB_SET_NOKEEP(nokeep, DCC_CK_FLOD_PATH);
+
+	return nokeep;
+}
+
+
+
+void
+set_db_tholds(DB_NOKEEP_CKS nokeep)
+{
+	DCC_CK_TYPES type;
+
+	for (type = 0; type < DIM(db_tholds); ++type) {
+		db_tholds[type] = (DB_TEST_NOKEEP(nokeep, type)
+				   ? DCC_TGTS_INVALID
+				   : DCC_CK_IS_REP_CMN(grey_on, type)
+				   ? DCC_TGTS_INVALID
+				   : grey_on ? 1
+				   : type == DCC_CK_SRVR_ID ? 1
+				   : BULK_THRESHOLD);
+	}
+}
+
+
+
+static const char *
+buf2path(const DB_BUF *b)
+{
+	switch (b->buf_type) {
+	case DB_BUF_TYPE_HASH:
+		return db_hash_nm;
+	case DB_BUF_TYPE_DB:
+		return db_nm;
+	case DB_BUF_TYPE_FREE:
+	default:
+		dcc_logbad(EX_SOFTWARE, "impossible buffer type for a path");
+	}
+}
+
+
+
+static int
+buf2fd(const DB_BUF *b)
+{
+	switch (b->buf_type) {
+	case DB_BUF_TYPE_HASH:
+		return db_hash_fd;
+	case DB_BUF_TYPE_DB:
+		return db_fd;
+	case DB_BUF_TYPE_FREE:
+	default:
+		dcc_logbad(EX_SOFTWARE, "impossible buffer type for fd");
+	}
+}
+
+
+
+static void
+rel_db_state(DB_STATE *st)
+{
+	DB_BUF *b;
+
+	b = st->b;
+	if (!b)
+		return;
+	st->b = 0;
+	st->d.v = 0;
+	st->s.rptr = DB_PTR_BAD;
+	if (--b->lock_cnt < 0)
+		dcc_logbad(EX_SOFTWARE,"negative database buffer lock");
+}
+
+
+
+void
+rel_db_states(void)
+{
+	DB_STATE *st;
+
+	for (st = &db_sts.rcd; st <= &db_sts.hash_ctl; ++st) {
+		rel_db_state(st);
+	}
+}
+
+
+
+/* release one or all unneeded buffers */
+u_char					/* 0=problem 1=did nothing 2=did>=1 */
+db_unload(DCC_EMSG emsg,
+	  u_char some)			/* 0=all, 1=only one, 2=finished */
+{
+	DB_BUF *b;
+	u_char result;
+
+	result = 1;
+	for (b = buf_oldest; b != 0; b = b->newer) {
+		if (b->buf_type == DB_BUF_TYPE_FREE
+		    || b->lock_cnt != 0)
+			continue;
+		if (some == 2
+		    && !(b->flags & DB_BUF_FG_USE_WRITE)
+		    && 0 > DCC_MADV_DONTNEED(b->buf.v))
+			dcc_error_msg("madvise(DONTNEED %s,%#x): %s",
+				      buf2path(b), db_pagesize, ERROR_STR());
+		if (!buf_munmap(emsg, b)) {
+			emsg = 0;
+			result = 0;
+		} else if (result) {
+			result = 2;
+		}
+		if (some == 1)
+			return result;
+	}
+
+	return result;
+}
+
+
+
+static u_char
+buf_write_part(DCC_EMSG emsg, DB_BUF *b, off_t offset, void *buf, int len)
+{
+	int i;
+
+	offset += (off_t)b->pg_num * (off_t)db_pagesize;
+
+	if (offset != lseek(buf2fd(b), offset, SEEK_SET)) {
+		db_failure(__LINE__,__FILE__, EX_IOERR, emsg,
+			   "buf_write_part lseek(%s,"OFF_HPAT"): %s",
+			   buf2path(b), offset, ERROR_STR());
+		return 0;
+	}
+	i = write(buf2fd(b), buf, len);
+	if (i != len) {
+		db_failure(__LINE__,__FILE__, EX_IOERR, emsg,
+			   "buf_write_part(%s,%u)=%d: %s",
+			   buf2path(b), len, i, ERROR_STR());
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+/* push part of a buffer toward the disk
+ *	this can be needed even when the file has been opened and mapped
+ *	read-only by dbclean */
+static u_char
+buf_flush_part(DCC_EMSG emsg, DB_BUF *b,
+	       u_int part,		/* DB_BUF_NUM_PARTS=buffer */
+	       u_char async UATTRIB)
+{
+	u_int flush_len;
+	char *flush_base;
+	DB_BUF_FM bit;
+
+	bit = PART2BIT(part) & (b->flush | b->flush_urgent);
+	if (!bit)
+		return 1;
+
+	/* Send a new buffer to disk at once. */
+	if (b->flags & DB_BUF_FG_EXTENSION) {
+		DB_BUF *b1, *b0;
+		u_char result;
+
+		/* To give the file system a chance to make the hash table
+		 * contiguous, first write all preceding new buffers.
+		 * In almost all cases, there will be none. */
+		result = 1;
+		do {
+			b0 = b;
+			for (b1 = buf_oldest; b1 != 0; b1 = b1->newer) {
+				if (!(b1->flags & DB_BUF_FG_EXTENSION)
+				    || b1->buf_type != b0->buf_type
+				    || b1->pg_num >= b0->pg_num)
+					continue;
+				b0 = b1;
+			}
+			b0->flags &= ~DB_BUF_FG_EXTENSION;
+			b0->flush = 0;
+			b0->flush_urgent = 0;
+			if (!db_invalidate
+			    && !buf_write_part(emsg, b0,
+					       0, b0->buf.c, db_pagesize))
+				result = 0;
+		} while (b0 != b);
+		return result;
+	}
+
+	flush_base = b->ranges[part].lo;
+	flush_len = b->ranges[part].hi - flush_base;
+	b->flush &= ~bit;
+	b->flush_urgent &= ~bit;
+
+	if (db_invalidate)
+		return 1;
+
+	if (b->flags & DB_BUF_FG_USE_WRITE) {
+		static char *wbuf;
+		static u_int wbuf_len;
+
+		/* In at least FreeBSD you cannot write() to the file
+		 * that underlies a mmap() region from that region */
+		if (wbuf_len < db_pagesize_part) {
+			/* the page size for the current file
+			 * might be different from the old file */
+			if (wbuf)
+				free(wbuf);
+			wbuf_len = db_pagesize_part;
+			wbuf = malloc(wbuf_len);
+		}
+
+		memcpy(wbuf, flush_base, flush_len);
+		return buf_write_part(emsg, b, flush_base - b->buf.c,
+				      wbuf, flush_len);
+
+#ifndef HAVE_OLD_MSYNC
+	} else if (async) {
+		if (0 > MSYNC(flush_base, flush_len, MS_ASYNC)) {
+			db_failure(__LINE__,__FILE__, EX_IOERR, emsg,
+				   "msync(db buffer %s,%#lx,%#x,MS_ASYNC): %s",
+				   buf2path(b), (long)flush_base, flush_len,
+				   ERROR_STR());
+			return 0;
+		}
+#endif
+	} else {
+		if (0 > MSYNC(flush_base, flush_len, MS_SYNC)) {
+			db_failure(__LINE__,__FILE__, EX_IOERR, emsg,
+				   "msync(db buffer %s,%#lx,%#x,MS_SYNC): %s",
+				   buf2path(b), (long)flush_base, flush_len,
+				   ERROR_STR());
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+
+
+static u_char
+buf_flush(DCC_EMSG emsg, DB_BUF *b, u_char async)
+{
+	u_int part;
+	DB_BUF_FM bits;
+	u_char result = 1;
+
+	bits = b->flush_urgent | b->flush;
+	for (part = 0;  bits != 0 && part < DB_BUF_NUM_PARTS; ++part) {
+		if (bits & PART2BIT(part)) {
+			if (!buf_flush_part(emsg, b, part, async)) {
+				emsg = 0;
+				result = 0;
+			}
+			bits = b->flush_urgent | b->flush;
+		}
+	}
+	return result;
+}
+
+
+
+/* Try to keep the data clean so that the fsync() required by Solaris
+ *	when the file is unloaded is not too expensive.
+ *	Try to flush frequently so that we don't stall as long in msync().
+ */
+void
+db_flush_needed(void)
+{
+	static DB_BUF *next_b = db_bufs;
+	static u_int next_part;
+	DB_BUF *b;
+	u_int part, all_parts;
+	int buf_num;
+	u_char worked;
+
+	/* send to the disk changes that cannot be recreated by dbclean */
+	if (db_urgent_need_flush_secs != 0
+	    && DB_IS_TIME(db_urgent_need_flush_secs,
+			  DB_URGENT_NEED_FLUSH_SECS)) {
+		worked = 0;
+		for (b = buf_newest; b; b = b->older) {
+			if (b->buf_type == DB_BUF_TYPE_FREE)
+				continue;
+
+			for (part = 0;
+			     b->flush_urgent != 0 && part < DB_BUF_NUM_PARTS;
+			     ++part) {
+				if ((b->flush_urgent & PART2BIT(part))) {
+					buf_flush_part(0, b, part, 1);
+					worked = 1;
+				}
+			}
+
+			/* Switch new data pages to mmap()
+			 * when this is not dbclean, since only dccd calls here
+			 *	they are not using mmap()
+			 *	they are either hash table pages or
+			 *	    not the last page in the file */
+			if ((b->flags & DB_BUF_FG_USE_WRITE)
+			    && !db_use_write
+			    && (b->buf_type != DB_BUF_TYPE_DB
+				|| (DB_PTR2PG_NUM(db_csize-1, db_pagesize)
+				    != b->pg_num))) {
+				if (b->lock_cnt != 0)
+					rel_db_states();
+				buf_munmap(0, b);
+			}
+		}
+
+		/* Keep the clock running if we did any work. This tends to
+		 * avoid stalls caused by colliding with the FreeBSD syncer */
+		if (worked) {
+			gettimeofday(&db_time, 0);
+			db_urgent_need_flush_secs = (db_time.tv_sec
+						+ DB_URGENT_NEED_FLUSH_SECS);
+		} else {
+			db_urgent_need_flush_secs = 0;
+		}
+	}
+
+	/* assume there will be nothing more to do */
+	db_need_flush_secs = db_urgent_need_flush_secs;
+
+#ifdef USE_MAP_NOSYNC
+	/* if we are using mmap(MAP_NOSYNC), then there are no bits
+	 * set in any b->flush words except that of the recent
+	 * DB_BUF_FG_USE_WRITE extensions of the file.  It is best to let
+	 * those blocks stay in RAM until the whole buffer is flushed and
+	 * switched to mmap above */
+	if (!db_use_write)
+		return;
+#endif
+
+	b = next_b;
+	part = next_part;
+	all_parts =  DB_PARTS_PER_FLUSH;
+	for (buf_num = DIM(db_bufs); buf_num >= 0; --buf_num) {
+		if (b > LAST(db_bufs)) {
+			part = 0;
+			b = db_bufs;
+		}
+		if (!b->flush
+		    || part >= DB_BUF_NUM_PARTS
+		    || b->buf_type == DB_BUF_TYPE_FREE) {
+			part = 0;
+			++b;
+			continue;
+		}
+
+		while (part < DB_BUF_NUM_PARTS) {
+			if (b->flush & PART2BIT(part)) {
+				buf_flush_part(0, b, part, 1);
+				if (--all_parts == 0) {
+					next_part = part+1;
+					next_b = b;
+					db_need_flush_secs = (db_time.tv_sec
+							+ DB_NEED_FLUSH_SECS);
+					return;
+				}
+				if (!b->flush)
+					part = DB_BUF_NUM_PARTS;
+			}
+			++part;
+		}
+	}
+}
+
+
+
+/* occassionally flush an unlocked data buffer for dbclean
+ *	dbclean mostly changes only the current record, so get started
+ *	writing the data to avoid stalling the system at the end. */
+u_char
+db_flush_db(DCC_EMSG emsg UATTRIB)
+{
+#ifdef USE_MAP_NOSYNC
+	DB_BUF *b;
+	int limit;
+	int pg_num;
+
+	/* Gently push the new hash table to disk.
+	 * The disk image will never be accurate.  This only allocates space.
+	 * Do not do this for systems that lack mmap(NOSYNC) such as Linux
+	 * that thrash themselves as the hash table is being built.  A
+	 * long pause when the database is closed is not as bad as spending
+	 * hours building the hash table. */
+	while (hash_clear_pg_num < db_hash_fsize/db_hash_page_len) {
+		pg_num = hash_clear_pg_num++;
+		for (b = buf_oldest; b != 0; b = b->newer) {
+			if (b->pg_num != pg_num
+			    || b->buf_type != DB_BUF_TYPE_HASH)
+				continue;
+			if (!(b->flags & DB_BUF_FG_EXTENSION))
+				break;
+			if (b->lock_cnt != 0)
+				rel_db_states();
+			return buf_munmap(emsg, b);
+		}
+
+		/* look for the next page if this one has already
+		 * been flushed */
+	}
+
+	/* flush some ordinary buffers */
+	limit = 2;
+	for (b = buf_oldest; b != 0; b = b->newer) {
+		if (b->flush_urgent == 0
+		    || b->buf_type == DB_BUF_TYPE_FREE
+		    || b->lock_cnt != 0)
+			continue;
+		if (!buf_flush(emsg, b, 1))
+			return 0;
+		if (--limit <= 0)
+			return 1;
+	}
+#endif
+	return 1;
+}
+
+
+
+/* mark part of a buffer dirty
+ *	"Urgent" changes are flushed by a timer.  Ordinary changes
+ *	are often ignored and expected to be rebuilt if the system crashes.
+ *	That the hash table is deleted as the system is shut down while the
+ *	database must be flushed from the system's buffer cache is a reason
+ *	to keep the disk image of the database good. */
+void
+db_set_flush(DB_STATE *st, u_char urgent, u_int len)
+{
+	DB_BUF *b;
+	DB_BUF_FM bit, new_bits, old_bits;
+	char *buf_base, *part_end, *start, *end;
+	u_int part, i;
+
+	/* nothing to do if the kernel is handling it
+	 * or if we are letting this change be reconstructed by dbclean */
+	b = st->b;
+	if (!(b->flags & DB_BUF_FG_USE_WRITE)) {
+#ifdef USE_MAP_NOSYNC
+		if (!urgent)
+#endif
+			return;
+	}
+
+	start = st->d.c;
+	buf_base = b->buf.c;
+
+	/* Increase to even pages in the hope that the file system might
+	 * be able to page-flip.  This might at least avoid reading into the
+	 * buffer cache to honor a write(). Besides, Solaris' msync() handles
+	 * only even pages. */
+	i = (start - buf_base) % system_pagesize;
+	start -= i;
+	len += i;
+	len = ((len + system_pagesize-1) / system_pagesize) * system_pagesize;
+
+	end = start + len;
+	if (end > buf_base+db_pagesize)
+		dcc_logbad(EX_SOFTWARE, "inflated dirty buffer size");
+
+	part = (start - buf_base) / db_pagesize_part;
+	part_end = buf_base + part * db_pagesize_part;
+	bit = PART2BIT(part);
+	new_bits = 0;
+	old_bits = b->flush | b->flush_urgent;
+	do {
+		part_end += db_pagesize_part;
+		if (part_end > end)
+			part_end = end;
+
+		if (!(old_bits & bit)) {
+			b->ranges[part].lo = start;
+			b->ranges[part].hi = part_end;
+		} else {
+			if (b->ranges[part].lo > start)
+				b->ranges[part].lo = start;
+			if (b->ranges[part].hi < part_end)
+				b->ranges[part].hi = part_end;
+		}
+		new_bits |= bit;
+
+		start = part_end;
+		bit <<= 1;
+		++part;
+	} while (part_end < end);
+
+	if (urgent) {
+		b->flush_urgent |= new_bits;
+		if (!db_urgent_need_flush_secs) {
+			db_urgent_need_flush_secs = (db_time.tv_sec
+						+ DB_URGENT_NEED_FLUSH_SECS);
+			if (db_need_flush_secs == 0)
+				db_need_flush_secs = db_urgent_need_flush_secs;
+		}
+	} else {
+		b->flush |= new_bits;
+		if (db_need_flush_secs == 0
+		    || db_need_flush_secs > db_time.tv_sec+DB_NEED_FLUSH_SECS)
+			db_need_flush_secs = db_time.tv_sec+DB_NEED_FLUSH_SECS;
+	}
+}
+
+
+
+/* Shut down the database, including flushing and releasing all
+ *	mmap()'ed buffers
+ * Do nothing to the files for mode=-1 because the file is new and garbage
+ *	or the caller is a fork of the server shedding memory. */
+u_char
+db_close(int mode)			/* -1=invalidate, 0=dirty, 1=clean */
+{
+	u_char result;
+
+	if (mode >= 0) {
+		/* flush the data and then release and flush the dirty flags */
+		result = make_clean(mode == 0 ? 0 : 1);
+		if (!db_unload(0, 0))
+			result = 0;
+	} else {
+		db_invalidate = 1;
+		rel_db_states();
+		result = (db_unload(0, 0) > 0);
+	}
+
+	/* Close the hash table first because the server is often
+	 * waiting for the lock on the main file held by dbclean.
+	 * Destroy the hash table if it is bad */
+	if (db_hash_fd >= 0) {
+		if (0 > close(db_hash_fd)) {
+			dcc_pemsg(EX_IOERR, 0, "close(%s): %s",
+				  db_hash_nm, ERROR_STR());
+			result = 0;
+		}
+		db_hash_fd = -1;
+	}
+	if (db_fd >= 0) {
+		if (0 > close(db_fd)) {
+			dcc_pemsg(EX_IOERR, 0, "close(%s): %s",
+				  db_nm, ERROR_STR());
+			result = 0;
+		}
+		db_fd = -1;
+	}
+
+	db_locked.tv_sec = 0;
+	return result;
+}
+
+
+
+/* Delete the hash table if the system is being rebooted and we
+ * don't trust the file system to get all of the hash table.  This might
+ * make system shut down faster */
+void
+db_stop(void)
+{
+	if (db_hash_fd < 0
+	    || !DB_IS_LOCKED()
+	    || !db_not_synced
+	    || db_hash_nm[0] == '\0')
+		return;
+
+	if (0 > unlink(db_hash_nm)
+	    && errno != ENOENT)
+		dcc_error_msg("unlink(%s): %s", db_hash_nm, ERROR_STR());
+}
+
+
+
+/* see if (another) instance of dbclean is already running */
+static int dbclean_lock_fd = -1;
+static DCC_PATH dbclean_lock_nm;
+
+u_char					/* 1=no (other) dbclean */
+lock_dbclean(DCC_EMSG emsg, const char *cur_db_nm)
+{
+	char pid[32];
+	int i;
+
+	fnm2rel_good(dbclean_lock_nm, cur_db_nm, DB_LOCK_SUFFIX);
+	dbclean_lock_fd = dcc_lock_open(emsg, dbclean_lock_nm,
+					O_RDWR|O_CREAT,
+					DCC_LOCK_OPEN_NOWAIT,
+					DCC_LOCK_ALL_FILE, 0);
+	if (dbclean_lock_fd < 0)
+		return 0;
+
+	i = 1+snprintf(pid, sizeof(pid), "%ld\n", (long)getpid());
+	if (i != write(dbclean_lock_fd, pid, i))
+		dcc_logbad(EX_IOERR, "write(%s, pid): %s",
+			   dbclean_lock_nm, ERROR_STR());
+
+	/* Let anyone write in it in case we are running as root
+	 * and get interrupted by a crash or gdb.  A stray, stale
+	 * private lock file cannot be locked */
+	chmod(dbclean_lock_nm, 0666);
+
+	return 1;
+}
+
+
+
+void
+unlock_dbclean(void)
+{
+	if (dbclean_lock_fd >= 0) {
+		if (0 > unlink(dbclean_lock_nm))
+			dcc_error_msg("unlink(%s): %s",
+				      dbclean_lock_nm, ERROR_STR());
+		close(dbclean_lock_fd);
+		dbclean_lock_fd = -1;
+	}
+}
+
+
+
+/* This locking does only multiple-readers/single-writer */
+int					/* -1=failed, 0=was not locked, 1=was */
+db_lock(void)
+{
+	struct stat sb;
+
+	if (DB_IS_LOCKED())
+		return 1;
+
+	if (!dcc_exlock_fd(0, db_fd, DCC_LOCK_ALL_FILE, 15*60, "", db_nm))
+		return -1;
+	if (0 > fstat(db_fd, &sb)) {
+		db_failure(__LINE__,__FILE__, EX_IOERR, 0,
+			   "stat(%s): %s", db_nm, ERROR_STR());
+		return -1;
+	}
+	if (db_fsize != (DB_HOFF)sb.st_size) {
+		if (db_fsize > (DB_HOFF)sb.st_size || !db_rdonly) {
+			db_failure(__LINE__,__FILE__, EX_IOERR, 0,
+				   "%s size changed from "OFF_HPAT
+				   " to "OFF_HPAT,
+				   db_nm, db_fsize, sb.st_size);
+			return -1;
+		}
+		db_fsize = sb.st_size;
+	}
+
+	db_locked = db_time;
+	return 0;
+}
+
+
+
+/* flush buffers to make the disk reasonably correct but not perfect
+ *	This does not compensate for a lack of coherent mmap() in the system.
+ *
+ *	It leaves the disk only as accurate as implied by db_not_synced.
+ *	This flushes buffers marked either urgent and ordinarily dirty.
+ *	If db_not_synced is set, then non-urgent dirty bits are not set. */
+static u_char
+make_clean_flush(void)
+{
+	DB_BUF *b;
+	u_char result;
+
+	result = 1;
+	for (b = buf_oldest; b != 0; b = b->newer) {
+		if (b->buf_type == DB_BUF_TYPE_FREE)
+			continue;
+		if (!buf_flush(0, b, 0))
+			result = 0;
+	}
+
+	return result;
+}
+
+
+
+/* push all of our database changes to the disk and try to clear the dirty bit
+ *	do not necessarily unmap anything */
+u_char
+make_clean(u_char clean)		/* 0=leave hash marked dirty, */
+{					/*	1=marked clean, 2=fsync */
+	u_char need_db_fsync, result;
+	struct stat sb;
+
+	rel_db_states();
+
+	result = 1;
+
+	/* quit if we are giving up */
+	if (db_invalidate)
+		return result;
+
+	if (db_failed_line)
+		clean = 0;
+
+	if (!make_clean_flush()) {
+		clean = 0;
+		result = 0;
+	}
+
+	/* simply unlock all of the buffers if they are clean
+	 * and do not need to (or cannot) be synchronized with fsync() */
+	if (!db_dirty
+	    && (clean < 2		/* not asked to synchronize */
+		|| db_rdonly		/* cannot be synchronized */
+		|| !db_not_synced))	/* does not need to be synchronized */
+		return result;
+
+	need_db_fsync = (clean == 2);
+
+	/* Send the meta-data to disk so that other processes
+	 * such as dbclean can find the new length of the file
+	 * on Solaris.  Otherwise the file looks broken because
+	 * its contained data length can be larger than its
+	 * inode size on Solaris. */
+	if (!need_db_fsync && clean) {
+		if (0 > fstat(db_fd, &sb)) {
+			dcc_error_msg("make_clean fstat(%s): %s",
+				      db_nm, ERROR_STR());
+			need_db_fsync = 1;
+		} else if (db_fsize != (DB_HOFF)sb.st_size) {
+			if (db_debug)
+				quiet_trace_msg("need fsync() because db_fsize="
+						OFF_HPAT" but stat="OFF_HPAT,
+						db_fsize, sb.st_size);
+			need_db_fsync = 1;
+		}
+	}
+
+	if (need_db_fsync
+	    && 0 > fsync(db_fd)) {
+		dcc_error_msg("make_clean fsync(%s): %s",
+			      db_nm, ERROR_STR());
+		clean = 0;
+		result = 0;
+	}
+
+	if (clean && !map_hash_ctl(0, 0)) {
+		clean = 0;
+		result = 0;
+	}
+	if (clean == 2) {
+		if (0 > fsync(db_hash_fd)) {
+			dcc_error_msg("make_clean fsync(%s): %s",
+				      db_hash_nm, ERROR_STR());
+			clean = 0;
+			result = 0;
+		} else {
+			db_not_synced = 0;
+			db_sts.hash_ctl.d.vals->s.flags &= ~HASH_CTL_FG_NOSYNC;
+			SET_FLUSH_HCTL(1);
+			if (!make_clean_flush()) {
+				clean = 0;
+				result = 0;
+			}
+		}
+	}
+
+	/* Clean the dirty flag in the hash table.
+	 * With luck, this will reach the disk after everything else. */
+	if (clean
+	    && !(db_sts.hash_ctl.d.vals->s.flags & HASH_CTL_FG_CLEAN)) {
+		db_sts.hash_ctl.d.vals->s.flags |= HASH_CTL_FG_CLEAN;
+		SET_FLUSH_HCTL(0);
+	}
+
+	/* finally flush the flag in the hash table */
+	rel_db_states();
+	if (!make_clean_flush())
+		result = 0;
+
+	if (clean)
+		db_dirty = 0;
+	return result;
+}
+
+
+
+/* mark the hash file and so the database dirty */
+static u_char
+db_make_dirty(DCC_EMSG emsg)
+{
+	if (db_dirty)
+		return 1;
+
+	if (!DB_IS_LOCKED()) {
+		dcc_logbad(EX_SOFTWARE, "dirtying unlocked database");
+		return 0;
+	}
+
+	if (db_rdonly)
+		dcc_logbad(EX_SOFTWARE, "dirtying read-only database");
+
+	if (!map_hash_ctl(emsg, 0))
+		return 0;
+	db_sts.hash_ctl.d.vals->s.flags &= ~HASH_CTL_FG_CLEAN;
+#ifdef USE_MAP_NOSYNC
+	if (!(db_sts.hash_ctl.d.vals->s.flags & HASH_CTL_FG_NOSYNC)) {
+		db_sts.hash_ctl.d.vals->s.synced = time(0);
+		db_sts.hash_ctl.d.vals->s.flags |= HASH_CTL_FG_NOSYNC;
+	}
+	db_not_synced = 1;
+#endif
+
+	SET_FLUSH_HCTL(1);
+	if (!buf_flush_part(emsg, db_sts.hash_ctl.b, 0, 0))
+		return 0;
+
+	db_dirty = 1;
+	return 1;
+}
+
+
+
+/* (start to) unlock the database */
+u_char					/* 0=failed, 1=at least started */
+db_unlock(void)
+{
+	DB_BUF *b;
+	int result;
+
+	if (!DB_IS_LOCKED())
+		return 1;
+
+	/* Clear the dirty bit in the database because we may not
+	 * be able to lock the database later to clear the dirty bit.
+	 * Dbclean needs to see the dirty bit clear. */
+	result = make_clean(1);
+
+	/* Release DB_BUF_FG_USE_WRITE buffers because they are not consistent
+	 *	among processes
+	 * Release everything if dccd wants stay out of RAM in favor
+	 *	of dbclean */
+	for (b = buf_oldest; b != 0; b = b->newer) {
+		if (b->buf_type == DB_BUF_TYPE_FREE)
+			continue;
+		if (db_minimum_map
+		    || (b->flags & DB_BUF_FG_USE_WRITE))
+			buf_munmap(0, b);
+	}
+
+	if (!dcc_unlock_fd(0, db_fd, DCC_LOCK_ALL_FILE, "", db_nm))
+		result = 0;
+	db_locked.tv_sec = 0;
+	return result;
+}
+
+
+
+static const char *
+mbyte2str(DB_PTR val)
+{
+	return db_ptr2str(val*1024*1024);
+}
+
+
+
+#if defined(RLIMIT_AS) || defined(RLIMIT_RSS) || defined(RLIMIT_FSIZE)
+static DB_PTR
+use_rlimit(int resource, const char *rlimit_nm,
+	   DB_PTR cur_val, DB_PTR min_val, const char *val_nm)
+{
+	struct rlimit limit_old, limit_new;
+	DB_PTR new_val;
+
+	if (0 > getrlimit(resource, &limit_old)) {
+		dcc_error_msg("getrlimit(%s): %s", rlimit_nm, ERROR_STR());
+		return cur_val;
+	}
+
+	if ((DB_PTR)limit_old.rlim_cur >= cur_val+DB_PAD_MBYTE*1024)
+		return cur_val;
+
+	/* assume we are root and try to increase the hard limit */
+	if ((DB_PTR)limit_new.rlim_max < cur_val+DB_PAD_BYTE) {
+		limit_new = limit_old;
+		limit_new.rlim_max = cur_val+DB_PAD_BYTE;
+		if (0 > setrlimit(resource, &limit_new)) {
+			if (db_debug)
+				quiet_trace_msg("setrlimit(%s, "
+						L_DPAT","L_DPAT"): %s",
+						rlimit_nm,
+						(DB_PTR)limit_new.rlim_cur,
+						(DB_PTR)limit_new.rlim_max,
+						ERROR_STR());
+		} else {
+			if (0 > getrlimit(resource, &limit_old)) {
+				dcc_error_msg("getrlimit(%s): %s",
+					      rlimit_nm, ERROR_STR());
+				return cur_val;
+			}
+		}
+	}
+
+	limit_new = limit_old;
+	if ((DB_PTR)limit_new.rlim_max < min_val+DB_PAD_BYTE)
+		limit_new.rlim_max = min_val + DB_PAD_BYTE;
+	limit_new.rlim_cur = limit_new.rlim_max;
+	if ((DB_PTR)limit_new.rlim_cur > cur_val+DB_PAD_BYTE)
+		limit_new.rlim_cur = cur_val+DB_PAD_BYTE;
+	if (0 > setrlimit(resource, &limit_new)) {
+		dcc_error_msg("setrlimit(%s, "L_DPAT","L_DPAT"): %s",
+			      rlimit_nm,
+			      (DB_PTR)limit_new.rlim_cur,
+			      (DB_PTR)limit_new.rlim_max,
+			      ERROR_STR());
+		new_val = limit_old.rlim_cur - DB_PAD_BYTE;
+		if (new_val < min_val)
+			new_val = min_val;
+	} else {
+		if (limit_old.rlim_cur < limit_new.rlim_cur
+		    && db_debug)
+			quiet_trace_msg("increased %s from %s to %s",
+					rlimit_nm,
+					db_ptr2str(limit_old.rlim_cur),
+#ifdef RLIM_INFINITY
+					(limit_new.rlim_cur == RLIM_INFINITY)
+					? "infinity" :
+#endif
+					db_ptr2str(limit_new.rlim_cur));
+		new_val = limit_new.rlim_cur - DB_PAD_BYTE;
+	}
+
+	if (cur_val > new_val) {
+		quiet_trace_msg("%s reduced %s from %s to %s",
+				rlimit_nm, val_nm,
+				db_ptr2str(cur_val),
+				db_ptr2str(new_val));
+		return new_val;
+	}
+
+	return cur_val;
+}
+#endif
+
+
+
+static void
+get_db_max_rss(void)
+{
+	DB_PTR old_val, new_val, db_min_mbyte, db_min_byte, db_max_mbyte;
+	int physmem_str_len;
+	DB_PTR physmem;
+
+	/* use default maximum if maximum is bogus or unset by ./configure */
+	db_max_mbyte = MAX_MAX_DB_MBYTE;
+#if DB_MAX_MBYTE != 0
+	db_max_mbyte = DB_MAX_MBYTE;
+	if (db_max_mbyte < DB_MIN_MIN_MBYTE
+	    || db_max_mbyte > MAX_MAX_DB_MBYTE) {
+		quiet_trace_msg("ignore bad ./configure --with-max-db-mem=%d",
+				DB_MAX_MBYTE);
+		db_max_mbyte = MAX_MAX_DB_MBYTE;
+	} else if (db_debug) {
+		quiet_trace_msg("DB max=%s"
+				" from ./configure --with-max-db-mem=%d",
+				mbyte2str(db_max_mbyte), DB_MAX_MBYTE);
+	}
+#endif
+#ifndef HAVE_BIG_FILES
+	/* we need big off_t for files larger than 2 GBytes */
+	if (db_max_mbyte > DB_MAX_2G_MBYTE) {
+		old_val = db_max_mbyte;
+		db_max_mbyte= DB_MAX_2G_MBYTE;
+		if (db_debug)
+			quiet_trace_msg("32-bit off_t reduced DB max from %s"
+					" to %s",
+					mbyte2str(old_val),
+					mbyte2str(db_max_mbyte));
+	}
+#endif
+
+	/* use default if ./configure --with-db-memory=MB is bogus or unset */
+#if DB_MIN_MBYTE == 0
+	db_min_mbyte = 64;
+#else
+	db_min_mbyte = DB_MIN_MBYTE;
+	if (db_min_mbyte < DB_MIN_MIN_MBYTE) {
+		quiet_trace_msg("ignore bad ./configure --with-db-memory=%d",
+				DB_MIN_MBYTE);
+		db_min_mbyte = DB_DEF_MIN_MBYTE;
+	} else if (db_min_mbyte > db_max_mbyte) {
+		quiet_trace_msg("ignore ./configure --with-db-memory=%d"
+				" > DB max=%s",
+				mbyte2str(db_max_mbyte));
+		db_min_mbyte = DB_DEF_MIN_MBYTE;
+	} else if (db_debug) {
+		quiet_trace_msg("use ./configure --with-db-memory=%d",
+				DB_MIN_MBYTE);
+	}
+#endif
+
+	db_min_byte = db_min_mbyte * (1024*1024);
+	db_max_byte = db_max_mbyte * (1024*1024);
+
+#ifdef RLIMIT_FSIZE
+	db_max_mbyte = (use_rlimit(RLIMIT_FSIZE, "RLIMIT_FSIZE",
+				   db_max_byte, db_min_byte, "DB max")
+			/ (1024*1024));
+	db_max_byte = db_max_mbyte * (1024*1024);
+#endif /* RLIMIT_FSIZE */
+
+	physmem = 0;
+#ifdef HAVE_PHYSMEM_TOTAL
+	/* maybe someday physmem_total() will be widely available */
+	physmem = physmem_total();
+	if (db_debug)
+		quiet_trace_msg("real=%s from physmem_total()",
+				db_ptr2str(physmem));
+#endif
+#ifdef HAVE__SC_PHYS_PAGES
+	if (physmem == 0) {
+		long pages, sizepage;
+
+		if ((pages = sysconf(_SC_PHYS_PAGES)) == -1) {
+			dcc_error_msg("sysconf(_SC_PHYS_PAGES): %s",
+				      ERROR_STR());
+		} else if ((sizepage = sysconf(_SC_PAGESIZE)) == -1) {
+			dcc_error_msg("sysconf(_SC_PAGESIZE): %s",
+				      ERROR_STR());
+		} else {
+			physmem = (DB_PTR)pages * (DB_PTR)sizepage;
+			if (db_debug)
+				quiet_trace_msg("real=%s"
+						" from sysconf(_SC_PHYS_PAGES)"
+						" and sysconf(_SC_PAGESIZE)",
+						db_ptr2str(physmem));
+		}
+	}
+#endif
+#ifdef HAVE_HW_PHYSMEM
+	if (physmem == 0) {
+		int mib[2] = {CTL_HW, HW_PHYSMEM};
+		unsigned long int hw_physmem;
+		size_t hw_physmem_len;
+
+		hw_physmem_len = sizeof(hw_physmem);
+		if (0 > sysctl(mib, 2, &hw_physmem, &hw_physmem_len, 0,0)) {
+			dcc_error_msg("sysctl(HW_PHYSMEM): %s", ERROR_STR());
+		} else {
+			physmem = hw_physmem;
+			if (db_debug)
+				quiet_trace_msg("real=%s from sysctl(mib)",
+						db_ptr2str(physmem));
+		}
+	}
+#endif
+#ifdef HAVE_PSTAT_GETSTATIC
+	if (physmem == 0) {
+		struct pst_static pss;
+
+		if (0 > pstat_getstatic(&pss, sizeof pss, 1, 0)) {
+			dcc_error_msg("pstat_getstatic(): %s", ERROR_STR());
+		} else if (pss.physical_memory <= 0
+			   || pss.page_size < 0) {
+			dcc_error_msg("pstat_getstatic() says"
+				      " physical_memory=%d page_size=%d",
+				      pss.physical_memory, pss.page_size);
+		} else {
+			physmem = ((DB_PTR)pss.physical_memory
+				   * (DB_PTR)pss.page_size);
+			if (db_debug)
+				quiet_trace_msg("real=%s"
+						" from pstat_getstatic()",
+						db_ptr2str(physmem));
+		}
+	}
+#endif
+
+	physmem_str_len = 0;
+	db_physmem_str[0] = '\0';
+	if (physmem == 0) {
+		quiet_trace_msg("failed to get real memory size");
+	} else {
+		physmem_str_len = snprintf(db_physmem_str,
+					   sizeof(db_physmem_str),
+					   "  real=%s",
+					   db_ptr2str(physmem));
+
+		/* Try to use half of physical memory
+		 *	if there is less than 2 GByte
+		 * all except 512 MByte between 2 GByte and 4 GByte,
+		 * and all but 1 GByte if there is more than 4 GByte */
+		if (physmem/(1024*1024) < 2*1024)
+			new_val = physmem/2;
+		else if (physmem/(1024*1024) <= 4*1024)
+			new_val = physmem - 512*(1024*1024);
+		else
+			new_val = physmem - 1024*(1024*1024);
+		if (new_val < db_min_byte) {
+			if (db_debug)
+				quiet_trace_msg("real=%s would give DB max=%s"
+						" smaller than minimum %s",
+						db_ptr2str(physmem),
+						db_ptr2str(new_val),
+						mbyte2str(db_min_mbyte));
+			new_val = db_min_byte;
+		}
+		if (db_max_byte > new_val) {
+			old_val = db_max_byte;
+			db_max_mbyte = new_val / (1024*1024);
+			db_max_byte = db_max_mbyte * (1024*1024);
+			if (db_debug)
+				quiet_trace_msg("real=%s reduced DB max"
+						" from %s to %s",
+						db_ptr2str(physmem),
+						db_ptr2str(old_val),
+						db_ptr2str(db_max_byte));
+		}
+	}
+
+	/* window need not be larger than the limit on the database size */
+	db_max_rss = db_max_byte;
+
+#ifdef RLIMIT_AS
+	/* try not to break process virtual memory limit,
+	 * but only if it is not ridiculously tiny */
+	db_max_rss = use_rlimit(RLIMIT_AS, "RLIMIT_AS",
+				db_max_rss, db_min_byte, "max RSS");
+#endif /* RLIMIT_AS */
+#ifdef RLIMIT_RSS
+	/* try not to break process resident memory limit
+	 * but only if it is not ridiculously tiny */
+	db_max_rss = use_rlimit(RLIMIT_RSS, "RLIMIT_RSS",
+				db_max_rss, db_min_byte, "max RSS");
+#endif /* RLIMIT_RSS */
+
+	/* limit the database to the window size */
+	if (db_max_byte > db_max_rss) {
+		old_val = db_max_mbyte;
+		db_max_mbyte = db_max_rss / (1024*1024);
+		db_max_byte = db_max_mbyte * (1024*1024);
+		if (db_debug)
+			quiet_trace_msg("max RSS reduced DB max from %s to %s",
+					mbyte2str(old_val),
+					mbyte2str(db_max_mbyte));
+	}
+
+#ifndef HAVE_64BIT_PTR
+	/* We cannot use a window larger than 2 GBytes on most systems without
+	 * big pointers.  Among the things that break is trying to mmap() more
+	 * than 2 GBytes.  So limit the window on 32-bit systems to a little
+	 * less than 2 GBytes and the database to not much more */
+	if (db_max_rss > DB_MAX_2G_MBYTE*(1024*1024)) {
+		if (db_debug)
+			quiet_trace_msg("32-bit pointers reduced max RSS"
+					" from %s to %s",
+					db_ptr2str(db_max_rss),
+					mbyte2str(DB_MAX_2G_MBYTE));
+		db_max_rss = DB_MAX_2G_MBYTE*(1024*1024);
+		new_val = db_max_rss+db_max_rss/4;
+		if (db_max_byte > new_val) {
+			old_val = db_max_mbyte;
+			db_max_mbyte = new_val / (1024*1024);
+			db_max_byte = db_max_mbyte * (1024*1024);
+			if (db_debug)
+				quiet_trace_msg("32-bit pointers reduced DB max"
+						" from %s to %s",
+						mbyte2str(old_val),
+						mbyte2str(db_max_mbyte));
+		}
+	}
+#endif
+
+	snprintf(&db_physmem_str[physmem_str_len],
+		 sizeof(db_physmem_str) - physmem_str_len,
+		 "  max RSS=%s  DB max=%s",
+		 db_ptr2str(db_max_rss), mbyte2str(db_max_mbyte));
+}
+
+
+
+/* Pick a buffer size that will hold an integral number of DB hash
+ * table entries and is a multiple of system's page size.
+ * The entire hash table should reside in memory
+ * if the system has enough memory. */
+u_int
+db_get_pagesize(u_int old_pagesize,	/* 0 or required page size */
+		u_int tgt_pagesize)	/* 0 or target page size */
+{
+	u_int min_pagesize, max_pagesize;
+
+	/* Ask the operating system only once so we don't get differing
+	 * answers and so compute a varying page size.
+	 * Some systems can't keep their stories straight. */
+	if (db_max_rss == 0)
+		get_db_max_rss();
+
+	/* Compute the least common multiple of the system page and
+	 * the DB hash table entry size.
+	 * This will give us the smallest page size that we can use. */
+	system_pagesize = getpagesize();
+	min_pagesize = lcm(system_pagesize, sizeof(HASH_ENTRY));
+
+	/* The kludge to speed conversion of database addresses to page numbers
+	 * and offsets on 32-bit systems depends on the page size being
+	 * a multiple of 256 */
+	if ((min_pagesize % (1<<DB_PTR_SHIFT)) != 0)
+		dcc_logbad(EX_SOFTWARE, "page size not a multiple of 256");
+
+	/* The DB buffer or page size must also be a multiple of the
+	 * the end-of-page padding used in the main database file. */
+	if (sizeof(DB_RCD) % DB_RCD_HDR_LEN != 0)
+		dcc_logbad(EX_SOFTWARE,
+			   "DB padding size %d"
+			   " is not a divisor of DB entry size %d",
+			   DB_RCD_HDR_LEN, ISZ(DB_RCD));
+	if (DB_RCD_LEN_MAX % DB_RCD_HDR_LEN != 0)
+		dcc_logbad(EX_SOFTWARE,
+			   "DB record not a multiple of header size");
+	min_pagesize = lcm(min_pagesize, DB_RCD_HDR_LEN);
+
+	/* Use the old buffer size if available so we are not confused
+	 * by padding at the ends of the old pages.
+	 * Fail if it is impossible.  This should cause dbclean to
+	 * rebuild the database. */
+	if (old_pagesize != 0) {
+		if ((old_pagesize % min_pagesize) != 0)
+			return 0;
+		/* adjust the number of buffers to fit our window size */
+		db_buf_total = db_max_rss / old_pagesize;
+		if (db_buf_total < (int)DB_BUF_MIN)
+			return 0;
+		if (db_buf_total > DB_BUF_MAX)
+			db_buf_total = DB_BUF_MAX;
+		return old_pagesize;
+	}
+
+	db_buf_total = DB_BUF_MAX;
+	max_pagesize = db_max_rss / db_buf_total;
+	max_pagesize -= max_pagesize % min_pagesize;
+
+	/* If we have a target page size, try to use it instead of the
+	 * maximum page size allowed by the resident set size.
+	 * Normal DCC databases grow large and want pages as large as possible
+	 * but greylist databases are often small.
+	 * We also want a tiny page when first reading the parameters while
+	 * opening. */
+	if (tgt_pagesize != 0 && tgt_pagesize < max_pagesize) {
+		tgt_pagesize -= tgt_pagesize % min_pagesize;
+		if (tgt_pagesize < min_pagesize)
+			tgt_pagesize = min_pagesize;
+		return tgt_pagesize;
+	} else if (max_pagesize > min_pagesize) {
+		return max_pagesize;
+	} else {
+		return min_pagesize;
+	}
+}
+
+
+
+/* (re)create the buffer pool
+ * The buffers are small blocks that point to the real mmap()'ed memory.
+ */
+u_char
+db_buf_init(u_int old_pagesize,		/* 0 or required page size */
+	    u_int tgt_pagesize)		/* 0 or target page size */
+{
+	DB_BUF *b, *bprev, *bnext;
+	int i;
+
+
+	db_pagesize = db_get_pagesize(old_pagesize, tgt_pagesize);
+	if (db_pagesize == 0)
+		return 0;
+
+	/* The fragments of pages must be multiples of system pages
+	 * so that msync() on Solaris can be given multiples of system
+	 * pages.  It's also a generally good idea. */
+	db_pagesize_part = db_pagesize/DB_BUF_NUM_PARTS;
+	db_pagesize_part = ((db_pagesize_part + system_pagesize-1)
+			    / system_pagesize) * system_pagesize;
+
+	db_page_max = db_pagesize - DB_RCD_HDR_LEN;
+	db_hash_page_len = db_pagesize/sizeof(HASH_ENTRY);
+
+	db_max_hash_entries = (MAX_HASH_ENTRIES
+			       - MAX_HASH_ENTRIES % db_hash_page_len);
+
+	memset(db_bufs, 0, sizeof(db_bufs));
+	b = db_bufs;
+	buf_oldest = b;
+	bprev = 0;
+	for (i = db_buf_total; --i != 0; b = bnext) {
+		bnext = b+1;
+		b->older = bprev;
+		b->newer = bnext;
+		bprev = b;
+	}
+	b->older = bprev;
+	buf_newest = b;
+
+	memset(db_buf_hash, 0, sizeof(db_buf_hash));
+
+	return 1;
+}
+
+
+
+static u_char
+make_new_hash(DCC_EMSG emsg, DB_HADDR new_hash_len)
+{
+	struct stat sb;
+	HASH_ENTRY *hash;
+	DB_HADDR next_haddr, cur_haddr, prev_haddr;
+	u_int pagenum;
+
+	if (getuid() == 0) {
+		/* if we are running as root,
+		 * don't change the owner of the database */
+		if (0 > fstat(db_fd, &sb)) {
+			dcc_pemsg(EX_IOERR, emsg, "fstat(%s): %s",
+				  db_nm, ERROR_STR());
+			return 0;
+		}
+		if (0 > fchown(db_hash_fd, sb.st_uid, sb.st_gid)) {
+			dcc_pemsg(EX_IOERR, emsg, "fchown(%s,%d,%d): %s",
+				  db_hash_nm, (int)sb.st_uid, (int)sb.st_gid,
+				  ERROR_STR());
+			return 0;
+		}
+	}
+
+	if (new_hash_len < MIN_HASH_ENTRIES)
+		new_hash_len = MIN_HASH_ENTRIES;
+
+	/* Increase the requested hash table size to a multiple of the database
+	 * page size.  The page size is chosen to be a multiple of the size of
+	 * a single hash table entry. */
+	db_hash_fsize = (((DB_HOFF)new_hash_len)*sizeof(HASH_ENTRY)
+			 + db_pagesize-1);
+	db_hash_fsize -= db_hash_fsize % db_pagesize;
+	new_hash_len = db_hash_fsize / sizeof(HASH_ENTRY);
+
+	if (new_hash_len > db_max_hash_entries)
+		new_hash_len = db_max_hash_entries;
+
+	/* create the empty hash table file */
+	rel_db_states();
+	if (!db_unload(emsg, 0))
+		return 0;
+	if (0 > ftruncate(db_hash_fd, 0)) {
+		dcc_pemsg(EX_IOERR, emsg, "truncate(%s,"L_HPAT"): %s",
+			  db_hash_nm, db_csize, ERROR_STR());
+		return 0;
+	}
+
+	db_hash_len = new_hash_len;
+	db_hash_used_stored_hash = db_hash_used = DB_HADDR_BASE;
+	db_hash_divisor = get_db_hash_divisor(db_hash_len);
+
+	/* Clear new hash file by linking its entries into the free list */
+	/* map and clear the first page */
+	if (!map_hash_ctl(emsg, 1))
+		return 0;
+
+	/* create the header */
+	strcpy(db_sts.hash_ctl.d.vals->s.magic, HASH_MAGIC_STR);
+	db_sts.hash_ctl.d.vals->s.free_fwd = DB_HADDR_BASE;
+	db_sts.hash_ctl.d.vals->s.free_bak = db_hash_len-1;
+	db_sts.hash_ctl.d.vals->s.len = db_hash_len;
+	db_sts.hash_ctl.d.vals->s.divisor = db_hash_divisor;
+	db_sts.hash_ctl.d.vals->s.used = DB_HADDR_BASE;
+	db_sts.hash_ctl.d.vals->s.synced = time(0);
+	db_dirty = 1;
+#ifdef USE_MAP_NOSYNC
+	db_sts.hash_ctl.d.vals->s.synced = time(0);
+	db_sts.hash_ctl.d.vals->s.flags |= HASH_CTL_FG_NOSYNC;
+	db_not_synced = 1;
+#endif
+
+	/* Link the hash table entries in the first and following pages.
+	 * The page size is chosen to be a multiple of the size of a
+	 * single hash table entry. */
+	prev_haddr = FREE_HADDR_END;
+	cur_haddr = DB_HADDR_BASE;
+	next_haddr = cur_haddr+1;
+	hash = &db_sts.hash_ctl.d.vals->h[DB_HADDR_BASE];
+	pagenum = 0;
+	for (;;) {
+		do {
+			DB_HADDR_CP(hash->bak, prev_haddr);
+			if (next_haddr == db_hash_len)
+				DB_HADDR_CP(hash->fwd, FREE_HADDR_END);
+			else
+				DB_HADDR_CP(hash->fwd, next_haddr);
+			++hash;
+			prev_haddr = cur_haddr;
+			cur_haddr = next_haddr++;
+		} while (cur_haddr % db_hash_page_len != 0);
+
+		if (++pagenum >= db_hash_fsize/db_pagesize)
+			break;
+
+		if (!map_hash(emsg, cur_haddr, &db_sts.free, 1))
+			return 0;
+		db_sts.free.b->flush_urgent = (DB_BUF_FM)-1;
+		hash = db_sts.free.d.h;
+	}
+
+	hash_clear_pg_num = 0;
+
+	return 1;
+}
+
+
+
+static u_char
+check_old_hash(DCC_EMSG emsg)
+{
+	static const u_char magic[sizeof(((HASH_CTL*)0)->s.magic)
+				  ] = HASH_MAGIC_STR;
+	const HASH_CTL *vals;
+	struct stat sb;
+	u_char old_db;
+
+	/* check the size of the existing hash file */
+	if (0 > fstat(db_hash_fd, &sb)) {
+		dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s",
+			  db_hash_nm, ERROR_STR());
+		return 0;
+	}
+	db_hash_fsize = sb.st_size;
+	if ((db_hash_fsize % sizeof(HASH_ENTRY)) != 0) {
+		dcc_pemsg(EX_DATAERR, emsg, "%s has size "OFF_DPAT","
+			  " not a multiple of %d",
+			  db_hash_nm, db_hash_fsize,
+			  ISZ(HASH_ENTRY));
+		return 0;
+	}
+
+	db_hash_len = db_hash_fsize/sizeof(HASH_ENTRY);
+	if (db_hash_len < MIN_HASH_ENTRIES) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s has too few records, "OFF_DPAT" bytes",
+			  db_hash_nm, db_hash_fsize);
+		return 0;
+	}
+
+	/* check the magic number */
+	if (!map_hash_ctl(emsg, 0))
+		return 0;
+	vals = db_sts.hash_ctl.d.vals;
+	if (memcmp(vals->s.magic, &magic, sizeof(magic))) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s has the wrong magic \"%.*s\"",
+			  db_hash_nm, ISZ(HASH_ENTRY), vals->s.magic);
+		return 0;
+	}
+
+	if (!(vals->s.flags & HASH_CTL_FG_CLEAN)) {
+		dcc_pemsg(EX_DATAERR, emsg, "%s was not closed cleanly",
+			  db_hash_nm);
+		return 0;
+	}
+	if (vals->s.flags & HASH_CTL_FG_NOSYNC) {
+#ifdef HAVE_BOOTTIME
+		int mib[2] = {CTL_KERN, KERN_BOOTTIME};
+		size_t boottime_len;
+#endif
+		struct timeval boottime;
+
+		boottime.tv_sec = 0x7fffffff;
+#ifdef HAVE_BOOTTIME
+		boottime_len = sizeof(boottime);
+		if (0 > sysctl(mib, 2, &boottime, &boottime_len, 0, 0)) {
+			dcc_error_msg("sysctl(KERN_BOOTTIME): %s", ERROR_STR());
+		}
+#endif
+		if (vals->s.synced <= boottime.tv_sec) {
+			dcc_pemsg(EX_DATAERR, emsg, "%s was not synchronized;"
+				  " synced=%d boottime=%d",
+				  db_hash_nm,
+				  (int)vals->s.synced, (int)boottime.tv_sec);
+			return 0;
+		}
+		db_not_synced = 1;
+	}
+
+	if (DB_HADDR_INVALID(vals->s.free_fwd)
+	    && (vals->s.free_fwd != FREE_HADDR_END
+		|| vals->s.free_fwd != vals->s.free_bak)) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s has a broken free list head of %#x",
+			  db_hash_nm, vals->s.free_fwd);
+		return 0;
+	}
+	if (DB_HADDR_INVALID(vals->s.free_bak)
+	    && (vals->s.free_bak != FREE_HADDR_END
+		|| vals->s.free_fwd != vals->s.free_bak)) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s has a broken free list tail of %#x",
+			  db_hash_nm, vals->s.free_bak);
+		return 0;
+	}
+
+	if (db_hash_len != vals->s.len) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s has %d entries but claims %d",
+			  db_hash_nm, db_hash_len,
+			  vals->s.len);
+		return 0;
+	}
+
+	db_hash_divisor = vals->s.divisor;
+	if (db_hash_divisor < MIN_HASH_DIVISOR
+	    || db_hash_divisor >= db_hash_len) {
+		dcc_pemsg(EX_DATAERR, emsg, "%s has hash divisor %d",
+			  db_hash_nm, db_hash_len);
+		return 0;
+	}
+
+	db_hash_used_stored_hash = db_hash_used = vals->s.used;
+	if (db_hash_used < DB_HADDR_BASE) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s contains impossible %u entries",
+			  db_hash_nm, HADDR2LEN(db_hash_used));
+		return 0;
+	}
+	if (db_hash_used >= db_hash_len) {
+		if (db_hash_used > db_hash_len)
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "%s contains only %u entries but %u used",
+				  db_hash_nm,
+				  HADDR2LEN(db_hash_len),
+				  HADDR2LEN(db_hash_used));
+		else
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "%s is filled with %u entries",
+				  db_hash_nm,
+				  HADDR2LEN(db_hash_len));
+		return 0;
+	}
+
+	/* old databases lack the growth values */
+	old_db = 0;
+	if (!db_rdonly
+	    && db_parms.old_db_csize == 0
+	    && db_parms.db_added == 0
+	    && db_parms.hash_used == 0
+	    && db_parms.old_hash_used == 0
+	    && db_parms.hash_added == 0
+	    && db_parms.rate_secs == 0
+	    && db_parms.last_rate_sec == 0) {
+		quiet_trace_msg("repair database growth measurements");
+		db_parms.old_db_csize = db_parms.db_csize;
+		old_db = 1;
+	}
+
+	if (db_hash_used != db_parms.hash_used
+	    && db_hash_fsize != 0) {
+		if (old_db) {
+			quiet_trace_msg("repair db_parms.old hash_used"
+					" and old_hash_used");
+			db_parms.old_hash_used = db_hash_used;
+			db_parms.hash_used = db_hash_used;
+		} else {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "%s contains %d"
+				  " entries instead of the %d that %s claims",
+				  db_hash_nm, db_hash_used,
+				  db_parms.hash_used, db_nm);
+			return 0;
+		}
+	}
+
+	db_csize_stored_hash = vals->s.db_csize;
+	if (db_csize_stored_hash != db_csize
+	    && db_hash_fsize != 0) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s contains "L_DPAT
+			  " bytes instead of the "L_DPAT" that %s claims",
+			  db_nm, db_csize,
+			  db_csize_stored_hash, db_hash_nm);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+/* open the files and generally get ready to work */
+u_char					/* 0=failed, 1=ok */
+db_open(DCC_EMSG emsg,
+	int new_db_fd,			/* -1 or already open db_fd */
+	const char *new_db_nm,
+	DB_HADDR new_hash_len,		/* 0 or # of entries */
+	DB_OPEN_MODES mode)			/* DB_OPEN_* */
+{
+	u_int cur_pagesize;
+	int hash_flags, db_open_flags;
+	struct stat db_sb;
+#	define OPEN_BAIL() {if (new_db_fd >= 0) db_fd = -1;		\
+		db_close(-1); return 0;}
+
+	db_close(1);
+	db_failed_line = __LINE__;
+	db_failed_file = __FILE__;
+	db_not_synced = 0;
+	db_minimum_map = 0;
+	db_invalidate = 0;
+	db_dirty = 0;
+	db_locked.tv_sec = 0;
+
+	db_rdonly = (mode & DB_OPEN_RDONLY) != 0;
+	db_use_write = (mode & DB_OPEN_MMAP_WRITE) != 0;
+
+	memset(&db_stats, 0, sizeof(db_stats));
+
+	if (!new_db_nm && db_nm[0] == '\0')
+		new_db_nm = grey_on ? DB_GREY_NAME : DB_DCC_NAME;
+	if (new_db_nm) {
+		if (!fnm2rel(db_nm, new_db_nm, 0)
+		    || !fnm2rel(db_hash_nm, db_nm, DB_HASH_SUFFIX)) {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "invalid DB nm \"%s\"", new_db_nm);
+			return 0;
+		}
+	}
+
+	if (new_db_fd >= 0) {
+		if (new_hash_len != 0) {
+			dcc_logbad(EX_SOFTWARE,
+				   "extending db_open(%s) without locking",
+				   db_nm);
+			return 0;
+		}
+		if (!db_rdonly) {
+			dcc_logbad(EX_SOFTWARE,
+				   "db_open(%s) read/write without locking",
+				   db_nm);
+			return 0;
+		}
+		db_open_flags = O_RDONLY;
+		hash_flags = O_RDONLY;
+
+		db_fd = new_db_fd;
+
+	} else {
+		db_open_flags = O_RDWR;
+		if (new_hash_len != 0) {
+			if (db_rdonly) {
+				dcc_logbad(EX_SOFTWARE,
+					   "db_open(%s) creating read-only",
+					   db_nm);
+				return 0;
+			}
+			hash_flags = O_RDWR | O_CREAT;
+		} else {
+			/* must open the file read/write to lock it */
+			hash_flags = O_RDWR;
+		}
+
+		db_fd = dcc_lock_open(emsg, db_nm, db_open_flags,
+				      (mode & DB_OPEN_LOCK_NOWAIT)
+				      ? DCC_LOCK_OPEN_NOWAIT
+				      : 0,
+				      DCC_LOCK_ALL_FILE, 0);
+		if (db_fd == -1) {
+			db_close(-1);
+			return 0;
+		}
+	}
+	gettimeofday(&db_time, 0);
+	db_locked = db_time;
+	if (0 > fstat(db_fd, &db_sb)) {
+		dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s", db_nm, ERROR_STR());
+		OPEN_BAIL();
+		return 0;
+	}
+	db_csize = db_fsize = db_sb.st_size;
+	if (db_fsize < ISZ(DB_HDR)) {
+		dcc_pemsg(EX_IOERR, emsg,
+			  "%s with %d bytes is too small to be a DCC database",
+			  db_nm, (int)db_fsize);
+		OPEN_BAIL();
+	}
+
+	/* check the header of the database file by temporarily mapping it */
+	db_buf_init(0, sizeof(DB_HDR));
+	if (!map_db(emsg, 0, sizeof(DB_HDR), &db_sts.db_parms, 0))
+		OPEN_BAIL();
+
+	db_parms_stored = *db_sts.db_parms.d.parms;
+	db_parms = *db_sts.db_parms.d.parms;
+
+	if (memcmp(db_parms.version, db_version_buf, sizeof(db_version_buf))) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s contains the wrong magic string \"%.*s\"",
+			  db_nm, ISZ(db_parms.version), db_parms.version);
+		OPEN_BAIL();
+	}
+	if (!(db_parms.flags & DB_PARM_FG_GREY) != !grey_on) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s is%s a greylist database but must%s be",
+			  db_nm,
+			  (db_parms.flags & DB_PARM_FG_GREY) ? "" : " not",
+			  grey_on ? "" : " not");
+		OPEN_BAIL();
+	}
+
+	cur_pagesize = db_parms.pagesize;
+
+	DB_SET_NOKEEP(db_parms.nokeep_cks, DCC_CK_INVALID);
+	DB_SET_NOKEEP(db_parms.nokeep_cks, DCC_CK_FLOD_PATH);
+	set_db_tholds(db_parms.nokeep_cks);
+
+	db_ck_fuzziness = grey_on ? grey_ck_fuzziness : dcc_ck_fuzziness;
+
+	db_csize = db_parms.db_csize;
+	if (db_csize < sizeof(DB_HDR)) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s says it contains "L_DPAT" bytes"
+			  " or fewer than the minimum of %d",
+			  db_nm, db_csize, DB_PTR_BASE);
+		/* that is a fatal error if we are not rebuilding */
+		if (new_hash_len != 0)
+			OPEN_BAIL();
+	}
+	if (db_csize > db_fsize) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s says it contains "L_DPAT" bytes"
+			  " or more than the actual size of "OFF_DPAT,
+			  db_nm, db_csize, db_fsize);
+		/* that is a fatal error if we are not rebuilding */
+		if (new_hash_len != 0)
+			OPEN_BAIL();
+	}
+
+	/* The buffer or page size we use must be the page size used to
+	 * write the files.  Try to change our size to match the file */
+	if (cur_pagesize != db_pagesize) {
+		db_invalidate = 1;
+		rel_db_states();
+		if (!db_unload(emsg, 0))
+			OPEN_BAIL();
+		db_invalidate = 0;
+		if (!db_buf_init(cur_pagesize, 0)) {
+			dcc_error_msg("%s has page size %d"
+				      " incompatible with %d in %s",
+				      db_nm,
+				      cur_pagesize, db_get_pagesize(0, 0),
+				      path2fnm(db_hash_nm));
+			OPEN_BAIL();
+		}
+	}
+
+	db_csize_stored_hash = 0;
+	db_hash_len = 0;
+	db_hash_fd = open(db_hash_nm, hash_flags, 0666);
+	if (db_hash_fd < 0) {
+		dcc_pemsg(EX_IOERR, emsg, "open(%s): %s",
+			  db_hash_nm, ERROR_STR());
+		OPEN_BAIL();
+	}
+	if (0 > fcntl(db_hash_fd, F_SETFD, FD_CLOEXEC)) {
+		dcc_pemsg(EX_IOERR, emsg, "fcntl(%s, FD_CLOEXEC): %s",
+			  db_hash_nm, ERROR_STR());
+		OPEN_BAIL();
+	}
+
+	if (new_hash_len != 0) {
+		if (!make_new_hash(emsg, new_hash_len))
+			OPEN_BAIL();
+	} else {
+		if (!check_old_hash(emsg))
+			OPEN_BAIL();
+	}
+
+	if (db_fsize % db_pagesize != 0) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s has size "OFF_HPAT","
+			  " not a multiple of its page size of %#x",
+			  db_nm, db_fsize, db_pagesize);
+		OPEN_BAIL();
+	}
+	if (db_fsize > db_csize + db_pagesize || db_csize > db_fsize) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s has size "OFF_HPAT" but claims "L_HPAT,
+			  db_nm, db_fsize, db_csize);
+		OPEN_BAIL();
+	}
+
+#ifndef USE_MAP_NOSYNC
+	/* Use `dbclean -F` on systems without mmap(NOSYNC) but with lots of
+	 * RAM.  Some Linux systems otherwise take too long to run dbclean. */
+	if (mode & DB_OPEN_MMAP_WRITE_NOSYNC) {
+		if (db_max_rss > db_fsize + db_hash_fsize)
+			db_use_write = 1;
+		if (db_debug)
+			quiet_trace_msg("db_max_rss="OFF_HPAT
+					" db_fsize+db_hash_fsize="OFF_HPAT
+					" so%s use -F",
+					db_max_rss, db_fsize+db_hash_fsize,
+					db_use_write ? "" : " do not");
+	}
+#endif
+
+	db_window_size = (DB_PTR)db_pagesize * db_buf_total;
+	snprintf(db_window_size_str, sizeof(db_window_size_str),
+		 "window=%s%s",
+		 db_ptr2str(db_window_size), db_physmem_str);
+	rel_db_states();
+	db_failed_line = 0;
+
+	return 1;
+#undef OPEN_BAIL
+}
+
+
+
+static u_char
+buf_munmap(DCC_EMSG emsg, DB_BUF *b)
+{
+	u_char result;
+
+	if (b->lock_cnt != 0)
+		dcc_logbad(EX_SOFTWARE, "unmapping locked DB buffer");
+
+	result = buf_flush(emsg, b, 1);
+
+	if (db_invalidate) {
+		if (0 > DCC_MADV_FREE(b->buf.v))
+			dcc_error_msg("madvise(FREE %s,%#x): %s",
+				      buf2path(b), db_pagesize, ERROR_STR());
+	}
+
+	if (0 > munmap(b->buf.v, db_pagesize)) {
+		db_failure(__LINE__,__FILE__, EX_IOERR, emsg,
+			   "munmap(%s,%d): %s",
+			   buf2path(b), db_pagesize, ERROR_STR());
+		result = 0;
+	}
+	b->buf.v = 0;
+	b->pg_num = -1;
+	b->buf_type = DB_BUF_TYPE_FREE;
+
+	return result;
+}
+
+
+
+static u_char
+buf_mmap(DCC_EMSG emsg, DB_BUF *b, DB_PG_NUM pg_num, u_char extend)
+{
+	int prot, flags;
+	off_t offset;
+	int fd;
+	void *p;
+	int retry;
+	u_char unloaded;
+
+
+	offset = (off_t)pg_num * (off_t)db_pagesize;
+	fd = buf2fd(b);
+
+	if (extend) {
+		offset = 0;
+#if defined(MAP_ANON)|| defined(MAP_ANONYMOUS)
+		fd = -1;
+		b->flags |= DB_BUF_FG_USE_WRITE | DB_BUF_FG_EXTENSION;
+#ifdef MAP_ANONYMOUS
+		/* Linux redefines things and requires either MAP_ANON
+		 * or MAP_PRIVATE; */
+		flags = MAP_ANONYMOUS| MAP_PRIVATE;
+#else
+		flags = MAP_ANON | MAP_PRIVATE;
+#endif /* MAP_ANONYMOUS */
+#else /* have neither MAP_ANON nor MAP_ANONYMOUS */
+		b->flags |= DB_BUF_FG_USE_WRITE;
+		flags = MAP_PRIVATE;
+#endif
+	} else if (db_rdonly) {
+		flags = MAP_SHARED;
+	} else if (db_use_write && !db_minimum_map) {
+		/* write() buffers instead of letting the Solaris virtual
+		 * memory system do it. Solaris will bog the system down doing
+		 * nothing but flushing dirty mmap() pages
+		 * We cannot use this hack in two processes simultaneously,
+		 * so do not use it in dccd while dbclean is running */
+		b->flags |= DB_BUF_FG_USE_WRITE;
+		flags = MAP_PRIVATE;
+	} else {
+#ifdef USE_MAP_NOSYNC
+		flags = (MAP_SHARED | MAP_NOSYNC);
+#else
+		flags = MAP_SHARED;
+#endif
+	}
+
+	prot = db_rdonly ? PROT_READ : (PROT_READ | PROT_WRITE);
+	for (retry = 1, unloaded = 2; unloaded > 1; ++retry) {
+		p = mmap(0, db_pagesize, prot, flags, fd, offset);
+
+		if (p == MAP_FAILED) {
+			if (errno == EACCES
+			    || errno == EBADF
+			    || errno == EINVAL
+			    || errno == ENODEV
+			    || retry > 20) {
+				dcc_pemsg(EX_IOERR, emsg,
+					  "try #%d"" mmap(%s"
+					  " %#x,%#x,%#x,%d,"OFF_HPAT"): %s",
+					  retry,
+					  buf2path(b),
+					  db_pagesize, prot, flags, fd, offset,
+					  ERROR_STR());
+				return 0;
+			}
+			dcc_error_msg("try #%d mmap(%s"
+				      " %#x,%#x,%#x,%d,"OFF_HPAT"): %s",
+				      retry,
+				      buf2path(b),
+				      db_pagesize, prot, flags, fd, offset,
+				      ERROR_STR());
+/* #define MMAP_FAIL_DEBUG 3 */
+#ifdef MMAP_FAIL_DEBUG
+		} else if (((uint)random() % MMAP_FAIL_DEBUG) == 0) {
+			/* pretend mmap() failed randomly */
+			dcc_error_msg(" test fail #%d mmap(%s,%#x,"OFF_HPAT")",
+				      retry,
+				      buf2path(b), db_pagesize, offset);
+			if (0 > munmap(p, db_pagesize))
+				dcc_error_msg( "test munmap(): %s",
+					      ERROR_STR());
+#endif
+		} else {
+			/* It worked.
+			 * Say so if it was not the first attempt. */
+			if (retry != 1)
+				dcc_error_msg("try #%d"
+					      " mmap(%s,%#x,"OFF_HPAT") ok",
+					      retry,
+					      buf2path(b), db_pagesize, offset);
+			break;
+		}
+
+		/* mmap() fails occassionally on some systems,
+		 * so try to release something and try again */
+		unloaded = db_unload(0, 1);
+	}
+
+
+	b->buf.v = p;
+	b->flush = 0;
+	b->flush_urgent = 0;
+
+	if (extend)
+		return 1;
+
+	/* madvise() on some systems including FreeBSD uses a lot of CPU cycles,
+	 * so it should not be done unless it is likely to do significant good.
+	 * Get all of our buffers if there is plenty of memory
+	 * and we are not trying to stay out of the way of dbclean. */
+	if (!db_minimum_map && db_fsize <= db_max_rss) {
+		/* The flat file would fit.  If the hash table would also
+		 * fit, tell the kernel to be aggressive */
+		if (db_fsize + db_hash_fsize <= db_max_rss
+		    && 0 > DCC_MADV_WILLNEED(p))
+			dcc_error_msg("madvise(WILLNEED %s,%#x): %s",
+				      buf2path(b), db_pagesize, ERROR_STR());
+	} else {
+		if (0 > DCC_MADV_RANDOM(p))
+			dcc_error_msg("madvise(RANDOM %s,%#x): %s",
+				      buf2path(b), db_pagesize, ERROR_STR());
+	}
+
+	return 1;
+}
+
+
+
+/* get a free buffer for a chunk of either the hash table or database files */
+static DB_BUF *
+get_free_buf(DCC_EMSG emsg, DB_BUF **bh)
+{
+	DB_BUF *b;
+
+	/* Look for an unlocked buffer.
+	 * We know there is one because we have more buffers than
+	 * can be locked simultaneously. */
+	b = buf_oldest;
+	for (;;) {
+		if (!b)
+			dcc_logbad(EX_SOFTWARE, "broken DB buffer MRU chain");
+		if (!b->lock_cnt)
+			break;
+		b = b->newer;
+	}
+
+	/* Found an unlocked buffer.
+	 * Unlink it from its hash chain. */
+	if (b->fwd)
+		b->fwd->bak = b->bak;
+	if (b->bak)
+		b->bak->fwd = b->fwd;
+	else if (b->hash)
+		*b->hash = b->fwd;
+	if (b->buf_type != DB_BUF_TYPE_FREE) {
+		if (!buf_munmap(emsg, b))
+			return 0;
+	}
+
+	b->flags = 0;
+
+	/* put it on the new hash chain */
+	b->bak = 0;
+	b->hash = bh;
+	b->fwd = *bh;
+	*bh = b;
+	if (b->fwd)
+		b->fwd->bak = b;
+
+	return b;
+}
+
+
+
+static DB_BUF *
+find_buf(DCC_EMSG emsg, DB_BUF_TYPE buf_type, DB_PG_NUM pg_num)
+{
+	DB_BUF *b, **bh;
+
+	bh = DB_BUF_HASH(pg_num, buf_type);
+	b = *bh;
+	for (;;) {
+		if (!b) {
+			/* we ran off the end of the buffer hash chain,
+			 * so get a free buffer */
+			b = get_free_buf(emsg, bh);
+			if (!b)
+				return 0;
+			b->buf_type = buf_type;
+			b->pg_num = pg_num;
+			break;
+		}
+		if (b->buf_type == buf_type
+		    && b->pg_num == pg_num)
+			break;		/* found the buffer we need */
+
+		b = b->fwd;
+	}
+
+	/* make the buffer newest */
+	if (buf_newest != b) {
+		/* unlink it */
+		b->newer->older = b->older;
+		if (b->older)
+			b->older->newer = b->newer;
+		else
+			buf_oldest = b->newer;
+		/* insert it at the head of the MRU list */
+		b->newer = 0;
+		b->older = buf_newest;
+		buf_newest->newer = b;
+		buf_newest = b;
+	}
+
+	return b;
+}
+
+
+
+static DB_BUF *
+find_st_buf(DCC_EMSG emsg, DB_BUF_TYPE buf_type, DB_STATE *st,
+	    DB_PG_NUM pg_num, u_char extend)
+{
+	DB_BUF *b;
+
+	/* release previous buffer unless it is the right one */
+	b = st->b;
+	if (b) {
+		if (b->pg_num == pg_num
+		    && b->buf_type == buf_type)
+			return b;	/* already have the target buffer */
+
+		st->b = 0;
+		st->d.v = 0;
+		if (--b->lock_cnt < 0)
+			dcc_logbad(EX_SOFTWARE, "bad database buffer lock");
+	}
+
+	/* look for the buffer */
+	b = find_buf(emsg, buf_type, pg_num);
+	if (!b)
+		return 0;
+
+	++b->lock_cnt;
+	if (b->buf.v) {
+		if (extend && !(b->flags & DB_BUF_FG_USE_WRITE))
+			dcc_logbad(EX_SOFTWARE, "extending ordinary buffer");
+
+	} else {
+		/* map it if it was not already known */
+		if (!buf_mmap(emsg, b, pg_num, extend)) {
+			b->buf_type = DB_BUF_TYPE_FREE;
+			b->pg_num = -1;
+			if (--b->lock_cnt != 0)
+				dcc_logbad(EX_SOFTWARE,
+					   "stolen database buffer lock %d",
+					   b->lock_cnt);
+			return 0;
+		}
+		if (buf_type == DB_BUF_TYPE_DB)
+			++db_stats.db_mmaps;
+		else if (buf_type == DB_BUF_TYPE_HASH)
+			++db_stats.hash_mmaps;
+	}
+
+	st->b = b;
+	st->d.v = 0;
+	return b;
+}
+
+
+
+static u_char
+map_hash_ctl(DCC_EMSG emsg, u_char new)
+{
+	DB_BUF *b;
+
+	b = find_st_buf(emsg, DB_BUF_TYPE_HASH, &db_sts.hash_ctl, 0, new);
+	if (!b)
+		return 0;
+	db_sts.hash_ctl.s.haddr = 0;
+	db_sts.hash_ctl.d.v = b->buf.v;
+	return 1;
+}
+
+
+
+/* mmap() a hash table entry */
+static u_char
+map_hash(DCC_EMSG emsg,
+	 DB_HADDR haddr,		/* this entry */
+	 DB_STATE *st,			/* point this to the entry */
+	 u_char new)
+{
+	DB_PG_NUM pg_num;
+	DB_PG_OFF pg_off;
+	DB_BUF *b;
+
+	if (haddr >= db_hash_len || haddr < DB_HADDR_BASE) {
+		dcc_pemsg(EX_DATAERR, emsg, "invalid hash address %#x",
+			  haddr);
+		return 0;
+	}
+
+	pg_num = haddr / db_hash_page_len;
+	pg_off = haddr % db_hash_page_len;
+
+	b = find_st_buf(emsg, DB_BUF_TYPE_HASH, st, pg_num, new);
+	if (!b)
+		return 0;
+	st->s.haddr = haddr;
+	st->d.h = &b->buf.h[pg_off];
+	return 1;
+}
+
+
+
+/* unlink a hash table entry from the free list
+ *	uses db_sts.tmp */
+static u_char
+unlink_free_hash(DCC_EMSG emsg,
+		 DB_STATE *hash_st)	/* remove this from the free list */
+{
+	DB_HADDR fwd, bak;
+
+	if (!db_make_dirty(emsg))
+		return 0;
+
+	fwd = DB_HADDR_EX(hash_st->d.h->fwd);
+	bak = DB_HADDR_EX(hash_st->d.h->bak);
+	if (!HE_IS_FREE(hash_st->d.h)
+	    || (DB_HADDR_INVALID(fwd) && fwd != FREE_HADDR_END)
+	    || (DB_HADDR_INVALID(bak) && bak != FREE_HADDR_END)
+	    || DB_HPTR_EX(hash_st->d.h->rcd) != DB_PTR_NULL) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "bad hash free list entry at %#x", hash_st->s.haddr);
+		return 0;
+	}
+
+	if (fwd != FREE_HADDR_END) {
+		if (!map_hash(emsg, fwd, &db_sts.tmp, 0))
+			return 0;
+		if (DB_HADDR_EX(db_sts.tmp.d.h->bak) != hash_st->s.haddr) {
+			dcc_pemsg(EX_DATAERR, emsg, "free %#x --> bad-free %#x",
+				  hash_st->s.haddr, fwd);
+			return 0;
+		}
+		DB_HADDR_CP(db_sts.tmp.d.h->bak, bak);
+		SET_FLUSH_HE(&db_sts.tmp);
+	} else {
+		if (!map_hash_ctl(emsg, 0))
+			return 0;
+		if (db_sts.hash_ctl.d.vals->s.free_bak != hash_st->s.haddr) {
+			dcc_pemsg(EX_DATAERR, emsg, "free %#x --> bad-free %#x",
+				  hash_st->s.haddr, fwd);
+			return 0;
+		}
+		db_sts.hash_ctl.d.vals->s.free_bak = bak;
+		SET_FLUSH_HCTL(0);
+	}
+
+	if (bak != FREE_HADDR_END) {
+		if (!map_hash(emsg, bak, &db_sts.tmp, 0))
+			return 0;
+		if (DB_HADDR_EX(db_sts.tmp.d.h->fwd) != hash_st->s.haddr) {
+			dcc_pemsg(EX_DATAERR, emsg, "bad free %#x <-- free %#x",
+				  bak, hash_st->s.haddr);
+			return 0;
+		}
+		DB_HADDR_CP(db_sts.tmp.d.h->fwd, fwd);
+		SET_FLUSH_HE(&db_sts.tmp);
+	} else {
+		if (!map_hash_ctl(emsg, 0))
+			return 0;
+		if (db_sts.hash_ctl.d.vals->s.free_fwd != hash_st->s.haddr) {
+			dcc_pemsg(EX_DATAERR, emsg, "free %#x --> bad-free %#x",
+				  hash_st->s.haddr, bak);
+			return 0;
+		}
+		db_sts.hash_ctl.d.vals->s.free_fwd = fwd;
+		SET_FLUSH_HCTL(0);
+	}
+
+	memset(hash_st->d.h, 0, sizeof(HASH_ENTRY));
+	SET_FLUSH_HE(hash_st);
+
+	++db_hash_used;
+	return 1;
+}
+
+
+
+/* get a free hash table entry and leave db_sts.free pointing to it */
+static u_char				/* 0=failed, 1=got it */
+get_free_hash(DCC_EMSG emsg,
+	      DB_HADDR result)		/* try near here */
+{
+	DB_HADDR pg_start, pg_lim, bak;
+	int i;
+
+	if (db_hash_len <= db_hash_used) {
+		dcc_pemsg(EX_OSFILE, emsg, "no free hash table entry;"
+			  " %d of %d used", db_hash_used, db_hash_len);
+		return 0;
+	}
+
+	/* Look first near the target */
+	if (result < DB_HADDR_BASE)
+		result = DB_HADDR_BASE;
+	pg_start = result - (result % db_hash_page_len);
+	pg_lim = pg_start + db_hash_page_len-1;
+	if (pg_lim >= db_hash_len)
+		pg_lim = db_hash_len-1;
+	for (i = 0; i < 3 && ++result < pg_lim; ++i) {
+		if (!map_hash(emsg, result, &db_sts.free, 0))
+			return 0;
+		if (HE_IS_FREE(db_sts.free.d.h))
+			return unlink_free_hash(emsg, &db_sts.free);
+	}
+
+	/* check the local ad hoc free list at the end of the page */
+	if (!map_hash(emsg, pg_lim, &db_sts.free, 0))
+		return 0;
+	if (HE_IS_FREE(db_sts.free.d.h)) {
+		/* the ad hoc free list is not empty,
+		 * so try to use the previous entry */
+		bak = DB_HADDR_EX(db_sts.free.d.h->bak);
+		if (bak != FREE_HADDR_END) {
+			if (!map_hash(emsg, bak, &db_sts.free, 0))
+				return 0;
+		}
+		return unlink_free_hash(emsg, &db_sts.free);
+	}
+
+
+	/* Give up and search from the start of the free list.  This happens
+	 * only when the current and all preceding pages are full. */
+	if (!map_hash_ctl(emsg, 0))
+		return 0;
+	result = db_sts.hash_ctl.d.vals->s.free_fwd;
+	if (DB_HADDR_INVALID(result)) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "broken hash free list head of %#x", result);
+		return 0;
+	}
+	if (!map_hash(emsg, result, &db_sts.free, 0))
+		return 0;
+	return unlink_free_hash(emsg, &db_sts.free);
+}
+
+
+
+/* mmap() a database entry
+ *	We assume that no database entry spans buffers,
+ *	and that there are enough buffers to accomodate all possible
+ *	concurrent requests. */
+static u_char
+map_db(DCC_EMSG emsg,
+       DB_PTR rptr,			/* address of the record */
+       u_int tgt_len,			/* its length */
+       DB_STATE *st,			/* point this to the record */
+       u_char extend)
+{
+	DB_PG_NUM pg_num;
+	DB_PG_OFF pg_off;
+	DB_BUF *b;
+
+	if (rptr+tgt_len > db_fsize) {
+		db_failure(__LINE__,__FILE__, EX_DATAERR, emsg,
+			   "invalid database address "L_HPAT" or length %d"
+			   " past db_fsize "OFF_HPAT" in %s",
+			   rptr, tgt_len, db_fsize, db_nm);
+		return 0;
+	}
+
+	/* Try to optimize this to avoid udivdi3() and umoddi3(),
+	 * because they are a major time sink here on 32-bit systems */
+	pg_num = DB_PTR2PG_NUM(rptr, db_pagesize);
+#ifdef HAVE_64BIT_LONG
+	pg_off = rptr % db_pagesize;
+#else
+	pg_off = rptr - pg_num*(DB_PTR)db_pagesize;
+#endif
+
+	/* do not go past the end of a buffer */
+	if (tgt_len+pg_off > db_pagesize) {
+		db_failure(__LINE__,__FILE__, EX_DATAERR, emsg,
+			   "invalid database address "L_HPAT
+			   " or length %#x in %s",
+			   rptr, tgt_len, db_nm);
+		return 0;
+	}
+
+	b = find_st_buf(emsg, DB_BUF_TYPE_DB, st, pg_num, extend);
+	if (!b)
+		return 0;
+	st->s.rptr = rptr;
+	st->d.r = (DB_RCD *)&b->buf.c[pg_off];
+	return 1;
+}
+
+
+
+u_char					/* 0=failed, 1=got it */
+db_map_rcd(DCC_EMSG emsg,
+	   DB_STATE *rcd_st,		/* point this to the record */
+	   DB_PTR rptr,			/* that is here */
+	   int *rcd_lenp)		/* put its length here */
+{
+	u_int rcd_len;
+
+	if (DB_PTR_IS_BAD(rptr)) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "getting bogus record at "L_HPAT", in %s",
+			  rptr, db_nm);
+		return 0;
+	}
+
+	if (!map_db(emsg, rptr, DB_RCD_HDR_LEN, rcd_st, 0))
+		return 0;
+	rcd_len = DB_RCD_LEN(rcd_st->d.r);
+
+	if (&rcd_st->d.c[rcd_len] > &rcd_st->b->buf.c[db_pagesize]) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "invalid checksum count %d at "L_HPAT" in %s",
+			  DB_NUM_CKS(rcd_st->d.r), rptr, db_nm);
+		return 0;
+	}
+
+	if (rcd_lenp)
+		*rcd_lenp = rcd_len;
+	return 1;
+}
+
+
+
+/* write the new sizes of the files into the files */
+static u_char
+db_set_sizes(DCC_EMSG emsg)
+{
+	u_char result = 1;
+
+	if (db_hash_fd != -1
+	    && (db_csize_stored_hash != db_csize
+		|| db_hash_used_stored_hash != db_hash_used)) {
+		if (!map_hash_ctl(emsg, 0)) {
+			result = 0;
+		} else {
+			db_sts.hash_ctl.d.vals->s.db_csize = db_csize;
+			db_csize_stored_hash = db_csize;
+
+			db_sts.hash_ctl.d.vals->s.used = db_hash_used;
+			db_hash_used_stored_hash = db_hash_used;
+
+			SET_FLUSH_HCTL(0);
+		}
+	}
+
+	if (db_fd != -1
+	    && (db_parms_stored.db_csize != db_csize
+		|| db_parms_stored.hash_used != db_hash_used)) {
+		if (!map_db(emsg, 0, sizeof(DB_HDR), &db_sts.db_parms, 0)) {
+			result = 0;
+		} else {
+			db_sts.db_parms.d.parms->db_csize = db_csize;
+			db_parms_stored.db_csize = db_csize;
+			db_parms.db_csize = db_csize;
+
+			db_sts.db_parms.d.parms->hash_used = db_hash_used;
+			db_parms_stored.hash_used = db_hash_used;
+			db_parms.hash_used = db_hash_used;
+
+			db_sts.db_parms.d.parms->last_rate_sec = db_time.tv_sec;
+			db_parms_stored.last_rate_sec = db_time.tv_sec;
+			db_parms.last_rate_sec = db_time.tv_sec;
+
+			db_set_flush(&db_sts.db_parms, 1, sizeof(DB_PARMS));
+		}
+	}
+
+	return result;
+}
+
+
+
+/* write the database parameters into the magic number headers of the files */
+u_char
+db_flush_parms(DCC_EMSG emsg)
+{
+	if (!db_set_sizes(emsg))
+		return 0;
+
+	if (db_fd == -1)
+		return 1;
+
+	if (memcmp(&db_parms, &db_parms_stored, sizeof(db_parms))) {
+		if (!map_db(emsg, 0, sizeof(DB_HDR), &db_sts.db_parms, 0))
+			return 0;
+
+		db_parms.pagesize = db_pagesize;
+
+		*db_sts.db_parms.d.parms = db_parms;
+		db_parms_stored = db_parms;
+
+		db_set_flush(&db_sts.db_parms, 1, sizeof(DB_PARMS));
+	}
+
+	return 1;
+}
+
+
+
+/* find a checksum in an already mapped record */
+DB_RCD_CK *				/* 0=not found, 1=broken database */
+db_find_ck(DCC_EMSG emsg,
+	   DB_RCD *rcd,
+	   DB_PTR rptr,
+	   DCC_CK_TYPES type)		/* find this type of checksum */
+{
+	DB_RCD_CK *rcd_ck;
+	int i;
+
+	rcd_ck = rcd->cks;
+	i = DB_NUM_CKS(rcd);
+	if (i >= DCC_NUM_CKS) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "impossible %d checksums in "L_HPAT" in %s",
+			  i, rptr, db_nm);
+		return (DB_RCD_CK *)1;
+	}
+
+	for (; i != 0; --i, ++rcd_ck) {
+		if (DB_CK_TYPE(rcd_ck) == type)
+			return rcd_ck;
+	}
+
+	return 0;
+}
+
+
+
+/* find a checksum type known to be in a record */
+DB_RCD_CK *				/* 0=it's not there */
+db_map_rcd_ck(DCC_EMSG emsg,
+	      DB_STATE *rcd_st,		/* point this to the record */
+	      DB_PTR rptr,		/* that is here */
+	      DCC_CK_TYPES type)	/* find this type of checksum */
+{
+	DB_RCD_CK *rcd_ck;
+
+	if (!db_map_rcd(emsg, rcd_st, rptr, 0))
+		return 0;
+
+	rcd_ck = db_find_ck(emsg, rcd_st->d.r, rptr, type);
+	if (rcd_ck == (DB_RCD_CK *)1)
+		return 0;
+	if (rcd_ck == 0) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "missing \"%s\" checksum in "L_HPAT" in %s",
+			  DB_TYPE2STR(type), rptr, db_nm);
+		return 0;
+	}
+	return rcd_ck;
+}
+
+
+
+static inline u_char			/* 1=has a small prime factor */
+modulus_has_divisor(DB_HADDR len)
+{
+	static int primes[] = {
+		3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
+		61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127,
+		131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193,
+		197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269,
+		271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
+		353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431,
+		433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499};
+	int *p;
+
+	for (p = &primes[0]; p <= LAST(primes); ++p) {
+		if ((len % *p) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+
+
+/* Get a modulus for the hash function that is tolerably likely to be
+ * relatively prime to most inputs.  The worst that happens when the modulus
+ * is composite is that large multiples of its factors will suffer more
+ * collisions. */
+DB_HADDR
+get_db_hash_divisor(DB_HADDR len)
+{
+	DB_HADDR divisor;
+
+	divisor = len - DB_HADDR_BASE;
+	if (!(divisor & 1))
+		--divisor;
+	while (divisor >= MIN_HASH_ENTRIES) {
+		if (modulus_has_divisor(divisor))
+			divisor -= 2;
+		else
+			break;
+	}
+	return divisor;
+}
+
+
+
+DB_HADDR
+db_hash(DCC_CK_TYPES type, const DCC_SUM sum)
+{
+	u_int64_t accum, wrap;
+	const u_int32_t *wp;
+	union {
+	    DCC_SUM	sum;
+	    u_int32_t	words[4];
+	} buf;
+	int align;
+	DB_HADDR haddr;
+
+#ifdef HAVE_64BIT_PTR
+	align = (u_int64_t)sum & 3;
+#else
+	align = (u_int)sum & 3;
+#endif
+	if (align == 0) {
+		/* We almost always take this branch because database
+		 * records contain 12+N*24 bytes.  That also implies that
+		 * we should not hope for better than 4 byte alignment. */
+		wp = (u_int32_t *)sum;
+	} else {
+		memcpy(buf.sum, sum, sizeof(buf.sum));
+		wp = buf.words;
+	}
+
+	/* MD5 checksums are uniformly distributed, and so DCC_SUMs are
+	 * directly useful for hashing except when they are server-IDs */
+	accum = *wp++;
+	accum += *wp++;
+	wrap = accum >>32;
+	accum <<= 32;
+	accum += wrap + type;
+	accum += *wp++;
+	accum += *wp;
+
+	haddr = accum % db_hash_divisor;
+	haddr += DB_HADDR_BASE;
+
+	/* do not hash into the last slot of a page, because it is used to
+	 * find local free slots */
+	if (haddr % db_hash_page_len == db_hash_page_len-1) {
+		++haddr;
+		if (haddr >= db_hash_len)
+			haddr = DB_HADDR_BASE;
+	}
+	return haddr;
+}
+
+
+
+/* look for a checksum in the hash table
+ *	return with an excuse, the home slot, or the last entry on
+ *	the collision chain */
+DB_FOUND
+db_lookup(DCC_EMSG emsg, DCC_CK_TYPES type, const DCC_SUM sum,
+	  DB_HADDR lo,			/* postpone if out of this window */
+	  DB_HADDR hi,
+	  DB_STATE *hash_st,		/* hash block for record or related */
+	  DB_STATE *rcd_st,		/* put the record or garbage here */
+	  DB_RCD_CK **prcd_ck)		/* point to cksum if found */
+{
+	DB_HADDR haddr, haddr_fwd, haddr_bak;
+	DB_PTR db_ptr;
+	DB_RCD_CK *found_ck;
+	DB_HADDR failsafe;
+
+	haddr = db_hash(type, sum);
+	if (haddr < lo || haddr > hi) {
+		if (lo == 0 && hi == MAX_HASH_ENTRIES) {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "out of range hash address");
+			return DB_FOUND_SYSERR;
+		}
+		return DB_FOUND_LATER;
+	}
+
+	if (prcd_ck)
+	    *prcd_ck = 0;
+
+	if (!map_hash(emsg, haddr, hash_st, 0))
+		return DB_FOUND_SYSERR;
+
+	if (HE_IS_FREE(hash_st->d.h))
+		return DB_FOUND_EMPTY;
+
+	if (!DB_HADDR_C_NULL(hash_st->d.h->bak))
+		return DB_FOUND_INTRUDER;
+
+	/* We know that the current hash table entry is in its home slot.
+	 * It might be for the key or checksum we are looking for
+	 * or it might be for some other checksum with the same hash value. */
+	for (failsafe = 0; failsafe <= db_hash_len; ++failsafe) {
+		if (HE_CMP(hash_st->d.h, type, sum)) {
+			/* This hash table entry could be for our target
+			 * checksum.  Read the corresponding record so we
+			 * decide whether we have a hash collision or we
+			 * have found a record containing our target checksum.
+			 *
+			 * find right type of checksum in the record */
+			db_ptr = DB_HPTR_EX(hash_st->d.h->rcd);
+			found_ck = db_map_rcd_ck(emsg, rcd_st, db_ptr, type);
+			if (!found_ck)
+				return DB_FOUND_SYSERR;
+			if (!memcmp(sum, found_ck->sum,
+				    sizeof(DCC_SUM))) {
+				if (prcd_ck)
+					*prcd_ck = found_ck;
+				return DB_FOUND_IT;
+			}
+		}
+
+		/* This DB record was a hash collision, or for a checksum
+		 * other than our target.
+		 * Fail if this is the end of the hash chain */
+		haddr_fwd = DB_HADDR_EX(hash_st->d.h->fwd);
+		if (haddr_fwd == DB_HADDR_NULL)
+			return DB_FOUND_CHAIN;
+
+		if (DB_HADDR_INVALID(haddr_fwd)) {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "broken hash chain fwd-link"
+				  " #%d %#x at %#x in %s",
+				  failsafe, haddr_fwd, haddr, db_hash_nm);
+			return DB_FOUND_SYSERR;
+		}
+
+		if (!map_hash(emsg, haddr_fwd, hash_st, 0))
+			return DB_FOUND_SYSERR;
+
+		haddr_bak = DB_HADDR_EX(hash_st->d.h->bak);
+		if (haddr_bak != haddr) {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "broken hash chain links #%d,"
+				  " %#x-->%#x but %#x<--%#x in %s",
+				  failsafe,
+				  haddr, haddr_fwd,
+				  haddr_bak, haddr_fwd,
+				  db_hash_nm);
+			return DB_FOUND_SYSERR;
+		}
+		haddr = haddr_fwd;
+	}
+	dcc_pemsg(EX_DATAERR, emsg, "infinite hash chain at %#x in %s",
+		  haddr, db_hash_nm);
+	return DB_FOUND_SYSERR;
+}
+
+
+
+/* combine checksums */
+DCC_TGTS
+db_sum_ck(DCC_TGTS prev,		/* previous sum */
+	  DCC_TGTS rcd_tgts,		/* from the record */
+	  DCC_CK_TYPES type UATTRIB)
+{
+	DCC_TGTS res;
+
+	/* This arithmetic must be commutative (after handling deleted
+	 * values), because inter-server flooding causes records to appear in
+	 * the database out of temporal order.
+	 *
+	 * DCC_TGTS_TOO_MANY can be thought of as a count of plus infinity.
+	 * DCC_TGTS_OK is like minus infinity.
+	 * DCC_TGTS_OK2 like half of minus infinity
+	 * DCC_TGTS_TOO_MANY (plus infinity) added to DCC_TGTS_OK (minus
+	 *	infinity) or DCC_TGTS_OK2 yields DCC_TGTS_OK or DCC_TGTS_OK2.
+	 *
+	 * Reputations never reach infinity.
+	 *
+	 * Claims of not-spam from all clients are discarded as they arrive
+	 * and before here. They can only come from the local white list
+	 */
+#define SUM_OK_DEL(p,r) {						    \
+		if (rcd_tgts == DCC_TGTS_OK || prev == DCC_TGTS_OK)	    \
+			return DCC_TGTS_OK;				    \
+		if (rcd_tgts == DCC_TGTS_OK2 || prev == DCC_TGTS_OK2)	    \
+			return DCC_TGTS_OK2;				    \
+		if (rcd_tgts == DCC_TGTS_DEL)				    \
+			return prev;					    \
+	}
+
+	res = prev+rcd_tgts;
+	if (res <= DCC_TGTS_TOO_MANY)
+		return res;
+
+	SUM_OK_DEL(prev, rcd_tgts);
+	return DCC_TGTS_TOO_MANY;
+#undef SUM_OK_DEL
+}
+
+
+
+/* delete all reports that contain the given checksum */
+static u_char				/* 1=done, 0=broken database */
+del_ck(DCC_EMSG emsg,
+       DCC_TGTS *res,			/* residual targets after deletion */
+       const DB_RCD *new,		/* delete reports older than this one */
+       DCC_CK_TYPES type,		/* delete this type of checksum */
+       DB_RCD_CK *prev_ck,		/* starting with this one */
+       DB_STATE *prev_st)		/* use this scratch state block */
+{
+	DB_PTR prev;
+
+	*res = 0;
+	for (;;) {
+		/* delete reports that are older than the delete request */
+		if (dcc_ts_newer_ts(&new->ts, &prev_st->d.r->ts)
+		    && DB_RCD_ID(prev_st->d.r) != DCC_ID_WHITE) {
+			DB_TGTS_RCD_SET(prev_st->d.r, 0);
+			DB_TGTS_CK_SET(prev_ck, 0);
+			SET_FLUSH_RCD(prev_st, 1);
+
+		} else {
+			/* sum reports that are not deleted */
+			*res = db_sum_ck(*res, DB_TGTS_RCD(prev_st->d.r), type);
+		}
+
+		prev = DB_PTR_EX(prev_ck->prev);
+		if (prev == DB_PTR_NULL)
+			return 1;
+		prev_ck = db_map_rcd_ck(emsg, prev_st, prev, type);
+		if (!prev_ck)
+			return 0;
+	}
+}
+
+
+
+/* see if the new and preceding records are from the same era */
+static inline u_char			/* 1=different eras */
+ck_old_spam(const DB_RCD *new, const DCC_TS* prev, DCC_CK_TYPES type)
+{
+	struct timeval tv;
+	time_t secs;
+	DCC_TS past;
+
+	secs = db_parms.ex_secs[type].spam;
+	if (secs > DCC_OLD_SPAM_SECS)
+		secs = DCC_OLD_SPAM_SECS;
+	dcc_ts2timeval(&tv, &new->ts);
+	dcc_timeval2ts(&past, &tv, -secs);
+
+	return dcc_ts_older_ts(prev, &past);
+}
+
+
+
+/* Mark reports made obsolete by a spam report
+ *	A new report of spam makes sufficiently old reports obsolete.
+ *
+ *	Sufficiently recent non-obsolete reports make a new report obsolete,
+ *	or at least not worth spending bandwidth to flood.
+ *	"Sufficiently recent" should be defined so that this server and
+ *	its downstream flooding peers always have reports of the checksums
+ *	in the report.  So we want to keep (not make obsolete) at least one
+ *	report per expiration duration.  We cannot know the expiration durations
+ *	of our peers, but we known DB_EXPIRE_SPAMSECS_DEF_MIN which influences
+ *	DCC_OLD_SPAM_SECS.
+ *
+ *	However, if another checksum in the new report was kept, then
+ *	prefer marking old checksums obsolete.
+ *
+ *	db_sts.rcd points to the new record
+ *	db_sts.rcd2 points the the previous record and is changed
+ */
+static u_char				/* 1=done, 0=broken database */
+ck_obs_spam(DCC_EMSG emsg,
+	    const DB_RCD *new,
+	    DCC_TGTS new_tgts,
+	    DB_RCD_CK *new_ck,
+	    DCC_CK_TYPES type,		/* check this type of checksum */
+	    DB_RCD_CK *prev_ck,		/* starting with this one */
+	    DCC_TGTS prev_ck_tgts,
+	    u_char *keeping_new)	/* 1=already keeping the new record */
+{
+	int limit;
+	DB_PTR prev;
+
+	limit = 100;
+	for (;;) {
+		/* preceding white listed entries make new entries obsolete */
+		if (DB_RCD_ID(db_sts.rcd2.d.r) == DCC_ID_WHITE) {
+			new_ck->type_fgs |= DB_CK_FG_OBS;
+			SET_FLUSH_RCD(&db_sts.rcd, 1);
+			return 1;
+		}
+
+		if (DB_CK_OBS(prev_ck)
+		    || DB_TGTS_RCD(db_sts.rcd2.d.r) == 0) {
+			/* notice duplicates and
+			 * don't look forever for recent non-obsolete report */
+			if (!memcmp(&new->ts, &db_sts.rcd2.d.r->ts,
+				    sizeof(new->ts))
+			    || --limit == 0) {
+				*keeping_new = 1;
+				return 1;
+			}
+
+		} else if (prev_ck_tgts != DCC_TGTS_TOO_MANY) {
+			/* Mark this predecessor obsolete because it
+			 * was before the checksum became spam. */
+			prev_ck->type_fgs |= DB_CK_FG_OBS;
+			SET_FLUSH_RCD(&db_sts.rcd2, 0);
+
+			/* continue backwards to mark more non-spam
+			 * predecessors obsolete */
+
+		} else if (!*keeping_new
+			   && ck_old_spam(new, &db_sts.rcd2.d.r->ts, type)) {
+			/* We do not yet have a reason to keep the new report
+			 * and this predecessor is at or after a spam report.
+			 * We need the new report because it and the
+			 * predecessor are from different eras.
+			 * If the new report is not of spam, it will be
+			 * compressed with a preceding spam report. */
+			*keeping_new = 1;
+			/* The predecessor is not needed if the new record
+			 * is for spam */
+			if (new_tgts == DCC_TGTS_TOO_MANY) {
+				prev_ck->type_fgs |= DB_CK_FG_OBS;
+				SET_FLUSH_RCD(&db_sts.rcd2, 0);
+			}
+			/* We're finished, because all older preceding reports
+			 * were marked obsolete when this older predecessor
+			 * was linked. */
+			return 1;
+
+		} else {
+			/* this predecessor is about as recent as the new
+			 * record, so the new record is unneeded noise that
+			 * would bloat other servers' databases. */
+			new_ck->type_fgs |= DB_CK_FG_OBS;
+			return 1;
+		}
+
+		prev = DB_PTR_EX(prev_ck->prev);
+		if (prev == DB_PTR_NULL) {
+			/* the new record is a new report of spam */
+			*keeping_new = 1;
+			return 1;
+		}
+
+		prev_ck = db_map_rcd_ck(emsg, &db_sts.rcd2, prev, type);
+		if (!prev_ck)
+			return 0;
+		prev_ck_tgts = DB_TGTS_CK(prev_ck);
+	}
+}
+
+
+
+/* mark extra server-ID declarations obsolete
+ *
+ *	db_sts.rcd points to the new record
+ *	db_sts.rcd2 points the the previous record and is changed */
+ static u_char				/* 1=done, 0=broken database */
+srvr_id_ck(DCC_EMSG emsg,
+	   const DB_RCD *new,
+	   DB_RCD_CK *new_ck,
+	   DB_RCD_CK *prev_ck)		/* starting with this one */
+{
+	DB_PTR prev;
+	DCC_SRVR_ID new_id, prev_id;
+	struct timeval tv;
+	DCC_TS week_ts;
+
+	dcc_ts2timeval(&tv, &new->ts);
+	tv.tv_usec = 0;
+	tv.tv_sec -= tv.tv_sec % (7*24*60*60);
+	dcc_timeval2ts(&week_ts, &tv, 0);
+
+	new_id = DB_RCD_ID(new);
+	for (;;) {
+		/* mark duplicate older declarations and deletions obsolete */
+		prev_id = DB_RCD_ID(db_sts.rcd2.d.r);
+		if (!DCC_ID_SRVR_TYPE(prev_id)
+		    || DB_TGTS_RCD(db_sts.rcd2.d.r) == 0) {
+			if (dcc_ts_newer_ts(&db_sts.rcd2.d.r->ts, &new->ts)) {
+				new_ck->type_fgs |= DB_CK_FG_OBS;
+				SET_FLUSH_RCD(&db_sts.rcd, 1);
+			} else {
+				prev_ck->type_fgs |= DB_CK_FG_OBS;
+				SET_FLUSH_RCD(&db_sts.rcd2, 1);
+			}
+			return 1;
+		}
+
+		/* Keep many identical type declarations as a kludge to ensure
+		 * that rewound flooding sends type declarations early.
+		 * Keep only one delcaration per week. */
+		if (DCC_ID_SRVR_TYPE(new_id)) {
+			/* Zap the new declaration and stop if the
+			 * new declaration is older than the predecessor. */
+			if (dcc_ts_newer_ts(&db_sts.rcd2.d.r->ts, &new->ts)) {
+				new_ck->type_fgs |= DB_CK_FG_OBS;
+				SET_FLUSH_RCD(&db_sts.rcd, 1);
+				return 1;
+			}
+
+			/* Stop when we find a duplicate type declaration
+			 * of a different week */
+			if (prev_id == new_id
+			    && dcc_ts_older_ts(&db_sts.rcd2.d.r->ts,
+					       &week_ts)) {
+				return 1;
+			}
+
+			/* continue zapping preceding declarations */
+			prev_ck->type_fgs |= DB_CK_FG_OBS;
+			SET_FLUSH_RCD(&db_sts.rcd2, 1);
+		}
+
+		prev = DB_PTR_EX(prev_ck->prev);
+		if (prev == DB_PTR_NULL)
+			return 1;
+
+		prev_ck = db_map_rcd_ck(emsg, &db_sts.rcd2,
+					prev, DCC_CK_SRVR_ID);
+		if (!prev_ck)
+			return 0;
+	}
+}
+
+
+
+/* Install pointers in the hash table for a record and fix the accumulated
+ *	counts in the record pointed to by db_sts.rcd
+ *	Use db_sts.rcd, db_sts.hash, db_sts.rcd2, db_sts.free, db_sts.tmp
+ *	The caller must deal with db_make_dirty() */
+u_char					/* 0=failed, 1=done */
+db_link_rcd(DCC_EMSG emsg, DB_HADDR lo, DB_HADDR hi)
+{
+	DCC_TGTS res;
+	DB_RCD *rcd;
+	DB_RCD_CK *prev_ck;
+	DB_RCD_CK *rcd_ck;
+	DCC_CK_TYPES rcd_type;
+	DCC_TGTS rcd_tgts, prev_ck_tgts;
+	int ck_num;
+	DB_HADDR haddr;
+	u_char keeping_new;
+
+	keeping_new = 0;
+	rcd = db_sts.rcd.d.r;
+	rcd_tgts = DB_TGTS_RCD_RAW(rcd);
+	rcd_ck = rcd->cks;
+	ck_num = DB_NUM_CKS(rcd);
+	if (ck_num > DIM(rcd->cks)) {
+		dcc_pemsg(EX_OSFILE, emsg,
+			  "bogus checksum count %#x at "L_HPAT" in %s",
+			  rcd->fgs_num_cks, db_sts.rcd.s.rptr, db_nm);
+		return 0;
+	}
+	for (; ck_num > 0; --ck_num, ++rcd_ck) {
+		rcd_type = DB_CK_TYPE(rcd_ck);
+		if (!DCC_CK_OK_DB(grey_on, rcd_type)) {
+			dcc_pemsg(EX_OSFILE, emsg,
+				  "invalid checksum type %s at "L_HPAT" in %s",
+				  DB_TYPE2STR(rcd_type),
+				  db_sts.rcd.s.rptr, db_nm);
+			return 0;
+		}
+
+		rcd_ck->prev = DB_PTR_CP(DB_PTR_NULL);
+
+		/* Do not link paths or whitelist file and line numbers */
+		if (rcd_type == DCC_CK_FLOD_PATH) {
+			DB_TGTS_CK_SET(rcd_ck, 0);
+			continue;
+		}
+
+		/* Do not link or total some checksums unless they are
+		 * whitelist entries.  If they are whitelist entries, they
+		 * will eventually get set to DCC_TGTS_OK or DCC_TGTS_OK2.
+		 * Blacklist entries are noticed later by server-ID
+		 * or do not matter DCC_TGTS_TOO_MANY. */
+		if (DB_TEST_NOKEEP(db_parms.nokeep_cks, rcd_type)
+		    && DB_RCD_ID(rcd) != DCC_ID_WHITE) {
+			DB_TGTS_CK_SET(rcd_ck, 1);
+			continue;
+		}
+
+		res = (rcd_tgts == DCC_TGTS_DEL) ? 0 : rcd_tgts;
+
+		switch (db_lookup(emsg, rcd_type, rcd_ck->sum, lo, hi,
+				  &db_sts.hash, &db_sts.rcd2, &prev_ck)) {
+		case DB_FOUND_SYSERR:
+			return 0;
+
+		case DB_FOUND_LATER:
+			continue;
+
+		case DB_FOUND_IT:
+			/* We found the checksum
+			 * Update the hash table to point to the new record */
+			DB_HPTR_CP(db_sts.hash.d.h->rcd, db_sts.rcd.s.rptr);
+			SET_FLUSH_HE(&db_sts.hash);
+			/* link new record to existing record */
+			rcd_ck->prev = DB_PTR_CP(db_sts.rcd2.s.rptr);
+
+			/* delete predecessors to a delete request
+			 * and compute the remaining sum */
+			if (rcd_tgts == DCC_TGTS_DEL) {
+				if (!del_ck(emsg, &res, rcd, rcd_type,
+					    prev_ck, &db_sts.rcd2))
+					return 0;
+				/* delete requests are obsolete if the
+				 * checksum is whitelisted */
+				if (res == DCC_TGTS_OK
+				    || res == DCC_TGTS_OK2)
+					rcd_ck->type_fgs |= DB_CK_FG_OBS;
+				break;
+			}
+
+			/* Simple checksum with a predecessor
+			 * This does not do the substantial extra work
+			 * to notice all delete requests that arrived early.
+			 * That problem is handled by the incoming flood
+			 * duplicate report detection mechanism.
+			 * We must detect precessors that were deleted because
+			 * they are partial duplicates of the new record. */
+			prev_ck_tgts = DB_TGTS_CK(prev_ck);
+			if (DB_RCD_SUMRY(rcd))
+				res = prev_ck_tgts;
+			else
+				res = db_sum_ck(prev_ck_tgts, res, rcd_type);
+			if ((res == DCC_TGTS_OK || res == DCC_TGTS_OK2
+			     || (DB_RCD_ID(db_sts.rcd2.d.r) == DCC_ID_WHITE))
+			    && DB_RCD_ID(rcd) != DCC_ID_WHITE){
+				/* obsolete whitelisted checksums */
+				rcd_ck->type_fgs |= DB_CK_FG_OBS;
+				break;
+			}
+			if (res == DCC_TGTS_TOO_MANY) {
+				/* mark obsolete unneeded reports of spam */
+				if (!DB_CK_OBS(rcd_ck)
+				    && !ck_obs_spam(emsg, rcd, rcd_tgts,
+						    rcd_ck, rcd_type,
+						    prev_ck, prev_ck_tgts,
+						    &keeping_new))
+					return 0;   /* (broken database) */
+			} else if (rcd_type == DCC_CK_SRVR_ID) {
+				/* mark obsolete server-ID assertions */
+				if (!DB_CK_OBS(rcd_ck)
+				    && !srvr_id_ck(emsg, rcd, rcd_ck, prev_ck))
+					return 0;   /* (broken database) */
+			}
+			break;
+
+		case DB_FOUND_EMPTY:
+			/* We found an empty hash table slot.
+			 * Update the slot to point to our new record
+			 * after removing it from the free list,
+			 * which marks it dirty. */
+			if (!unlink_free_hash(emsg, &db_sts.hash))
+				return 0;
+			DB_HPTR_CP(db_sts.hash.d.h->rcd, db_sts.rcd.s.rptr);
+			HE_MERGE(db_sts.hash.d.h,rcd_type, rcd_ck->sum);
+			if (res >= BULK_THRESHOLD)
+				keeping_new = 1;
+			break;
+
+		case DB_FOUND_CHAIN:
+			/* We found a hash collision, a chain of 1 or more
+			 * records with the same hash value.
+			 * Get a free slot, link it to the end of the
+			 * existing chain, and point it to the new record.
+			 * The buffer containing the free slot is marked
+			 * dirty when it is removed from the free list. */
+			if (!get_free_hash(emsg, db_sts.hash.s.haddr))
+				return 0;
+			DB_HADDR_CP(db_sts.free.d.h->bak, db_sts.hash.s.haddr);
+			DB_HADDR_CP(db_sts.hash.d.h->fwd, db_sts.free.s.haddr);
+			DB_HPTR_CP(db_sts.free.d.h->rcd, db_sts.rcd.s.rptr);
+			HE_MERGE(db_sts.free.d.h,rcd_type, rcd_ck->sum);
+			SET_FLUSH_HE(&db_sts.hash);
+			if (res >= BULK_THRESHOLD)
+				keeping_new = 1;
+			break;
+
+		case DB_FOUND_INTRUDER:
+			/* The home hash slot for our key contains an
+			 * intruder.  Move it to a new free slot */
+			if (!get_free_hash(emsg, db_sts.hash.s.haddr))
+				return 0;
+			*db_sts.free.d.h = *db_sts.hash.d.h;
+			/* re-link the neighbors of the intruder */
+			haddr = DB_HADDR_EX(db_sts.free.d.h->bak);
+			if (haddr == DB_HADDR_NULL) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "bad hash chain reverse link at %#x"
+					  " in %s",
+					  haddr, db_hash_nm);
+				return 0;
+			}
+			if (!map_hash(emsg, haddr, &db_sts.tmp, 0))
+				return 0;
+			DB_HADDR_CP(db_sts.tmp.d.h->fwd, db_sts.free.s.haddr);
+			SET_FLUSH_HE(&db_sts.tmp);
+			haddr = DB_HADDR_EX(db_sts.hash.d.h->fwd);
+			if (haddr != DB_HADDR_NULL) {
+				if (!map_hash(emsg, haddr, &db_sts.tmp, 0))
+					return 0;
+				DB_HADDR_CP(db_sts.tmp.d.h->bak,
+					    db_sts.free.s.haddr);
+				SET_FLUSH_HE(&db_sts.tmp);
+			}
+			/* install the new entry in its home slot */
+			DB_HADDR_CP(db_sts.hash.d.h->fwd, DB_HADDR_NULL);
+			DB_HADDR_CP(db_sts.hash.d.h->bak, DB_HADDR_NULL);
+			DB_HPTR_CP(db_sts.hash.d.h->rcd, db_sts.rcd.s.rptr);
+			HE_MERGE(db_sts.hash.d.h,rcd_type, rcd_ck->sum);
+			SET_FLUSH_HE(&db_sts.hash);
+			if (res >= BULK_THRESHOLD)
+				keeping_new = 1;
+			break;
+		}
+
+		/* Fix the checksum's total in the record */
+		DB_TGTS_CK_SET(rcd_ck, res);
+		SET_FLUSH_RCD(&db_sts.rcd, 0);
+	}
+
+	return db_set_sizes(emsg);
+}
+
+
+
+/* Add a record to the database and the hash table
+ *	The record must be known to be valid
+ *	Use db_sts.rcd, db_sts.hash, db_sts.rcd2, db_sts.free, db_sts.tmp
+ *	On exit db_sts.rcd points to the new record in the database */
+DB_PTR					/* 0=failed */
+db_add_rcd(DCC_EMSG emsg, const DB_RCD *new_rcd)
+{
+	u_int new_rcd_len, pad_len;
+	DB_PTR new_db_csize, rcd_pos, new_page_num;
+	DB_BUF *b;
+
+	if (!db_make_dirty(emsg))
+		return 0;
+
+	new_rcd_len = (sizeof(*new_rcd)
+		       - sizeof(new_rcd->cks)
+		       + (DB_NUM_CKS(new_rcd) * sizeof(new_rcd->cks[0])));
+
+	rcd_pos = db_csize;
+	new_db_csize = rcd_pos+new_rcd_len;
+
+	new_page_num = DB_PTR2PG_NUM(new_db_csize, db_pagesize);
+	if (new_page_num == DB_PTR2PG_NUM(db_csize, db_pagesize)) {
+		if (!map_db(emsg, rcd_pos, new_rcd_len, &db_sts.rcd, 0))
+			return 0;
+
+	} else {
+		/* fill with zeros to get past a page boundary. */
+		pad_len = new_page_num*db_pagesize - db_csize;
+		pad_len = (((pad_len + DB_RCD_HDR_LEN-1) / DB_RCD_HDR_LEN)
+			   * DB_RCD_HDR_LEN);
+		if (pad_len != 0) {
+			if (!map_db(emsg, db_csize, pad_len, &db_sts.rcd, 0))
+				return 0;
+			memset(db_sts.rcd.d.r, 0, pad_len);
+			db_set_flush(&db_sts.rcd, 1, pad_len);
+			db_csize += pad_len;
+
+			rcd_pos = db_csize;
+			new_db_csize = rcd_pos+new_rcd_len;
+		}
+
+		/* extend the file by writing a full page to it with write(),
+		 * because extending by mmap() often does not work */
+		db_fsize = db_csize+db_pagesize;
+		if (!map_db(emsg, rcd_pos, db_pagesize, &db_sts.rcd, 1))
+			return 0;
+		b = db_sts.rcd.b;
+		b->flush = (DB_BUF_FM)-1;
+
+		/* push new page to disk if dblist or dbclean is running */
+		if (db_minimum_map) {
+			rel_db_state(&db_sts.rcd);
+			if (!buf_munmap(emsg, b))
+				return 0;
+			if (!map_db(emsg, rcd_pos, new_rcd_len, &db_sts.rcd, 0))
+				return 0;
+		}
+	}
+
+	/* install the record */
+	memcpy(db_sts.rcd.d.r, new_rcd, new_rcd_len);
+	/* Mark its buffer to be sent to the disk to keep the database
+	 * as good as possible even if we crash.  We don't need to worry
+	 * about later changes to the hash links because dbclean will
+	 * rebuild them if we crash */
+	db_set_flush(&db_sts.rcd, 1, new_rcd_len);
+	db_csize = new_db_csize;
+
+	/* install pointers in the hash table
+	 * and update the total counts in the record */
+	if (!db_link_rcd(emsg, 0, MAX_HASH_ENTRIES))
+		return 0;
+
+	++db_stats.adds;
+	return rcd_pos;
+}
diff -r 000000000000 -r c7f6b056b673 srvrlib/db.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srvrlib/db.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,585 @@
+/* Distributed Checksum Clearinghouse database definitions
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.139 $Revision$
+ */
+
+#ifndef DB_H
+#define DB_H
+
+#include "srvr_defs.h"
+#include <math.h>
+
+extern u_char grey_on;
+
+#define DB_DCC_NAME	"dcc_db"
+#define DB_GREY_NAME	"grey_db"
+#define DB_HASH_SUFFIX  ".hash"
+#define DB_LOCK_SUFFIX	".lock"
+
+#define WHITELIST_NM(g)	    ((g) ? "grey_whitelist" : "whitelist")
+
+#define DB_VERSION3_STR "DCC checksum database version 3"
+#define DB_VERSION4_STR "DCC checksum database version 4"
+#define DB_VERSION5_STR "DCC checksum database version 5"
+/* tighten DB_EX_SEC in the next version */
+#define DB_VERSION_STR  DB_VERSION5_STR
+
+#define HASH_MAGIC_STR   "DCC hash 6"
+
+/* hash table indeces are only 32 bits */
+#define MAX_HASH_ENTRIES    0xffffffff
+
+#define MIN_CLEAN_HASH_ENTRIES  1024	/* run dbclean at this size */
+#define MIN_HASH_ENTRIES    (8*MIN_CLEAN_HASH_ENTRIES)
+#define DEF_HASH_ENTRIES    (6*1024*1024)
+#define MIN_HASH_DIVISOR    ((MIN_HASH_ENTRIES*7)/8)
+
+
+#define DB_CP3(x,v) do {u_int32_t _v = v; (x)[0] = _v>>16;		\
+    (x)[1] = _v>>8; (x)[2] = _v;} while (0)
+#define DB_CP4(x,v) do {u_int32_t _v = v; (x)[0] = _v>>24;		\
+    (x)[1] = _v>>16; (x)[2] = _v>>8; (x)[3] = _v;} while (0)
+#define DB_EX3(x) ((((u_int32_t)(x)[0])<<16) + ((x)[1]<<8) + (x)[2])
+#define DB_EX4(x) ((((u_int32_t)(x)[0])<<24) + (((u_int32_t)(x)[1])<<16) \
+		   + ((x)[2]<<8) + (x)[3])
+/* the least significant byte should be tested first */
+#define DB_ZERO3(x) ((x)[2] == 0 && (x)[1] == 0 && (x)[0] == 0)
+#define DB_ZERO4(x) ((x)[3] == 0 && (x)[2] == 0 && (x)[1] == 0 && (x)[0] == 0)
+
+
+/* a single checksum in a database record */
+typedef u_char      DB_TGTS[3];		/* a compressed count */
+typedef u_int64_t   DB_PTR;		/* database record offset */
+typedef u_int32_t   DB_PTR_C;		/*      compressed by DB_PTR_CP() */
+typedef struct {
+    DB_PTR_C    prev;			/* previous record for this checksum */
+    DB_TGTS     tgts;			/* accumulated reported targets */
+    DCC_CK_TYPE_B type_fgs;
+#    define      DB_CK_FG_OBS	0x80    /* obsolete report of a checksum */
+/* #    define	 DB_CK_	0x40		   once used */
+#    define	 DB_CK_MASK	0x0f
+#    define	 DB_CK_OBS(ck)	((ck)->type_fgs & DB_CK_FG_OBS)
+#    define      DB_CK_TYPE(ck)	((DCC_CK_TYPES)((ck)->type_fgs & DB_CK_MASK))
+    DCC_SUM     sum;
+} DB_RCD_CK;
+#define DB_TGTS_CK_SET(ck,v) DB_CP3((ck)->tgts,v)
+#define DB_TGTS_CK(ck) DB_EX3((ck)->tgts)
+
+/* shape of a checksum database entry */
+typedef struct {
+    DCC_TS      ts;			/* original server's creation date */
+    DCC_SRVR_ID srvr_id_auth;		/* initial server & client auth bit */
+#    define	 DB_RCD_ID(r)	((r)->srvr_id_auth & ~DCC_SRVR_ID_AUTH)
+    DB_TGTS     tgts_del;		/* # target addresses or delete flag */
+    u_char      fgs_num_cks;		/* # of cksums | flags */
+#    define      DB_RCD_FG_TRIM	    0x80    /* some checksums deleted */
+#    define	 DB_RCD_FG_SUMRY    0x40    /* fake summary record */
+#    define	 DB_RCD_FG_DELAY    0x20    /* delayed for fake summary */
+#    define	 DB_RCD_TRIMMED(r)  ((r)->fgs_num_cks & DB_RCD_FG_TRIM)
+#    define	 DB_RCD_SUMRY(r)    ((r)->fgs_num_cks & DB_RCD_FG_SUMRY)
+#    define	 DB_RCD_DELAY(r)    ((r)->fgs_num_cks & DB_RCD_FG_DELAY)
+#    define      DB_NUM_CKS(r)	    ((r)->fgs_num_cks & DB_CK_MASK)
+    DB_RCD_CK   cks[DCC_DIM_CKS];
+} DB_RCD;
+
+#define DB_RCD_HDR_LEN (ISZ(DB_RCD) - ISZ(DB_RCD_CK)*DCC_DIM_CKS)
+#define DB_RCD_LEN(r) (DB_RCD_HDR_LEN + DB_NUM_CKS(r) * ISZ(DB_RCD_CK))
+#define DB_RCD_LEN_MAX sizeof(DB_RCD)
+
+#define DB_TGTS_RCD_SET(r,v) DB_CP3((r)->tgts_del,v)
+#define DB_TGTS_RCD_RAW(r) DB_EX3((r)->tgts_del)
+static inline DCC_TGTS
+DB_TGTS_RCD(const DB_RCD *r)
+{
+	DCC_TGTS e = DB_TGTS_RCD_RAW(r);
+	return e == DCC_TGTS_DEL ? 0 : e;
+}
+
+/* this allows database of up to 48 GBytes */
+#define DB_PTR_MULT	    ((DB_PTR)12)    /* gcd of all sizes of DB_RCD */
+#define DB_PTR_CP(v)	    ((u_int32_t)((v) / DB_PTR_MULT))
+#define DB_PTR_EX(x)	    ((x) * DB_PTR_MULT)
+
+/* The kludge to speed conversion of database addresses to page numbers
+ * and offsets on 32-bit systems */
+#define DB_PTR_SHIFT 8
+#ifdef HAVE_64BIT_LONG
+#define DB_PTR2PG_NUM(p,s) ((p) / (s))
+#else
+#define DB_PTR2PG_NUM(p,s) ((u_int32_t)((p) >> DB_PTR_SHIFT)		\
+			    / (s >> DB_PTR_SHIFT))
+#endif
+
+#define DB_PTR_NULL	    0
+#define DB_PTR_BASE	    ISZ(DB_HDR)
+#define DB_PTR_MAX	    DB_PTR_EX((((DB_PTR)1)<<(sizeof(DB_PTR_C)*8)) -1)
+#define DB_PTR_BAD	    (DB_PTR_MAX+1)
+#define DB_PTR_IS_BAD(l)    ((l) < DB_PTR_BASE || (l) >= DB_PTR_MAX)
+
+
+typedef DCC_TS DB_SN;			/* database serial number */
+
+/* non-spam expiration */
+#define DB_EXPIRE_SECS_DEF	    (24*60*60)
+#define DB_EXPIRE_SECS_MAX	    DCC_MAX_SECS
+#define DB_EXPIRE_SECS_MIN	    (60*60)
+#define DB_EXPIRE_SECS_DEF_MIN	    (2*60*60)
+/* spam expiration */
+#define DB_EXPIRE_SPAMSECS_DEF	    (30*24*60*60)
+#define DB_EXPIRE_SPAMSECS_DEF_MIN  (1*24*60*60)
+#define DB_EXPIRE_REP_SECS_DEF	    DB_EXPIRE_SECS_DEF
+#define	DB_EXPIRE_REP_SPAMSECS_DEF  DB_EXPIRE_SPAMSECS_DEF
+/* keep server-ID declarations a week longer than reputations so that they
+ * will be first to be flooded */
+#define DB_EXPIRE_SRVR_ID_SECS	    (DB_EXPIRE_REP_SPAMSECS_DEF+7*24*60*60)
+
+/* re-announce spam this often */
+#define DCC_OLD_SPAM_SECS   (DB_EXPIRE_SPAMSECS_DEF_MIN/2)
+
+
+/* seconds to greylist or delay new mail messages
+ *  RFC 2821 says SMTP clients should wait at least 30 minutes to retry,
+ *  but 15 minutes seems more common than 30 minutes.  Many retry after
+ *  only 5 minutes, and some after only 1 (one!) second.  However,
+ *  many of those that retry after a few seconds keep trying for a minute
+ *  or two. */
+#define DEF_GREY_EMBARGO    270
+#define MAX_GREY_EMBARGO    (24*60*60)
+
+#define DEF_GREY_WINDOW	    (7*24*60*60)    /* wait as long as this */
+#define MAX_GREY_WINDOW	    (10*24*60*60)
+#define DEF_GREY_WHITE	    (63*24*60*60)   /* remember this long */
+#define MAX_GREY_WHITE	    DB_EXPIRE_SECS_MAX
+
+
+typedef struct {
+    DCC_TS      all;
+    DCC_TS      spam;
+} DB_EX_TS_TYPE;
+typedef DB_EX_TS_TYPE DB_EX_TS[DCC_DIM_CKS];
+typedef DCC_TS	DB_SPAM_EX_TS[DCC_DIM_CKS];
+typedef struct {
+    DCC_TGTS	unused;
+    int32_t	all;			/* allsecs */
+    int32_t	spam;			/* spamsecs */
+} DB_EX_SEC;
+typedef DB_EX_SEC DB_EX_SECS[DCC_DIM_CKS];
+
+#define DCC_CK_OK_GREY_CLNT(t) ((t) > DCC_CK_INVALID			    \
+				&& t <= DCC_CK_G_TRIPLE_R_BULK)
+#define DCC_CK_OK_GREY_FLOD(t) ((t) == DCC_CK_BODY			    \
+				|| ((t) >= DCC_CK_G_MSG_R_TOTAL		    \
+				    && (t) <= DCC_CK_FLOD_PATH)		    \
+				|| ((t) == DCC_CK_IP && grey_weak_ip))
+
+#define DEF_FLOD_THOLDS(g,t) ((g) ? 1					    \
+			      : t == DCC_CK_SRVR_ID ? 1 : BULK_THRESHOLD)
+
+#define DCC_CK_OK_DCC_CLNT(g,t) ((t) > DCC_CK_INVALID			    \
+				 && (t) <= DCC_CK_G_TRIPLE_R_BULK	    \
+				 && ((g)|| (t) <= DCC_CK_FUZ2))
+#define DCC_CK_OK_DB(g,t) ((t) > DCC_CK_INVALID && t <= DCC_CK_TYPE_LAST    \
+			   && ((g) || ((t) != DCC_CK_G_MSG_R_TOTAL	    \
+				       && (t) != DCC_CK_G_TRIPLE_R_BULK)))
+#define DCC_CK_OK_FLOD(g,t) ((g) ? DCC_CK_OK_GREY_FLOD(t)		    \
+			     : ((t) > DCC_CK_INVALID			    \
+				&& ((t) <= DCC_CK_FUZ2			    \
+				    || (t) == DCC_CK_FLOD_PATH		    \
+				    || (t) == DCC_CK_SRVR_ID)))
+
+
+typedef u_int32_t DB_NOKEEP_CKS;	/* bitmask of ignored checksums */
+#define DB_SET_NOKEEP(map,t)	((map) |= (1<<(t)))
+#define DB_RESET_NOKEEP(map,t)	((map) &= ~(1<<(t)))
+#define DB_TEST_NOKEEP(map,t)	((map) & (1<<(t)))
+
+/* relative fuzziness of checksums */
+#define DCC_CK_FUZ_LVL_NO   1		/* least fuzzy */
+#define DCC_CK_FUZ_LVL1	    2		/* somewhat fuzzy */
+#define DCC_CK_FUZ_LVL2	    3		/* fuzzier */
+#define DCC_CK_FUZ_LVL3	    4		/* reputations */
+#define DCC_CK_FUZ_LVL_REP  DCC_CK_FUZ_LVL3
+extern const u_char *db_ck_fuzziness;
+
+
+typedef DB_PTR		    DB_HOFF;	/* byte offset into hash table */
+typedef u_int32_t	    DB_HADDR;	/* index of a hash table entry */
+typedef u_char DB_HADDR_C[4];		/* compressed hash chain link */
+#define DB_HADDR_CP(x,v)    DB_CP4(x,v)
+#define DB_HADDR_EX(x)      DB_EX4(x)
+#define DB_HADDR_NULL       0		/* no-answer from hashing & linking */
+#define DB_HADDR_C_NULL(x)  DB_ZERO4(x)
+#define DB_HADDR_INVALID(h) ((h) < DB_HADDR_BASE || (h) >= db_hash_len)
+#define DB_HADDR_C_INVALID(h) DB_HADDR_INVALID(DB_HADDR_EX(h))
+
+typedef u_char DB_PTR_HC[4];
+#define DB_HPTR_CP(x,v) {u_int32_t _v = DB_PTR_CP(v);			\
+    (x)[0] = _v>>24; (x)[1] = _v>>16; (x)[2] = _v>>8; (x)[3] = _v;}
+#define DB_HPTR_EX(x) DB_PTR_EX(((x)[0]<<24) + ((x)[1]<<16)		\
+				+ ((x)[2]<<8) + (x)[3])
+
+
+/* shape of the magic string that starts a database */
+typedef char DB_VERSION_BUF[64];
+typedef struct {
+    DB_VERSION_BUF version;		/* see DB_VERSION_STR */
+    DB_PTR      db_csize;		/* size of database contents in bytes */
+    u_int32_t   pagesize;		/* size of 1 DB buffer */
+    DB_SN       sn;			/* creation or expiration serial # */
+    time_t	cleared;		/* when created */
+    time_t	cleaned;		/* real instead of repair cleaning */
+    time_t	cleaned_cron;		/* cleaned by cron */
+    DB_SPAM_EX_TS ex_spam;		/* recent expiration timestamps */
+    DB_SPAM_EX_TS ex_all;		/* recent expiration timestamps */
+    DB_EX_SECS  ex_secs;		/* recent expiration thresholds */
+    DB_NOKEEP_CKS nokeep_cks;		/* ignore these checksums */
+    u_int	flags;
+#    define DB_PARM_FG_GREY	0x01    /* greylist database */
+#    define DB_PARM_FG_CLEARED	0x02    /* new file */
+#    define DB_PARM_EXP_SET	0x04	/* have explicit expiration durations */
+    DB_PTR	old_db_csize;		/* size at last cleaning */
+    DB_PTR	db_added;		/* bytes previously added to database */
+    DB_HADDR	hash_used;		/* recent of entries used */
+    DB_HADDR	old_hash_used;		/* entries used at last cleaning */
+    DB_HADDR	hash_added;		/* entries added */
+    time_t	rate_secs;		/* denominator of rates */
+#    define	 DB_MIN_RATE_SECS (12*60*60)
+#    define	 DB_MAX_RATE_SECS (14*24*60*60)
+    time_t	last_rate_sec;
+    DB_HADDR	old_kept_cks;		/* reported checksums at cleaning */
+} DB_PARMS;
+typedef union {
+    DB_PARMS	p;
+    char	c[256*3];
+} DB_HDR;
+
+#ifdef DB_VERSION4_STR
+typedef struct {
+    DB_VERSION_BUF version;
+    DB_PTR      db_csize;
+    u_int32_t   pagesize;
+    DB_SN       sn;
+    time_t	cleared;
+    time_t	cleaned;
+    time_t	cleaned_cron;
+    DB_SPAM_EX_TS ex_spam;
+    DB_EX_SECS  ex_secs;
+    DB_NOKEEP_CKS nokeep_cks;
+    u_int	flags;
+    DB_PTR	old_db_csize;
+    DB_PTR	db_added;
+    DB_HADDR	hash_used;
+    DB_HADDR	old_hash_used;
+    DB_HADDR	hash_added;
+    time_t	rate_secs;
+    time_t	last_rate_sec;
+    DB_HADDR	old_kept_cks;
+} DB_V4_PARMS;
+#endif
+#ifdef DB_VERSION3_STR
+typedef struct {
+    DB_VERSION_BUF version;
+    DB_PTR      db_csize;
+    u_int32_t   pagesize;
+    DB_SN       sn;
+    DB_SPAM_EX_TS ex_spam;
+    DB_EX_SECS  ex_secs;
+    DB_NOKEEP_CKS nokeep_cks;
+    DCC_TGTS    unused[DCC_DIM_CKS];
+    u_int	flags;
+#    define DB_PARM_V3_FG_GREY		0x01
+#    define DB_PARM_V3_FG_SELF_CLEAN	0x02
+#    define DB_PARM_V3_FG_SELF_CLEAN2	0x04
+#    define DB_PARM_V3_FG_CLEARED	0x08
+    DB_PTR	old_db_csize;
+    DB_PTR	db_added;
+    DB_HADDR	hash_used;
+    DB_HADDR	old_hash_used;
+    DB_HADDR	hash_added;
+    time_t	rate_secs;
+    time_t	last_rate_sec;
+    DB_HADDR	old_kept_cks;
+} DB_V3_PARMS;
+#endif
+
+/* shape of a database hash table entry */
+typedef struct {
+    DB_HADDR_C  fwd, bak;		/* hash collision chain */
+    u_char	hv_type[2];		/* checksum type + some hash bits */
+#    define	 HE_TYPE(e)	((DCC_CK_TYPES)((e)->hv_type[0] & 0xf))
+#    define	 HE_IS_FREE(e)	((e)->hv_type[0] == 0)
+#    define	 HE_MERGE(e,t,s) ((e)->hv_type[0] = ((((s)[0])<<4)+t),	\
+				  (e)->hv_type[1] = (s)[1])
+#    define	 HE_CMP(e,t,s)	((e)->hv_type[1] == (s)[1]		\
+				 && (e)->hv_type[0] ==(u_char)((((s)[0])<<4)+t))
+
+    DB_PTR_HC   rcd;			/* record for this hash table entry */
+} HASH_ENTRY;
+
+
+
+typedef union {
+    HASH_ENTRY	h[8];			/* this must be larger than following */
+    struct {
+	char	    magic[16];
+	u_int	    flags;
+#	 define	     HASH_CTL_FG_CLEAN	0x01	/* consistent */
+#	 define	     HASH_CTL_FG_NOSYNC	0x02	/* pushed to stable storage */
+	DB_HADDR    free_fwd;		/* hash table internal free list */
+	DB_HADDR    free_bak;
+#	 define	     FREE_HADDR_END 1
+	DB_HADDR    len;		/* size of file in entries */
+	DB_HADDR    used;		/* entries actually used */
+	DB_HADDR    divisor;		/* hash modulus */
+	DB_PTR	    db_csize;		/* size of the database file */
+	time_t	    synced;
+    } s;
+} HASH_CTL;
+
+
+#define DB_HADDR_BASE	((DB_HADDR)((sizeof(HASH_CTL)+sizeof(HASH_ENTRY)-1) \
+				    / sizeof(HASH_ENTRY)))
+#define HADDR2LEN(l)	((int)((l)-DB_HADDR_BASE))  /* offset to length */
+
+
+/* control a block of mapped memory */
+typedef u_int16_t DB_PG_NUM;
+typedef u_int32_t DB_PG_OFF;
+typedef enum {
+    DB_BUF_TYPE_FREE = 0,
+    DB_BUF_TYPE_HASH,
+    DB_BUF_TYPE_DB
+} DB_BUF_TYPE;
+#ifdef HAVE_64BIT_LONG
+typedef u_int64_t DB_BUF_FM;
+#else
+typedef u_int32_t DB_BUF_FM;
+#endif
+#define DB_BUF_NUM_PARTS    (8*ISZ(DB_BUF_FM))
+#define PART2BIT(part)	    (((DB_BUF_FM)1) << (part))
+typedef struct db_buf {
+    struct db_buf *fwd, *bak, **hash;
+    struct db_buf *older, *newer;
+    union {
+	void	    *v;
+	HASH_ENTRY  *h;
+	char	    *c;
+    } buf;
+    DB_PG_NUM	pg_num;
+    int		lock_cnt;
+    DB_BUF_TYPE	buf_type;
+    DB_BUF_FM	flush;
+    DB_BUF_FM	flush_urgent;
+    struct {
+	char	*lo, *hi;
+    } ranges[DB_BUF_NUM_PARTS];
+    u_char	flags;
+#    define	 DB_BUF_FG_USE_WRITE	0x01    /* use write() */
+#    define	 DB_BUF_FG_EXTENSION	0x02	/* new page in file */
+} DB_BUF;
+
+/* context for searching for or adding a record */
+typedef struct {
+    union {				/* pointer to data in buffer */
+	void	    *v;
+	HASH_ENTRY  *h;
+	char	    *c;
+	DB_RCD      *r;
+	DB_PARMS    *parms;
+	HASH_CTL    *vals;
+    } d;
+    union {				/* database address */
+	DB_HADDR    haddr;
+	DB_PTR      rptr;
+    } s;
+    DB_BUF      *b;
+} DB_STATE;
+
+/* see db_close() before changing this */
+typedef struct {
+    DB_STATE	rcd;			/* must be first */
+    DB_STATE	rcd2;
+    DB_STATE	sumrcd;
+    DB_STATE	hash;
+    DB_STATE	free;
+    DB_STATE	tmp;
+    DB_STATE	db_parms;
+    DB_STATE	hash_ctl;		/* hash control info; must be last */
+} DB_STATES;
+extern DB_STATES db_sts;
+
+extern int db_failed_line;
+extern const char *db_failed_file;
+#define DB_ERROR_MSG(s) db_error_msg(__LINE__,__FILE__, "%s", s)
+#define DB_ERROR_MSG2(s1,s2) db_error_msg(__LINE__,__FILE__, "%s: %s", s1,s2)
+
+extern struct timeval db_time;
+#define DB_IS_TIME(tgt,lim) DCC_IS_TIME(db_time.tv_sec,tgt,lim)
+#define DB_ADJ_TIMER(tgt,lim,new) DCC_ADJ_TIMER(db_time.tv_sec,tgt,lim,new)
+
+extern u_char db_minimum_map;		/* this is dccd & dbclean is running */
+extern int db_fd, db_hash_fd;
+extern DCC_PATH db_nm, db_hash_nm;
+extern struct timeval db_locked;	/* 0 or when database was locked */
+extern int db_debug;
+extern DB_SN db_sn;
+
+extern DB_HOFF db_hash_fsize;		/* size of hash table file */
+extern DB_HADDR db_hash_len;		/* # of hash table entries */
+extern DB_HADDR db_hash_divisor;	/* modulus */
+extern DB_HADDR db_hash_used;		/* # of hash table entries in use */
+extern u_int db_hash_page_len;		/* # of HASH_ENTRY's per buffer */
+extern DB_HADDR db_max_hash_entries;	/* max size of hash table */
+extern DB_PTR db_fsize;			/* size of database file */
+extern DB_PTR db_csize;			/* size of database contents in bytes */
+extern const DB_VERSION_BUF db_version_buf;
+extern DB_PARMS db_parms;
+extern DCC_TGTS db_tholds[DCC_DIM_CKS];
+extern u_int db_pagesize;		/* size of 1 DB buffer */
+extern u_int db_page_max;		/* only padding after this */
+extern char db_window_size_str[];	/* size of mmap() window */
+
+typedef struct {
+    u_int   db_mmaps;
+    u_int   hash_mmaps;
+    u_int   adds;			/* reports added */
+} DB_STATS;
+extern DB_STATS db_stats;
+
+
+/* If the two files were smaller than the typical mmap() limit of a fraction
+ * of a GByte, they could be mmap()'ed directly.  They are often too large.
+ *
+ * Use a modest pool of buffers to map the DB hash table and the database
+ * itself.
+ * Each access to the files could be with a single, common buffer,
+ * but that would involve many more mmap() system calls.
+ * Most of the DB hash table is expected to fit in the application's memory.
+ *
+ * Use the same modest pool of buffers to map the database itself.
+ * References to the database have a lot of locality, so the commonly used
+ * checksums and counts should remain in memory.
+ *
+ * Common operating system limits on the number of mapped segments are
+ * below 256 and so that is a bound on DB_BUF_MAX */
+#define DB_BUF_MAX 128			/* maximum # of buffers */
+#define DB_BUF_PARTS_MAX    (DB_BUF_MAX*DB_BUF_NUM_PARTS)
+
+/* enough buffers so max simultaneous pointers can be satisfied */
+#define DB_BUF_MIN (sizeof(DB_STATES)/sizeof(DB_STATE) + 2)
+
+extern int db_buf_total;		/* total # of db buffers */
+extern DB_PTR db_max_rss;		/* maximum db resident set size */
+extern DB_PTR db_max_byte;
+
+
+extern time_t db_need_flush_secs;
+#define DB_NEED_FLUSH_SECS	5
+#define DB_STALE_SECS		(30*60)	/* limit on buffer staleness */
+#define	DB_FLUSHES		(DB_STALE_SECS / DB_NEED_FLUSH_SECS)
+#define DB_PARTS_PER_FLUSH	((DB_BUF_PARTS_MAX + DB_FLUSHES-1) / DB_FLUSHES)
+#define DB_URGENT_NEED_FLUSH_SECS   120
+
+
+/* fix configure script if this changes */
+#define DB_MIN_MIN_MBYTE    32
+#define DB_DEF_MIN_MBYTE    64		/* a reasonable tiny default */
+#define DB_PAD_MBYTE	    128		/* RAM for rate limiting blocks etc */
+#define DB_PAD_BYTE	(DB_PAD_MBYTE*1024*1024)
+#define DB_MAX_2G_MBYTE	(2048-DB_PAD_MBYTE) /* <2 GByte on 32 bit machines */
+/* the database cannot exceed 48 GBytes because of DB_PTR_CP */
+#define MAX_MAX_DB_MBYTE    (48*1024)
+/*	fix INSTALL.html if those change */
+
+
+/* srvr/db.c */
+extern void db_failure(int, const char *, int, DCC_EMSG,
+		       const char *, ...) PATTRIB(5,6);
+extern void db_error_msg(int, const char *, const char *, ...) PATTRIB(3,4);
+extern void db_set_flush(DB_STATE *, u_char, u_int);
+#define SET_FLUSH_RCD(st,u)	db_set_flush(st,u, DB_RCD_LEN((st)->d.r))
+#define SET_FLUSH_RCD_HDR(st,u)	db_set_flush(st,u, DB_RCD_HDR_LEN)
+#define SET_FLUSH_HE(st)	db_set_flush(st, 0, sizeof(HASH_ENTRY))
+#define SET_FLUSH_HCTL(u)   db_set_flush(&db_sts.hash_ctl,u, sizeof(HASH_CTL))
+extern void rel_db_states(void);
+extern u_char db_unload(DCC_EMSG, u_char);
+extern u_char db_flush_db(DCC_EMSG);
+extern u_char make_clean(u_char);
+extern u_char db_close(int);
+extern void db_stop(void);
+extern u_char lock_dbclean(DCC_EMSG, const char *);
+extern void unlock_dbclean(void);
+extern u_int db_get_pagesize(u_int, u_int);
+extern u_char db_buf_init(u_int, u_int);
+typedef u_char DB_OPEN_MODES;
+# define DB_OPEN_RDONLY		    0x01
+# define DB_OPEN_LOCK_WAIT	    0x02    /* wait to get lock */
+# define DB_OPEN_LOCK_NOWAIT	    0x04    /* get lock but don't wait */
+# define DB_OPEN_MMAP_WRITE	    0x08    /* use write() instead of mmap() */
+# define DB_OPEN_MMAP_WRITE_NOSYNC  0x10    /*	    if no mmap(NOSYNC) */
+extern u_char db_open(DCC_EMSG, int, const char *, DB_HADDR, DB_OPEN_MODES);
+extern u_char db_flush_parms(DCC_EMSG );
+#define DB_IS_LOCKED() (db_locked.tv_sec != 0)
+extern int db_lock(void);
+extern u_char db_unlock(void);
+extern void db_flush_needed(void);
+extern DCC_TGTS db_sum_ck(DCC_TGTS, DCC_TGTS, DCC_CK_TYPES);
+extern const char *db_ptr2str(DB_PTR);
+extern const char *size2str(char *, u_int, double, u_char);
+extern double db_add_rate(const DB_PARMS *, u_char);
+extern DB_NOKEEP_CKS def_nokeep_cks(void);
+extern void set_db_tholds(DB_NOKEEP_CKS);
+extern u_char db_map_rcd(DCC_EMSG, DB_STATE *, DB_PTR, int *);
+extern DB_RCD_CK *db_find_ck(DCC_EMSG, DB_RCD *, DB_PTR, DCC_CK_TYPES);
+extern DB_RCD_CK *db_map_rcd_ck(DCC_EMSG, DB_STATE *, DB_PTR, DCC_CK_TYPES);
+extern DB_HADDR get_db_hash_divisor(DB_HADDR);
+extern DB_HADDR db_hash(DCC_CK_TYPES, const DCC_SUM);
+typedef enum {
+    DB_FOUND_SYSERR=0,			/* fatal error */
+    DB_FOUND_LATER,			/* out of specified hash table range */
+    DB_FOUND_IT,
+    DB_FOUND_EMPTY,			/* home slot empty */
+    DB_FOUND_CHAIN,			/* not in chain--have last entry */
+    DB_FOUND_INTRUDER			/* intruder in home slot */
+} DB_FOUND;
+extern DB_FOUND db_lookup(DCC_EMSG, DCC_CK_TYPES, const DCC_SUM,
+			  DB_HADDR, DB_HADDR, DB_STATE *,
+			  DB_STATE *, DB_RCD_CK **);
+extern u_char db_link_rcd(DCC_EMSG, DB_HADDR, DB_HADDR);
+extern DB_PTR db_add_rcd(DCC_EMSG, const DB_RCD *);
+
+#endif /* DB_H */
diff -r 000000000000 -r c7f6b056b673 srvrlib/flod.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srvrlib/flod.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,440 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * open, create, and check DCC server output flood mapped file
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.58 $Revision$
+ */
+
+#include "srvr_defs.h"
+
+FLOD_MMAPS *flod_mmaps;
+DCC_PATH flod_mmap_path;
+DCC_PATH flod_path;
+
+static int mmap_fd = -1;
+
+
+void
+flod_mmap_path_set(void)
+{
+	fnm2rel_good(flod_mmap_path,
+		     grey_on ? GREY_FLOD_NM : DCCD_FLOD_NM,
+		     ".map");
+	fnm2rel_good(flod_path,
+		     grey_on ? GREY_FLOD_NM : DCCD_FLOD_NM,
+		     0);
+}
+
+
+
+u_char
+flod_mmap_sync(DCC_EMSG emsg, u_char touch)
+{
+	u_char result = 1;
+
+	if (flod_mmap_path[0] == '\0')
+		flod_mmap_path_set();
+
+	if (flod_mmaps
+	    && 0 > MSYNC(flod_mmaps, sizeof(*flod_mmaps), MS_SYNC)) {
+		dcc_pemsg(EX_IOERR, emsg, "msync(%s): %s",
+			  flod_mmap_path, ERROR_STR());
+		result = 0;
+		emsg = 0;
+	}
+
+	if (touch) {
+		if (mmap_fd >= 0
+		    && 0 > fsync(mmap_fd)) {
+			dcc_pemsg(EX_IOERR, emsg, "fsync(%s): %s",
+				  flod_mmap_path, ERROR_STR());
+			result = 0;
+			emsg = 0;
+		}
+
+		/* Ensure that the mtime changes at least occassionally.
+		 * Several systems do not update the mtimes of files modified
+		 * with mmap().  Some like BSD/OS delay changing the mtime until
+		 * the file is accessed with read().  Others including
+		 * filesystems on some versions of Linux apparently never change
+		 * the mtime. */
+		if (!dcc_set_mtime(emsg, flod_mmap_path, mmap_fd, 0)) {
+			result = 0;
+			emsg = 0;
+		}
+	}
+
+	return result;
+}
+
+
+
+u_char					/* 1=no problems, 0=complaints */
+flod_unmap(DCC_EMSG emsg, const DCCD_STATS *dccd_stats)
+{
+	u_char result = 1;
+
+	if (!flod_mmap_sync(emsg, 0)) {
+		result = 0;
+		emsg = 0;
+	}
+
+	if (flod_mmaps) {
+		if (dccd_stats)
+			memcpy(&flod_mmaps->dccd_stats, dccd_stats,
+			       sizeof(flod_mmaps->dccd_stats));
+		if (0 > munmap((void *)flod_mmaps, sizeof(*flod_mmaps))) {
+			dcc_pemsg(EX_IOERR, emsg, "munmap(%s,%d): %s",
+				  flod_mmap_path,
+				  ISZ(*flod_mmaps), ERROR_STR());
+			result = 0;
+			emsg = 0;
+		}
+		flod_mmaps = 0;
+	}
+
+	if (mmap_fd >= 0) {
+		if (close(mmap_fd) < 0) {
+			dcc_pemsg(EX_IOERR, emsg, "close(%s): %s",
+				  flod_mmap_path, ERROR_STR());
+			result = 0;
+			emsg = 0;
+		}
+		mmap_fd = -1;
+	}
+
+	return result;
+}
+
+
+
+static int				/* 1=success, 0=retry, -1=fatal */
+flod_mmap_try(DCC_EMSG emsg,
+	      const DB_SN *sn,
+	      u_char rw)		/* 0=rdonly/unlocked, 1=write/locked */
+{
+	int flags;
+	struct stat sb;
+	union {
+	    FLOD_MMAPS	m;
+	    u_char	b[1];
+	} init;
+	void *p;
+	int i;
+
+	if (flod_mmap_path[0] == '\0')
+		flod_mmap_path_set();
+
+	if (rw)
+		mmap_fd = dcc_lock_open(emsg, flod_mmap_path, O_RDWR|O_CREAT,
+					DCC_LOCK_OPEN_NOWAIT,
+					DCC_LOCK_ALL_FILE, 0);
+	else
+		mmap_fd = dcc_lock_open(emsg, flod_mmap_path, O_RDONLY,
+					DCC_LOCK_OPEN_NOLOCK, 0, 0);
+	if (mmap_fd == -1)
+		return (errno == EWOULDBLOCK) ? -1 : 0;
+
+	if (fstat(mmap_fd, &sb) < 0) {
+		dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s",
+			  flod_mmap_path, ERROR_STR());
+		flod_unmap(0, 0);
+		return 0;
+	}
+	if (0 > fcntl(mmap_fd, F_SETFD, FD_CLOEXEC)) {
+		dcc_pemsg(EX_IOERR, emsg, "fcntl(%s, FD_CLOEXEC): %s",
+			  flod_mmap_path, ERROR_STR());
+		flod_unmap(0, 0);
+		return 0;
+	}
+
+	if (sb.st_size == 0 && rw) {
+		memset(&init, 0, sizeof(init));
+		strcpy(init.m.magic, FLOD_MMAP_MAGIC);
+		if (sn)
+			init.m.sn = *sn;
+		i = write(mmap_fd, &init, sizeof(init));
+		if (i < 0) {
+			dcc_pemsg(EX_IOERR, emsg, "write(%s, init): %s",
+				  flod_mmap_path, ERROR_STR());
+			flod_unmap(0, 0);
+			return -1;
+		}
+		if (i != ISZ(init)) {
+			dcc_pemsg(EX_IOERR, emsg,
+				  "write(%s, init)=%d instead of %d",
+				  flod_mmap_path, i, ISZ(init));
+			flod_unmap(0, 0);
+			return -1;
+		}
+	} else {
+		i = read(mmap_fd, &init, sizeof(init));
+		if (i < 0) {
+			dcc_pemsg(EX_IOERR, emsg, "read(%s, init): %s",
+				  flod_mmap_path, ERROR_STR());
+			flod_unmap(0, 0);
+			return -1;
+		}
+		if (i < ISZ(init)) {
+			if (i < sb.st_size) {
+				dcc_pemsg(EX_IOERR, emsg, "read(%s, init)=%d",
+					  flod_mmap_path, i);
+				flod_unmap(0, 0);
+				return -1;
+			}
+		}
+	}
+
+	if (sb.st_size >= (off_t)sizeof(init.m.magic)
+	    && strcmp(init.m.magic, FLOD_MMAP_MAGIC)) {
+		dcc_pemsg(EX_IOERR, emsg, "wrong magic in %s;"
+			  " \"%s\" instead of \"%s\"",
+			  flod_mmap_path, init.m.magic, FLOD_MMAP_MAGIC);
+		flod_unmap(0, 0);
+		return 0;
+	}
+
+	flags = rw ? (PROT_READ|PROT_WRITE) : PROT_READ;
+	p = mmap(0, sizeof(*flod_mmaps), flags, MAP_SHARED, mmap_fd, 0);
+	if (p == MAP_FAILED) {
+		dcc_pemsg(EX_IOERR, emsg, "mmap(%s): %s",
+			  flod_mmap_path, ERROR_STR());
+		flod_unmap(0, 0);
+		return 0;
+	}
+	flod_mmaps = p;
+
+	if (sb.st_size == 0) {
+		dcc_pemsg(EX_IOERR, emsg, "%s (re)created", flod_mmap_path);
+	} else if (sb.st_size != sizeof(FLOD_MMAPS)) {
+		dcc_pemsg(EX_IOERR, emsg, "%s has size %d instead of %d",
+			  flod_mmap_path, (int)sb.st_size, ISZ(FLOD_MMAPS));
+		flod_unmap(0, 0);
+		return 0;
+	}
+
+	if (sn
+	    && memcmp(&flod_mmaps->sn, sn, sizeof(flod_mmaps->sn))) {
+		char sn1_buf[30], sn2_buf[32];
+		dcc_pemsg(EX_IOERR, emsg, "s/n %s instead of %s in %s",
+			  ts2str(sn1_buf, sizeof(sn1_buf), &flod_mmaps->sn),
+			  ts2str(sn2_buf, sizeof(sn2_buf), sn),
+			  flod_mmap_path);
+		flod_unmap(0, 0);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+u_char					/* 0=failed, 1=mapped */
+flod_mmap(DCC_EMSG emsg,
+	  const DB_SN *sn,
+	  const DCCD_STATS *dccd_stats,
+	  u_char rw,			/* 0=rdonly/unlocked, 1=write/locked */
+	  u_char trace)
+{
+	int i;
+
+	if (!flod_unmap(emsg, dccd_stats) && emsg) {
+		if (trace)
+			dcc_trace_msg("%s", emsg);
+		*emsg = '\0';
+	}
+
+	/* try to open the existing file */
+	i = flod_mmap_try(emsg, sn, rw);
+
+	/* finished if that went well or we are not allowed to fix things */
+	if (i != 0 || !rw)
+		return i > 0;
+
+	/* delete the file if it is broken */
+	if (emsg && *emsg != '\0') {
+		if (trace)
+			dcc_trace_msg("%s", emsg);
+		*emsg = '\0';
+	}
+	if (0 > unlink(flod_mmap_path)
+	    && errno != ENOENT) {
+		dcc_pemsg(EX_IOERR, emsg, "unlink(%s): %s",
+			  flod_mmap_path, ERROR_STR());
+		flod_unmap(0, 0);
+		return 0;
+	}
+	flod_unmap(0, 0);
+
+	/* try to recreate the file */
+	if (flod_mmap_try(emsg, sn, 1) > 0) {
+		if (emsg) {
+			if (trace)
+				dcc_trace_msg("%s", emsg);
+			*emsg = '\0';
+		}
+		return 1;
+	}
+
+	flod_unmap(0, 0);
+	return 0;
+}
+
+
+
+const char *
+flod_stats_printf(char *buf, int buf_len,
+		  int st,		/* 0=off, 1=restarting, 2=on */
+		  int oflods_total,
+		  int oflods_active,
+		  int iflods_active)
+{
+	snprintf(buf, buf_len,
+		 "flood %s %3d streams  %d out active %d in",
+		 st == 0 ? "off" : st == 1 ? "restarting" : "on",
+		 oflods_total, oflods_active, iflods_active);
+	return buf;
+}
+
+
+
+static void
+mmap_fg_sub(char **bufp, int *buf_lenp,
+	    const char **prefix, const char *str)
+{
+	int i;
+
+	i = snprintf(*bufp, *buf_lenp, "%s%s", *prefix, str);
+	if (*buf_lenp <= i) {
+		*buf_lenp = 0;
+	} else {
+		*bufp += i;
+		*buf_lenp -= i;
+	}
+	*prefix = "  ";
+}
+
+
+const char *
+flodmap_fg(char *buf, int buf_len,
+	     const char *prefix, const FLOD_MMAP *mp)
+{
+	char *buf0 = buf;
+
+	if (!buf_len)
+		return "";
+	*buf = '\0';
+	if (!mp)
+		return buf0;
+
+	if ((mp->flags & FLODMAP_FG_IN_OFF)
+	    && (mp->flags & FLODMAP_FG_OUT_OFF)) {
+		mmap_fg_sub(&buf, &buf_len, &prefix, "off");
+	} else {
+		if (mp->flags & FLODMAP_FG_IN_OFF)
+			mmap_fg_sub(&buf, &buf_len, &prefix, "in off");
+		if (mp->flags & FLODMAP_FG_OUT_OFF)
+			mmap_fg_sub(&buf, &buf_len, &prefix, "out off");
+	}
+
+	if (mp->flags & FLODMAP_FG_ROGUE)
+		mmap_fg_sub(&buf, &buf_len, &prefix, "crazy");
+
+	if (mp->flags & FLODMAP_FG_REWINDING)
+		mmap_fg_sub(&buf, &buf_len, &prefix, "rewinding");
+
+	if (mp->flags & FLODMAP_FG_NEED_REWIND)
+		mmap_fg_sub(&buf, &buf_len, &prefix, "need rewind");
+	else if (mp->flags & FLODMAP_FG_FFWD_IN)
+		mmap_fg_sub(&buf, &buf_len, &prefix, "need FFWD");
+
+	if (mp->flags & FLODMAP_FG_PASSIVE)
+		mmap_fg_sub(&buf, &buf_len, &prefix, "passive");
+	if ((mp->flags & FLODMAP_FG_OUT_SRVR)
+	    && !(mp->flags & FLODMAP_FG_PASSIVE))
+		mmap_fg_sub(&buf, &buf_len, &prefix, "forced passive");
+
+	if (!(mp->flags & FLODMAP_FG_IN_SRVR)) {
+		if (mp->flags & FLODMAP_FG_SOCKS)
+			mmap_fg_sub(&buf, &buf_len, &prefix, "SOCKS");
+		if (mp->flags & FLODMAP_FG_NAT)
+			mmap_fg_sub(&buf, &buf_len, &prefix, "NAT");
+	} else {
+		if (mp->flags & FLODMAP_FG_SOCKS)
+			mmap_fg_sub(&buf, &buf_len, &prefix, "rejected SOCKS");
+		if (mp->flags & FLODMAP_FG_NAT)
+			mmap_fg_sub(&buf, &buf_len, &prefix, "rejected NAT");
+	}
+
+	if (mp->flags & FLODMAP_FG_LEAF)
+		mmap_fg_sub(&buf, &buf_len, &prefix, "leaf");
+
+	if (mp->flags & FLODMAP_FG_MAPPED)
+		mmap_fg_sub(&buf, &buf_len, &prefix, "IDs mapped");
+
+	if (mp->flags & FLODMAP_FG_FFWD_IN)
+		mmap_fg_sub(&buf, &buf_len, &prefix, "want fastforward");
+
+	if (mp->flags & FLODMAP_FG_USE_2PASSWD)
+		mmap_fg_sub(&buf, &buf_len, &prefix, "2nd password");
+
+	if (mp->flags & FLODMAP_FG_IPv4)
+		mmap_fg_sub(&buf, &buf_len, &prefix, "IPv4");
+
+	if (mp->flags & FLODMAP_FG_IPv6)
+		mmap_fg_sub(&buf, &buf_len, &prefix, "IPv6");
+
+	return buf0;
+}
+
+
+
+/* this function lets clients such as dbclean know when flooding is quiet */
+int					/* -1=sick, 0=off, 1=not off */
+flod_running(const char *st)
+{
+	char off_buf[11];
+	int out, active, in;
+
+	if (4 != sscanf(st, "flood %10s %d streams %d out active %d in",
+			off_buf, &out, &active, &in))
+		return 1;
+
+	if (strcmp(off_buf, "off"))
+		return 1;
+	return active != 0 || in != 0;
+}
diff -r 000000000000 -r c7f6b056b673 srvrlib/read_rcd.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srvrlib/read_rcd.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,190 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.24 $Revision$
+ */
+
+#include "srvr_defs.h"
+
+static int read_rcd_fd = -1;
+#define BUF_SIZE    (64*1024)
+static u_char read_rcd_buf[BUF_SIZE];
+static u_int read_rcd_bufsize = BUF_SIZE;
+static off_t read_rcd_base;
+static u_int read_rcd_buflen;
+
+static int system_pagesize;
+
+
+
+int
+read_db(DCC_EMSG emsg, void *buf, u_int buf_len,
+	int fd, off_t pos, const char *file_nm)
+{
+	int i;
+
+	if (-1 == lseek(fd, pos, SEEK_SET)) {
+		dcc_pemsg(EX_IOERR, emsg, "lseek(%s, 0): %s",
+			  DB_NM2PATH_ERR(file_nm), ERROR_STR());
+		return -1;
+	}
+
+	i = read(fd, buf, buf_len);
+	if (i >= 0)
+		return i;
+
+	dcc_pemsg(EX_IOERR, emsg, "read(%s): %s",
+		  DB_NM2PATH_ERR(file_nm), ERROR_STR());
+	return -1;
+}
+
+
+
+u_char
+read_db_hdr(DCC_EMSG emsg, DB_HDR *hdrp,
+	   int fd, const char *file_nm)
+{
+	return (sizeof(*hdrp) == read_db(emsg, hdrp, sizeof(*hdrp),
+					  fd, 0, file_nm));
+}
+
+
+
+/* invalidate the read_rcd() read-ahead cache */
+void
+read_rcd_invalidate(u_int bufsize)
+{
+	read_rcd_fd = -1;
+
+	if (system_pagesize == 0)
+		system_pagesize = getpagesize();
+	bufsize = ((bufsize + system_pagesize-1) / system_pagesize
+		   * system_pagesize);
+	if (bufsize == 0 || bufsize > BUF_SIZE)
+		bufsize = BUF_SIZE;
+	read_rcd_bufsize = bufsize;
+}
+
+
+
+static int
+read_rcd_sub(DCC_EMSG emsg, void *buf, u_int buflen,
+	     int fd, off_t pos, const char *file_nm)
+{
+	int i;
+
+	if (fd == read_rcd_fd
+	    && pos >= read_rcd_base
+	    && pos+buflen <= read_rcd_base+read_rcd_buflen) {
+		memcpy(buf, &read_rcd_buf[pos-read_rcd_base], buflen);
+		return buflen;
+	}
+
+	if (system_pagesize == 0)
+		system_pagesize = getpagesize();
+	read_rcd_base = pos - (pos % system_pagesize);
+
+	i = read_db(emsg, read_rcd_buf, read_rcd_bufsize,
+		    read_rcd_fd = fd,
+		    read_rcd_base, file_nm);
+	if (i <= 0) {
+		read_rcd_fd = -1;
+		return i;
+	}
+	read_rcd_buflen = i;
+	if (buflen > read_rcd_buflen)
+		buflen = read_rcd_buflen;
+	memcpy(buf, &read_rcd_buf[pos-read_rcd_base], buflen);
+	return buflen;
+}
+
+
+
+int					/* -1=error 0=eof >0=record length */
+read_rcd(DCC_EMSG emsg, DB_RCD *rcd,
+	 int fd, off_t pos, const char *file_nm)
+{
+	int i, cks_len, num_cks;
+
+	/* read a record, starting with its beginning */
+	i = read_rcd_sub(emsg, rcd, DB_RCD_HDR_LEN,
+			 fd, pos, file_nm);
+	if (DB_RCD_HDR_LEN != i) {
+		if (i == 0)
+			return 0;
+		if (i > 0) {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "header of record at "OFF_HPAT" of %s"
+				  " truncated by %d bytes",
+				  lseek(fd, 0, SEEK_CUR) - i,
+				  DB_NM2PATH_ERR(file_nm), DB_RCD_HDR_LEN-i);
+		}
+		return -1;
+	}
+
+	num_cks = DB_NUM_CKS(rcd);
+	if (num_cks > DIM(rcd->cks)) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "bogus checksum count %#x at "OFF_HPAT" of %s",
+			  rcd->fgs_num_cks, lseek(fd, 0, SEEK_CUR),
+			  DB_NM2PATH_ERR(file_nm));
+		return -1;
+	}
+	cks_len = num_cks * sizeof(rcd->cks[0]);
+	if (cks_len != 0) {
+		i = read_rcd_sub(emsg, rcd->cks, cks_len,
+				 fd, pos+DB_RCD_HDR_LEN, file_nm);
+		if (i != cks_len) {
+			if (i < 0)
+				return -1;
+			if (!i)
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "record at "OFF_HPAT" of %s"
+					  " truncated after header",
+					  lseek(fd, 0, SEEK_CUR),
+					  DB_NM2PATH_ERR(file_nm));
+			else
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "record at "OFF_HPAT" of %s"
+					  " truncated by %d bytes",
+					  lseek(fd, 0, SEEK_CUR),
+					  DB_NM2PATH_ERR(file_nm), cks_len-i);
+			return 0;
+		}
+	}
+
+	return DB_RCD_HDR_LEN+cks_len;
+}
diff -r 000000000000 -r c7f6b056b673 srvrlib/srvr_defs.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srvrlib/srvr_defs.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,260 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * common server definitions
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.85 $Revision$
+ */
+
+#ifndef SRVR_DEFS_H
+#define SRVR_DEFS_H
+
+#include "dcc_clnt.h"
+#include "dcc_ids.h"
+#include "dcc_heap_debug.h"
+#include "db.h"
+
+typedef int64_t   SCNTR;
+
+typedef struct {
+    struct timeval reset;
+    SCNTR   nops;			/* DCC_OP_NOP packets */
+    SCNTR   reports;			/* DCC_OP_REPORT or DCC_OP_REPORT_REP */
+    SCNTR   report_retrans;		/* duplicate reports */
+    SCNTR   report_reject;		/* reports ignored by -Q */
+    SCNTR   report10;			/* reports of >10 targets */
+    SCNTR   report100;			/* reports of >100 targets */
+    SCNTR   report1000;			/* reports of >1000 targets */
+    SCNTR   reportmany;			/* reports of spam */
+    SCNTR   queries;			/* DCC_OP_QUERY */
+    SCNTR   resp10;			/* responses of >10 targets */
+    SCNTR   resp100;			/*   "       " >100 targets */
+    SCNTR   resp1000;			/*   "       " >1000 targets */
+    SCNTR   respmany;			/*   "       " spam */
+    SCNTR   respwhite;			/* whitelisted responses */
+    SCNTR   bad_op;			/* unknown, blacklisted, over active */
+    SCNTR   bad_passwd;			/* requests with bad passwords */
+    SCNTR   blist;			/* blacklisted requests */
+    SCNTR   send_error;			/* error responses sent */
+    SCNTR   admin;			/* DCC_OP_ADMN */
+    SCNTR   rl;				/* responses rate-limited */
+    SCNTR   anon_rl;			/* anonymous responses rate-limited */
+    SCNTR   adds;			/* reports added */
+    SCNTR   iflod_total;		/* total reports received */
+    SCNTR   iflod_accepted;		/* timely and properly signed */
+    SCNTR   iflod_stale;
+    SCNTR   iflod_dup;
+    SCNTR   iflod_wlist;		/* locally whitelisted */
+    SCNTR   iflod_not_deleted;		/* delete commands ignored */
+    SCNTR   norep;
+    SCNTR   rep1;
+    SCNTR   rep10;
+    SCNTR   rep20;
+    SCNTR   rep30;
+    SCNTR   rep60;
+    SCNTR   report_reps;		/* DCC_OP_REPORT_REP packets */
+} DCCD_STATS;
+
+
+typedef char FLOD_EMSG[DCC_FLOD_MAX_RESP];
+typedef struct {
+    int		trace_gen;		/* to get at least one trace message */
+    u_char	complained;
+    FLOD_EMSG	trace_msg;
+    FLOD_EMSG	msg;
+} LAST_ERROR;
+
+
+/* memory mapped file of flooding information
+ *  it is memory mapped so that dblist can report the state of flooding
+ *  and so dbclean can see when flooding has stopped */
+
+#define GREY_FLOD_NM	"grey_flod"
+#define DCCD_FLOD_NM	"flod"
+extern DCC_PATH flod_mmap_path, flod_path;
+
+#ifndef DCCD_MAX_FLOODS
+#define DCCD_MAX_FLOODS	32
+#endif
+
+typedef struct {
+    /* timer and backoff for ordinary connect() */
+    time_t	retry;
+    int		retry_secs;
+    /* timer for complaints about missing incoming connection */
+    time_t	msg;
+    int		msg_secs;
+} CONN_TIMERS;
+typedef u_int32_t FLOD_MMAP_FLAGS;
+typedef struct {
+    char	rem_hostname[DCC_MAXDOMAINLEN];
+    char	rem_portname[MAXPORTNAMELEN+1];
+    u_int16_t	rem_port;
+    DCC_SOCKU	rem_su;			/* address for hostname */
+    int		host_error;		/* for failure to resolve hostname */
+    DB_PTR	confirm_pos;		/* confirmed sent to here */
+    DCC_SRVR_ID	rem_id, in_passwd_id, out_passwd_id;
+    CONN_TIMERS	otimers, itimers;
+    time_t	ids_mtime;		/* mtime ids file when we checked */
+    FLOD_MMAP_FLAGS flags;
+#    define	 FLODMAP_FG_MARK	0x00000001
+#    define	 FLODMAP_FG_IN_OFF	0x00000002
+#    define	 FLODMAP_FG_OUT_OFF	0x00000004
+#    define	 FLODMAP_FG_ROGUE	0x00000008  /* evil server */
+#    define	 FLODMAP_FG_IN_CONN	0x00000010  /* input connected */
+#    define	 FLODMAP_FG_OUT_CONN	0x00000020  /* output connected */
+#    define	 FLODMAP_FG_IPv4	0x00000040  /* override IPv6 choice */
+#    define	 FLODMAP_FG_IPv6	0x00000080  /* override IPv6 choice */
+#    define	 FLODMAP_FG_PASSIVE	0x00000100  /* peer uses SOCKS */
+#    define	 FLODMAP_FG_SOCKS	0x00000200
+#    define	 FLODMAP_FG_NAT		0x00000400  /* SOCKS without library */
+#    define	 FLODMAP_FG_NAT_AUTO	0x00000800  /* assumed NAT */
+#     define	  FLODMAP_FG_ACT (FLODMAP_FG_SOCKS | FLODMAP_FG_NAT	\
+				  | FLODMAP_FG_NAT_AUTO)
+#    define	 FLODMAP_FG_OUT_SRVR	0x00001000  /* connected by peer */
+#    define	 FLODMAP_FG_IN_SRVR	0x00002000  /* connected by peer */
+#    define	 FLODMAP_FG_REWINDING	0x00004000  /* answering rewind */
+#    define	 FLODMAP_FG_NEED_REWIND	0x00008000  /* database purged */
+#    define	 FLODMAP_FG_FFWD_IN	0x00010000  /* want fastforward */
+#    define	 FLODMAP_FG_USE_2PASSWD	0x00020000
+#    define	 FLODMAP_FG_LEAF	0x00040000  /* path length restricted */
+#    define	 FLODMAP_FG_MAPPED	0x00080000  /* server-IDs translated */
+    u_char	iversion;		/* incoming flood protocol */
+    struct {
+	time_t	    cnts_cleared;
+	time_t	    in_conn_changed;
+	time_t	    out_conn_changed;
+	u_int	    out_total_conn;	/* seconds connected */
+	u_int	    in_total_conn;
+	SCNTR	    out_reports;	/* total reports sent */
+	SCNTR	    total;		/*		received */
+	SCNTR	    accepted;
+	SCNTR	    stale;		/* too old or in the future */
+	SCNTR	    dup;		/* already received */
+	SCNTR	    wlist;		/* whitelisted */
+	SCNTR	    not_deleted;	/* delete commands ignored */
+    } cnts;
+    LAST_ERROR	    oflod_err;
+    LAST_ERROR	    iflod_err;
+} FLOD_MMAP;
+
+typedef struct {
+    char	magic[32];
+#    define	 FLOD_MMAP_MAGIC	"DCC flod map version 18"
+    char	pad[32-sizeof(DB_PTR)];
+    DB_PTR	delay_pos;		/* delay flooding newer than this */
+    DB_SN	sn;			/* ensure match with database */
+    FLOD_MMAP	mmaps[DCCD_MAX_FLOODS];
+    DCCD_STATS	dccd_stats;
+} FLOD_MMAPS;
+extern FLOD_MMAPS *flod_mmaps;
+
+
+static inline void
+dcc_secs2ts(DCC_TS *ts, time_t secs)
+{
+	u_int64_t t;
+
+	t = ((u_int64_t)secs) << DCC_TS_SECS_LSHIFT;
+	ts->b[0] = t>>40; ts->b[1] = t>>32;
+	ts->b[2] = t>>24; ts->b[3] = t>>16; ts->b[4] = t>>8; ts->b[5] = t;
+}
+
+static inline void
+dcc_timeval2ts(DCC_TS *ts, const struct timeval *tv, int delta_secs)
+{
+	u_int64_t t;
+
+	t = ((u_int64_t)tv->tv_sec+delta_secs) << DCC_TS_SECS_LSHIFT;
+	t += tv->tv_usec >> DCC_TS_US_RSHIFT;
+	ts->b[0] = t>>40; ts->b[1] = t>>32;
+	ts->b[2] = t>>24; ts->b[3] = t>>16; ts->b[4] = t>>8; ts->b[5] = t;
+}
+
+static inline void
+dcc_ts2timeval(struct timeval *tv, const DCC_TS *ts)
+{
+	u_int64_t t = ((((u_int64_t)ts->b[0])<<40)
+		       + (((u_int64_t)ts->b[1])<<32)
+		       + (((u_int64_t)ts->b[2])<<24)
+		       + (((u_int64_t)ts->b[3])<<16)
+		       + (((u_int64_t)ts->b[4])<<8)
+		       + ts->b[5]);
+	tv->tv_sec = t >> DCC_TS_SECS_LSHIFT;
+	tv->tv_usec = ((t &  DCC_TS_US_MASK) << DCC_TS_US_RSHIFT);
+}
+
+static inline int
+dcc_ts_newer_ts(const DCC_TS *ts1, const DCC_TS *ts2)
+{
+	return memcmp(ts1, ts2, sizeof(DCC_TS)) > 0;
+}
+
+static inline int
+dcc_ts_older_ts(const DCC_TS *ts1, const DCC_TS *ts2)
+{
+	return memcmp(ts1, ts2, sizeof(DCC_TS)) < 0;
+}
+
+
+#define DB_TYPE2STR(t) dcc_type2str_err(t,0,1,grey_on)
+
+/* not thread safe */
+extern DCC_PATH db_path_buf;
+#define DB_NM2PATH_ERR(nm) fnm2abs_err(db_path_buf, nm)
+
+
+extern void flod_mmap_path_set(void);
+extern u_char flod_mmap_sync(DCC_EMSG, u_char);
+extern u_char flod_unmap(DCC_EMSG, const DCCD_STATS *);
+extern u_char flod_mmap(DCC_EMSG, const DB_SN *, const DCCD_STATS *,
+			u_char, u_char);
+extern const char *flod_stats_printf(char *, int, int, int, int, int);
+extern const char *flodmap_fg(char *, int, const char *, const FLOD_MMAP *);
+extern int flod_running(const char *);
+
+extern int read_db(DCC_EMSG, void *, u_int, int, off_t, const char *);
+extern u_char read_db_hdr(DCC_EMSG, DB_HDR *, int fd, const char *);
+extern void read_rcd_invalidate(u_int);
+extern int read_rcd(DCC_EMSG, DB_RCD *, int, off_t, const char *);
+
+extern char *ts2str(char *, u_int, const DCC_TS *);
+extern const char *ts2str_err(const DCC_TS *);
+
+extern char *dcc_srvr_id2str(char *, u_int, DCC_SRVR_ID);
+
+
+#endif /* SRVR_DEFS_H */
diff -r 000000000000 -r c7f6b056b673 srvrlib/ts2str.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srvrlib/ts2str.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,77 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.25 $Revision$
+ */
+
+#include "srvr_defs.h"
+
+
+/* not necessarily thread safe because DCC_GMTIME_R might be ancient */
+char *
+ts2str(char *ts_buf, u_int ts_buf_len, const DCC_TS *ts)
+{
+	struct timeval tv;
+	time_t secs;
+	struct tm tm;
+	char time_buf[30];
+
+	dcc_ts2timeval(&tv, ts);
+	secs = tv.tv_sec;		/* tv_sec is not always a time_t  */
+	DCC_GMTIME_R(&secs, &tm);
+	strftime(time_buf, sizeof(time_buf), "%y/%m/%d %H:%M:%S", &tm);
+	snprintf(ts_buf, ts_buf_len, "%s.%06d",
+		 time_buf, (int)tv.tv_usec);
+	return ts_buf;
+}
+
+
+
+/* this is not thread safe but good enough for error messages */
+const char *
+ts2str_err(const DCC_TS *ts)
+{
+	static int bufno;
+	static struct {
+	    char    str[40];
+	} bufs[4];
+	char *s;
+
+	s = bufs[bufno].str;
+	bufno = (bufno+1) % DIM(bufs);
+
+	return ts2str(s, sizeof(bufs[0].str), ts);
+}
diff -r 000000000000 -r c7f6b056b673 thrlib/.manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thrlib/.manifest	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,7 @@
+Makefile.in
+clnt_threaded.c
+cmn.c
+cmn_defs.h
+reply.c
+totals.c
+.manifest
diff -r 000000000000 -r c7f6b056b673 thrlib/Makefile.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thrlib/Makefile.in	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,52 @@
+# make DCC common threading code
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.16 $Revision$
+# @configure_input@
+
+DEPTH	=..
+LIB	=thr
+SRCS	=clnt_threaded.c cmn.c reply.c totals.c
+
+CFLAGS	+=@PTHREAD_CFLAGS@
+
+install:
+	@:
+
+deinstall:
+	@:
+
+@MAKE_LIB@
diff -r 000000000000 -r c7f6b056b673 thrlib/clnt_threaded.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thrlib/clnt_threaded.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,626 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * threaded version of client locking
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.73 $Revision$
+ */
+
+#include "dcc_ck.h"
+#include "dcc_clnt.h"
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#else
+#include <sys/pthread.h>
+#endif
+#include <signal.h>
+
+u_char grey_on;
+u_char grey_query_only;
+
+DCC_WF cmn_wf, cmn_tmp_wf;
+
+
+/* many POSIX thread implementations have unexpected side effects on
+ * ordinary system calls, so don't use the threaded version unless
+ * necessary */
+
+/* protect the links among contexts and the miscellaneous global
+ * variables in the DCC client library */
+static pthread_mutex_t ctxts_mutex;
+#ifdef DCC_DEBUG_CLNT_LOCK
+static pthread_t ctxts_owner;
+#endif
+
+/* make syslog() thread-safe */
+static pthread_mutex_t syslog_mutex;
+static u_char syslog_threaded;
+
+#ifdef DCC_DEBUG_HEAP
+static pthread_mutex_t malloc_mutex;
+static u_char malloc_threaded;
+#endif
+
+/* make gethostbyname() thread-safe */
+static pthread_mutex_t host_mutex;
+
+static pthread_t clnt_resolve_tid;
+static pthread_cond_t clnt_resolve_cond;
+static u_char clnt_resolve_stopping;
+
+/* The threaded DNS blacklist support uses fork() to create helper processes
+ * to wait for the typical single-threaded DNS resolver library. */
+static pthread_mutex_t helper_mutex;
+
+/* create user logs in a burst while holding a lock
+ *	this reduces the total number of file descriptors needed
+ *	at a cost of stopping everything while copying from the main
+ *	log file to the per-user log files */
+pthread_mutex_t user_log_mutex;
+pthread_t user_log_owner;
+
+
+/* this is used only in the threaded DCC clients */
+void
+clnt_sigs_off(sigset_t *sigsold)
+{
+	sigset_t sigsnew;
+	int error;
+
+	sigemptyset(&sigsnew);
+	sigaddset(&sigsnew, SIGHUP);
+	sigaddset(&sigsnew, SIGINT);
+	sigaddset(&sigsnew, SIGTERM);
+	sigaddset(&sigsnew, SIGALRM);
+	error = pthread_sigmask(SIG_BLOCK, &sigsnew, sigsold);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_sigmask(): %s",
+			   ERROR_STR1(error));
+}
+
+
+
+void
+dcc_ctxts_lock(void)
+{
+	int error;
+
+#ifdef DCC_DEBUG_CLNT_LOCK
+	if (ctxts_owner == pthread_self())
+		dcc_logbad(EX_SOFTWARE, "already have ctxts lock");
+#endif
+
+	error = pthread_mutex_lock(&ctxts_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(ctxts): %s",
+			   ERROR_STR1(error));
+#ifdef DCC_DEBUG_CLNT_LOCK
+	ctxts_owner = pthread_self();
+#endif
+}
+
+
+
+void
+dcc_ctxts_unlock(void)
+{
+	int error;
+
+#ifdef DCC_DEBUG_CLNT_LOCK
+	ctxts_owner = 0;
+#endif
+	error = pthread_mutex_unlock(&ctxts_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(ctxts): %s",
+			   ERROR_STR1(error));
+}
+
+
+
+#ifdef DCC_DEBUG_CLNT_LOCK
+void
+assert_ctxts_locked(void)
+{
+	if (ctxts_owner != pthread_self())
+		dcc_logbad(EX_SOFTWARE, "don't have ctxts lock");
+}
+
+
+
+void
+assert_ctxts_unlocked(void)
+{
+	if (ctxts_owner == pthread_self())
+		dcc_logbad(EX_SOFTWARE, "have ctxts lock");
+}
+#endif
+
+
+
+void
+dcc_syslog_lock(void)
+{
+	int error;
+
+	if (!syslog_threaded)
+		return;
+	error = pthread_mutex_lock(&syslog_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(syslog): %s",
+			   ERROR_STR1(error));
+}
+
+
+
+void
+dcc_syslog_unlock(void)
+{
+	int error;
+
+	if (!syslog_threaded)
+		return;
+	error = pthread_mutex_unlock(&syslog_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(syslog): %s",
+			   ERROR_STR1(error));
+}
+
+
+
+/* gethostbyname() etc. are usually not reentrant */
+u_char dcc_host_locked = 1;
+
+/* do not worry about locking gethostbyname() until the locks have
+ * been initialized */
+static u_char dcc_host_threaded = 0;
+
+/* This function is mentioned in dccifd/dccif-test/dccif-test.c
+ *	and so cannot change lightly. */
+void
+dcc_host_lock(void)
+{
+	int error;
+
+	if (!dcc_host_threaded)
+		return;
+
+	error = pthread_mutex_lock(&host_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(host): %s",
+			   ERROR_STR1(error));
+	dcc_host_locked = 1;
+}
+
+
+
+/* This function is mentioned in dccifd/dccif-test/dccif-test.c
+ *	and so cannot change lightly. */
+void
+dcc_host_unlock(void)
+{
+	int error;
+
+	if (!dcc_host_threaded)
+		return;
+
+	dcc_host_locked = 0;
+	error = pthread_mutex_unlock(&host_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(host): %s",
+			   ERROR_STR1(error));
+}
+
+
+
+#ifdef DCC_DEBUG_HEAP
+void
+dcc_malloc_lock(void)
+{
+	int error;
+
+	if (!malloc_threaded)		/* no locking until locks created */
+		return;
+	error = pthread_mutex_lock(&malloc_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(malloc): %s",
+			   ERROR_STR1(error));
+}
+
+
+void
+dcc_malloc_unlock(void)
+{
+	int error;
+
+	if (!malloc_threaded)		/* no locking until locks created */
+		return;
+	error = pthread_mutex_unlock(&malloc_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(malloc): %s",
+			   ERROR_STR1(error));
+}
+#endif /* DCC_DEBUG_HEAP */
+
+
+
+#ifdef HAVE_LOCALTIME_R
+/* make localtime() thread safe */
+static pthread_mutex_t localtime_mutex;
+static u_char localtime_threaded;
+
+void
+dcc_localtime_lock(void)
+{
+	int error;
+
+	if (!localtime_threaded)
+		return;
+	error = pthread_mutex_lock(&localtime_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(localtime): %s",
+			   ERROR_STR1(error));
+}
+
+
+
+void
+dcc_localtime_unlock(void)
+{
+	int error;
+
+	if (!localtime_threaded)
+		return;
+	error = pthread_mutex_unlock(&localtime_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(localtime): %s",
+			   ERROR_STR1(error));
+}
+#endif /* HAVE_LOCALTIME_R */
+
+
+
+const char *main_white_nm;
+const char *mapfile_nm = DCC_MAP_NM_DEF;
+
+/* resolve things */
+DCC_CLNT_CTXT *
+resolve_sub(DCC_CLNT_CTXT *ctxt,	/* 0=allocate and initialize */
+	    DCC_WF *wf, DCC_WF *tmp_wf)
+{
+	DCC_EMSG emsg;
+
+	if (!ctxt) {
+		dcc_wf_init(wf, 0);
+		if (main_white_nm)
+			dcc_new_white_nm(emsg, wf, main_white_nm);
+
+		emsg[0] = '\0';
+		if (!dcc_map_info(emsg, mapfile_nm, -1))
+			dcc_logbad(EX_USAGE, "%s", emsg);
+
+		ctxt = dcc_alloc_ctxt();
+	}
+
+	if (wf->ascii_nm[0] != '\0') {
+		if (clnt_resolve_stopping)
+			return ctxt;
+		dcc_ctxts_unlock();
+		switch (dcc_rdy_white(emsg, wf, tmp_wf)) {
+		case DCC_WHITE_OK:
+		case DCC_WHITE_NOFILE:
+		case DCC_WHITE_SILENT:
+			break;
+		case DCC_WHITE_CONTINUE:
+		case DCC_WHITE_COMPLAIN:
+			dcc_error_msg("%s", emsg);
+			break;
+		}
+		dcc_ctxts_lock();
+
+		/* Tell the other threads that the hash table
+		 * in the disk file has changed.
+		 * This kludge lets this thread use its own
+		 * wf structure without hogging the lock
+		 * on cmn_wf. */
+		if (wf->closed) {
+			wf->closed = 0;
+			cmn_wf.need_reopen = 1;
+		}
+	}
+
+	return ctxt;
+}
+
+
+
+static void * NRATTRIB
+clnt_resolve_thread(void *arg UATTRIB)
+{
+	DCC_WF wf, tmp_wf;
+	DCC_CLNT_CTXT *ctxt;
+	DCC_EMSG emsg;
+	int error;
+
+	/* let the thread in charge of signals deal with them */
+	clnt_sigs_off(0);
+
+	ctxt = 0;
+	dcc_ctxts_lock();
+	for (;;) {
+		if (clnt_resolve_stopping) {
+			dcc_ctxts_unlock();
+			pthread_exit(0);
+		}
+
+		ctxt = resolve_sub(ctxt, &wf, &tmp_wf);
+
+		if (clnt_resolve_stopping) {
+			dcc_ctxts_unlock();
+			pthread_exit(0);
+		}
+		emsg[0] = '\0';
+		if (!dcc_clnt_rdy(emsg, ctxt, DCC_CLNT_FG_NO_FAIL))
+			dcc_error_msg("%s", emsg);
+		else if (!dcc_info_unlock(emsg))
+			dcc_logbad(dcc_ex_code, "%s", emsg);
+
+		if (grey_on) {
+			if (clnt_resolve_stopping) {
+				dcc_ctxts_unlock();
+				pthread_exit(0);
+			}
+			emsg[0] = '\0';
+			if (!dcc_clnt_rdy(emsg, ctxt, (DCC_CLNT_FG_GREY
+						       | DCC_CLNT_FG_NO_FAIL)))
+				dcc_error_msg("%s", emsg);
+			else if (!dcc_info_unlock(emsg))
+				dcc_logbad(dcc_ex_code, "%s", emsg);
+		}
+
+#ifdef DCC_DEBUG_CLNT_LOCK
+		ctxts_owner = 0;
+#endif
+		error = pthread_cond_wait(&clnt_resolve_cond, &ctxts_mutex);
+		if (error != 0)
+			dcc_logbad(EX_SOFTWARE,
+				   "pthread_cond_wait(resolve): %s",
+				   ERROR_STR1(error));
+#ifdef DCC_DEBUG_CLNT_LOCK
+		ctxts_owner = pthread_self();
+#endif
+	}
+}
+
+
+
+u_char					/* 1=awoke the resolver thread */
+dcc_clnt_wake_resolve(void)
+{
+	int error;
+
+	/* we cannot awaken ourself or awaken the thread before it starts */
+	if (clnt_resolve_tid == 0
+	    || pthread_equal(pthread_self(), clnt_resolve_tid))
+		return 0;
+
+	error = pthread_cond_signal(&clnt_resolve_cond);
+	if (error != 0)
+		dcc_logbad(EX_SOFTWARE, "pthread_cond_signal(resolve): %s",
+			   ERROR_STR1(error));
+	return 1;
+}
+
+
+
+void
+dcc_clnt_stop_resolve(void)
+{
+	if (clnt_resolve_stopping)
+		return;
+	clnt_resolve_stopping = 1;
+	if (pthread_equal(pthread_self(), clnt_resolve_tid))
+		return;
+	pthread_cond_signal(&clnt_resolve_cond);
+}
+
+
+
+/* some pthreads implementations (e.g. AIX) don't like static
+ * initializations */
+static void
+dcc_mutex_init(void *mutex, const char *nm)
+{
+	int error = pthread_mutex_init(mutex, 0);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_init(%s): %s",
+			   nm, ERROR_STR1(error));
+}
+
+
+
+static pthread_mutex_t work_mutex;
+static pthread_t cwf_owner;
+
+void
+lock_work(void)
+{
+	int result = pthread_mutex_lock(&work_mutex);
+	if (result)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(work_free): %s",
+			   ERROR_STR1(result));
+}
+
+
+
+void
+unlock_work(void)
+{
+	int result = pthread_mutex_unlock(&work_mutex);
+	if (result)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(work_free): %s",
+			   ERROR_STR1(result));
+}
+
+
+
+/* lock all CWF structures as well as the cmn_wf structure */
+static pthread_mutex_t cwf_mutex;
+
+void
+lock_wf(void)
+{
+	int error = pthread_mutex_lock(&cwf_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(cwf): %s",
+			   ERROR_STR1(error));
+	cwf_owner = pthread_self();
+}
+
+
+
+void
+unlock_wf(void)
+{
+	int error;
+	cwf_owner = 0;
+	error = pthread_mutex_unlock(&cwf_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(cwf): %s",
+			   ERROR_STR1(error));
+}
+
+
+
+#ifdef DCC_DEBUG_CLNT_LOCK
+void
+assert_cwf_locked(void)
+{
+	if (cwf_owner != pthread_self())
+		dcc_logbad(EX_SOFTWARE, "don't have cwf lock");
+}
+#endif
+
+
+
+void
+dcc_clnt_thread_init(void)
+{
+	DCC_CLNT_CTXT *ctxt;
+	int error;
+
+	/* Some pthreads implementations (e.g. AIX) don't like static
+	 * POSIX thread initializations */
+
+	dcc_mutex_init(&ctxts_mutex, "ctxt");
+	dcc_mutex_init(&syslog_mutex, "syslog");
+	syslog_threaded = 1;
+#ifdef DCC_DEBUG_HEAP
+	dcc_mutex_init(&malloc_mutex, "heap");
+	malloc_threaded = 1;
+#endif
+#ifndef HAVE_LOCALTIME_R
+	dcc_mutex_init(&localtime_mutex, "localtime");
+	localtime_threaded = 1;
+#endif
+	dcc_mutex_init(&host_mutex, "host");
+	dcc_host_threaded = 1;
+	dcc_host_locked = 0;
+
+	dcc_mutex_init(&user_log_mutex, "user_log");
+
+	dcc_mutex_init(&work_mutex, "wf_mutex");
+	dcc_mutex_init(&cwf_mutex, "cwf_mutex");
+
+	/* prevent race between resolver thread and other threads to
+	 * initialize things by doing it before starting the thread */
+	lock_wf();
+	dcc_ctxts_lock();
+	ctxt = resolve_sub(0, &cmn_wf, &cmn_tmp_wf);
+	dcc_rel_ctxt(ctxt);
+	dcc_ctxts_unlock();
+	unlock_wf();
+
+	error = pthread_cond_init(&clnt_resolve_cond, 0);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "phtread_cond_init(resolve): %s",
+			   ERROR_STR1(error));
+	error = pthread_create(&clnt_resolve_tid, 0, clnt_resolve_thread, 0);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_create(resolve): %s",
+			   ERROR_STR1(error));
+	error = pthread_detach(clnt_resolve_tid);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_detach(resolve): %s",
+			   ERROR_STR1(error));
+}
+
+
+
+/* protect DNS blacklist helper channels */
+u_char
+helper_lock_init(void)
+{
+	dcc_mutex_init(&helper_mutex, "helper");
+	return 1;
+}
+
+
+
+void
+helper_lock(void)
+{
+	int error;
+
+	error =  pthread_mutex_lock(&helper_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE,
+			   "pthread_mutex_lock(helper counter): %s",
+			   ERROR_STR1(error));
+}
+
+
+
+void
+helper_unlock(void)
+{
+	int error;
+
+	error = pthread_mutex_unlock(&helper_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE,
+			   "pthread_mutex_unlock(helper counter): %s",
+			   ERROR_STR1(error));
+}
diff -r 000000000000 -r c7f6b056b673 thrlib/cmn.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thrlib/cmn.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,2523 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * threaded version of client library
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.148 $Revision$
+ */
+
+
+#include "cmn_defs.h"
+#include "dcc_paths.h"
+
+CMN_ACTION action = CMN_REJECT;
+
+CHGHDR chghdr = SETHDR;
+
+const char *userdirs;
+static DCC_PATH userdirs_path;
+static int userdirs_len;
+
+u_char dcc_query_only;
+u_char try_extra_hard;			/* 0 or DCC_CLNT_FG_NO_FAIL */
+u_char to_white_only;
+
+u_int dcc_ctxt_sn = 1;			/* change X-DCC header server name */
+
+const char *max_max_work_src = "FD_SETSIZE limit";
+int max_work;
+int init_work;
+int total_work;
+
+static int total_rcpt_sts;
+RCPT_ST *rcpt_st_free;
+
+/* cwf_mutex protects all CWF structures as well as the cmn_wf structure */
+typedef struct cwf {			/* private whitelist state */
+    struct cwf *older, *newer;
+    DCC_WF	wf;
+} CWF;
+static CWF *cur_cwf, cwfs[NUM_CWFS];
+
+/* protected by user_log_mutex */
+static int user_log_fd = -1;
+static int log_fd2 = -1;
+
+
+/* the work lock must be held or not yet exist */
+static void
+add_rcpt_sts(int i)
+{
+	RCPT_ST *rcpt_st;
+
+	total_rcpt_sts += i;
+
+	rcpt_st = dcc_malloc(sizeof(*rcpt_st)*i);
+	memset(rcpt_st, 0, sizeof(*rcpt_st)*i);
+
+	while (i-- != 0) {
+		rcpt_st->fwd = rcpt_st_free;
+		rcpt_st_free = rcpt_st;
+		++rcpt_st;
+	}
+}
+
+
+
+void
+cmn_init(void)
+{
+	init_work = 50;
+	if (init_work > max_max_work)
+		init_work = max_max_work;
+	add_rcpt_sts(init_work);
+
+	finish_replies();
+
+	/* start the client library threads and locks */
+	dcc_clnt_thread_init();
+	for (cur_cwf = cwfs; cur_cwf <= LAST(cwfs); ++cur_cwf) {
+		cur_cwf->newer = cur_cwf-1;
+		cur_cwf->older = cur_cwf+1;
+		dcc_wf_init(&cur_cwf->wf, DCC_WF_PER_USER);
+	}
+	cur_cwf = cwfs;
+	LAST(cwfs)->older = cur_cwf;
+	cur_cwf->newer = LAST(cwfs);
+
+	totals_init();
+}
+
+
+
+void
+cmn_create(CMN_WORK *cwp)
+{
+	cwp->tmp_fd = -1;
+	cwp->log_fd = -1;
+}
+
+
+
+u_char
+cmn_open_tmp(CMN_WORK *cwp)
+{
+	cwp->tmp_fd = dcc_mkstemp(cwp->emsg,
+				  cwp->tmp_nm, sizeof(cwp->tmp_nm),
+				  cwp->id, sizeof(cwp->id),
+				  0, 0, DCC_TMP_LOG_PREFIX, 1, 0);
+
+	return cwp->tmp_fd >= 0;
+}
+
+
+
+void
+cmn_close_tmp(CMN_WORK *cwp)
+{
+	if (cwp->tmp_fd >= 0) {
+		if (0 > close(cwp->tmp_fd))
+			thr_error_msg(cwp, "close(%s): %s",
+				      cwp->tmp_nm, ERROR_STR());
+		cwp->tmp_fd = -1;
+	}
+	cwp->tmp_nm[0] = '\0';
+}
+
+
+
+u_char
+cmn_write_tmp(CMN_WORK *cwp, const void *buf, int len)
+{
+	int i;
+
+	if (cwp->tmp_fd < 0)
+		return 1;
+
+	i = write(cwp->tmp_fd, buf, len);
+	if (i == len)
+		return 1;
+
+	if (i < 0)
+		thr_error_msg(cwp, "write(%s,%d): %s",
+			      cwp->tmp_nm, len, ERROR_STR());
+	else
+		thr_error_msg(cwp, "write(%s,%d)=%d",
+			      cwp->tmp_nm, len, i);
+	cmn_close_tmp(cwp);
+	return 0;
+}
+
+
+
+/* If the immediate SMTP client because it is a listed MX server,
+ *	then we must ignore its IP address and keep looking for the
+ *	real SMTP client. */
+u_char					/* 1=listed MX server */
+check_mx_listing(CMN_WORK *cwp)
+{
+	DCC_TGTS tgts;
+	u_char result;
+
+	lock_wf();
+	result = dcc_white_mx(cwp->emsg, &tgts, &cwp->cks);
+	unlock_wf();
+	if (!result) {
+		thr_error_msg(cwp, "%s", cwp->emsg);
+		return 0;
+	}
+
+	if (tgts == DCC_TGTS_OK) {
+		return 0;
+	}
+
+	if (tgts == DCC_TGTS_SUBMIT_CLIENT) {
+		/* Common SMTP submission clients are too dumb to do the
+		 * right thing with 4yz rejections of individual Rcpt_To
+		 * commands.  So reject the message for all or no recipients. */
+		cwp->cmn_fgs |= CMN_FG_FROM_SUBMIT;
+		thr_log_print(cwp, 1,
+			      "%s is a listed 'submit' client\n",
+			      dcc_trim_ffff(cwp->sender_str));
+		return 0;
+	}
+
+	if (tgts == DCC_TGTS_OK_MXDCC) {
+		thr_log_print(cwp, 1,
+			      "%s is a whitelisted MX server with DCC client\n",
+			      dcc_trim_ffff(cwp->sender_str));
+		cwp->ask_st |= ASK_ST_QUERY;
+	} else if (tgts == DCC_TGTS_OK_MX) {
+		thr_log_print(cwp, 1, "%s is a whitelisted MX server\n",
+			      dcc_trim_ffff(cwp->sender_str));
+	} else {
+		return 0;
+	}
+
+	/* we cannot greylist or reject through our MX servers */
+	cwp->cmn_fgs |= CMN_FG_FROM_MX;
+	if (cwp->action == CMN_REJECT)
+		cwp->action = CMN_DISCARD;
+
+	cwp->sender_name[0] = '\0';
+	cwp->sender_str[0] = '\0';
+	dcc_unget_ip_ck(&cwp->cks);
+
+	/* tell caller to look at the next Received: header */
+	return 1;
+}
+
+
+
+/* clear a common work area for a message, possibly not the first
+ * in the session */
+void
+cmn_clear(CMN_WORK *cwp, struct work *wp,
+	  u_char new_conn)		/* 1=first message for the connection */
+{
+	log_stop(cwp);
+
+	cmn_close_tmp(cwp);
+
+	if (cwp->num_rcpts)
+		free_rcpt_sts(cwp, 1);
+
+	memset(&cwp->CMN_WORK_ZERO, 0,
+	       sizeof(*cwp) - ((char*)&cwp->CMN_WORK_ZERO - (char*)cwp));
+	cwp->cmn_fgs  = 0;
+	cwp->mail_host[0] = '\0';
+	cwp->env_from[0] = '\0';
+	cwp->early_log.len = 0;
+	cwp->emsg[0] = '\0';
+	cwp->id[0] = '\0';
+	cwp->header.used = 0;
+	cwp->cmn_fgs = CMN_FG_LOG_EARLY;
+	if (dcc_query_only)
+		cwp->ask_st |= ASK_ST_QUERY_GREY;
+	cwp->action = action;
+
+	if (new_conn) {
+		cwp->wp = wp;
+		cwp->helo[0] = '\0';
+		cwp->clnt_name[0] = '\0';
+		cwp->clnt_str[0] = '\0';
+	} else {
+		/* assume for now that the sender is the current SMTP client */
+		strcpy(cwp->sender_name, cwp->clnt_name);
+		strcpy(cwp->sender_str, cwp->clnt_str);
+	}
+}
+
+
+
+/* free all of the per-recipient state for a message */
+void
+free_rcpt_sts(CMN_WORK *cwp, u_char need_lock)
+{
+	RCPT_ST *rcpt_st, *next_rcpt_st;
+
+	rcpt_st = cwp->rcpt_st_first;
+	if (!rcpt_st)
+		return;
+
+	if (need_lock)
+		lock_work();
+	cwp->rcpt_st_first = 0;
+	do {
+		next_rcpt_st = rcpt_st->fwd;
+		rcpt_st->fwd = rcpt_st_free;
+		rcpt_st_free = rcpt_st;
+	} while ((rcpt_st = next_rcpt_st) != 0);
+	cwp->num_rcpts = 0;
+
+	if (need_lock)
+		unlock_work();
+}
+
+
+
+RCPT_ST *
+alloc_rcpt_st(CMN_WORK *cwp,
+	   u_char unlocked)		/* 1=unlocked on entry & exit */
+{
+	RCPT_ST *rcpt_st;
+
+	if (cwp->num_rcpts >= MAX_RCPTS) {
+		thr_error_msg(cwp, "too many recipients");
+		return 0;
+	}
+
+	if (unlocked)
+		lock_work();
+	rcpt_st = rcpt_st_free;
+	if (!rcpt_st) {
+		if (dcc_clnt_debug > 1)
+			thr_trace_msg(cwp,
+				      "add %d recipient blocks to %d",
+				      init_work, total_rcpt_sts);
+		add_rcpt_sts(init_work);
+		rcpt_st = rcpt_st_free;
+	}
+	rcpt_st_free = rcpt_st->fwd;
+	if (unlocked)
+		unlock_work();
+
+	rcpt_st->fwd = 0;
+	rcpt_st->log_pos_white = -1;
+	rcpt_st->log_pos_to = -1;
+	memset(rcpt_st->wtgts, 0, sizeof(rcpt_st->wtgts));
+	rcpt_st->env_to_tgts = 0;
+	rcpt_st->user_tgts = 0;
+	rcpt_st->grey_result = ASK_GREY_OFF;
+	rcpt_st->embargo_num = 0;
+	rcpt_st->fgs = 0;
+	rcpt_st->sws = 0;
+	rcpt_st->user[0] = '\0';
+	rcpt_st->rej_msg[0] = '\0';
+	rcpt_st->dir[0] = '\0';
+	rcpt_st->user_log_nm[0] = '\0';
+
+	rcpt_st->cwp = cwp;
+	if (!cwp->rcpt_st_first) {
+		cwp->rcpt_st_first = rcpt_st;
+	} else {
+		cwp->rcpt_st_last->fwd = rcpt_st;
+	}
+	cwp->rcpt_st_last = rcpt_st;
+	++cwp->num_rcpts;
+
+	return rcpt_st;
+}
+
+
+
+void
+parse_userdirs(const char *arg)
+{
+	/* add '/' to end of the path without converting it to "/" */
+	if (*arg == '\0') {
+		userdirs_path[0] = '\0';
+		userdirs_len = 0;
+	} else {
+		strncpy(userdirs_path, arg,
+			sizeof(userdirs_path));
+		userdirs_len = strlen(userdirs_path)-1;
+		while (userdirs_len > 1 && userdirs_path[userdirs_len] == '/')
+			--userdirs_len;
+		userdirs_path[++userdirs_len] = '/';
+		userdirs_path[++userdirs_len] = '\0';
+
+	}
+	userdirs = userdirs_path;
+}
+
+
+
+/* sanitize recipient mailbox and per-user log and whitelist directory */
+u_char					/* 0=complain about something */
+get_user_dir(RCPT_ST *rcpt_st,
+	     const char *str1, int str1_len, const char *str2, int str2_len)
+{
+	char *p;
+	char c;
+	u_char seen_slash;
+	int dots;
+	int i;
+
+	if (!userdirs) {
+		rcpt_st->dir[0] = '\0';
+		return 1;
+	}
+
+	memcpy(rcpt_st->dir, userdirs, userdirs_len);
+	i = userdirs_len;
+	if (i+str1_len < ISZ(rcpt_st->dir))
+		memcpy(&rcpt_st->dir[i], str1, str1_len);
+	i += str1_len;
+	if (str2) {
+		if (i+str2_len+1 < ISZ(rcpt_st->dir)) {
+			rcpt_st->dir[i++] = '/';
+			memcpy(&rcpt_st->dir[i], str2, str2_len);
+		}
+		i += str2_len;
+	}
+	if (i >= ISZ(rcpt_st->dir)) {
+		dcc_pemsg(EX_DATAERR, rcpt_st->cwp->emsg,
+			  "recipient \"%s\" is too long", rcpt_st->dir);
+		rcpt_st->dir[0] = '\0';
+		return 0;
+	}
+	rcpt_st->dir[i] = '\0';
+
+	/* To get a consistent directory name,
+	 * convert ASCII upper to lower case.
+	 * Be simplistic about international character sets and
+	 * avoid locale and portability complications.
+	 * Refuse insecure paths. */
+	seen_slash = 1;			/* userdirs ends with '/' */
+	dots = 0;
+	p = &rcpt_st->dir[userdirs_len];
+	for (;;) {
+		c = *p;
+		if (c == '/' || c == '\\' || c == '\0') {
+			if (dots == 2) {
+				dcc_pemsg(EX_DATAERR, rcpt_st->cwp->emsg,
+					  "path \"%s\" is insecure",
+					  rcpt_st->dir);
+				rcpt_st->dir[0] = '\0';
+				return 0;
+			}
+			if (c == '\0')
+				break;
+			seen_slash = 1;
+			dots = 0;
+		} else if (c == '.' && seen_slash && dots <= 1) {
+			++dots;
+		} else {
+			*p = DCC_TO_LOWER(c);
+			seen_slash = 0;
+			dots = 0;
+		}
+		++p;
+	}
+
+	return 1;
+}
+
+
+
+/* start main log file */
+void
+log_start(CMN_WORK *cwp)
+{
+	char date_buf[40];
+
+	/* don't even whine if there is no log directory */
+	if (dcc_main_logdir[0] == '\0')
+		return;
+
+	/* nothing to do if we already have a log file */
+	if (cwp->log_fd >= 0)
+		return;
+
+	cwp->log_size = 0;
+	cwp->log_fd = dcc_main_log_open(cwp->emsg, cwp->log_nm,
+					cwp->id, sizeof(cwp->id));
+	if (cwp->log_fd < 0) {
+		static time_t whined;
+		time_t now;
+
+		/* complain about not being able to open log files
+		 * only occassionally */
+		now = time(0);
+		if (now < whined || now > whined+5*60 || dcc_clnt_debug)
+			dcc_error_msg("%s", cwp->emsg);
+		whined = now;
+		cwp->emsg[0] = '\0';
+		return;
+	}
+
+	gettimeofday(&cwp->ldate, 0);
+
+	thr_log_print(cwp, 0, DCC_LOG_DATE_PAT"\n",
+		      dcc_time2str(date_buf, sizeof(date_buf), DCC_LOG_DATE_FMT,
+				   cwp->ldate.tv_sec));
+}
+
+
+
+/* get an independent FD for the main log file that can be
+ * repositioned without affecting additional output to the main log. */
+static u_char
+log2_start(CMN_WORK *cwp)
+{
+	DCC_PATH abs_nm;
+
+#ifdef DCC_DEBUG_CLNT_LOCK
+	if (user_log_owner != pthread_self())
+		dcc_logbad(EX_SOFTWARE, "don't have user_log lock");
+#endif
+
+	if (log_fd2 >= 0)
+		return 1;
+
+	/* give up if things are already broken */
+	if (log_fd2 != -1
+	    || cwp->log_fd < 0)
+		return 0;
+
+	/* Some systems don't synchronize the meta data among FDs for
+	 * a file, causing the second FD to appear to be truncated. */
+	if (fsync(cwp->log_fd) < 0)
+		thr_error_msg(cwp, "log_fd fsync(%s): %s",
+			      fnm2abs_err(abs_nm, cwp->log_nm),
+			      ERROR_STR());
+
+	log_fd2 = open(cwp->log_nm, O_RDWR, 0);
+	if (log_fd2 < 0) {
+		thr_error_msg(cwp, "log_fd2 open(%s): %s",
+			      fnm2abs_err(abs_nm, cwp->log_nm),
+			      ERROR_STR());
+		log_fd2 = -2;
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+static void
+log_fd2_close(int flag)
+{
+	if (user_log_owner == pthread_self()) {
+		if (log_fd2 >= 0)
+			close(log_fd2);
+		log_fd2 = flag;
+	}
+}
+
+
+
+void
+log_stop(CMN_WORK *cwp)
+{
+	thr_log_late(cwp);
+	log_fd2_close(-1);
+
+	if (cwp->log_fd < 0)
+		return;
+
+	/* Close before renaming to accomodate WIN32 foolishness.
+	 * Assuming dcc_mkstemp() works properly, there is no race */
+	dcc_log_close(0, cwp->log_nm, cwp->log_fd, &cwp->ldate);
+	if (!(cwp->ask_st & ASK_ST_LOGIT)) {
+		/* Delete the log file if it is not interesting */
+		unlink(cwp->log_nm);
+	} else {
+		/* give it a permanent name if it is interesting */
+		dcc_log_keep(0, cwp->log_nm);
+	}
+	cwp->log_nm[0] = '\0';
+	cwp->log_fd = -1;
+}
+
+
+
+void
+log_write(CMN_WORK *cwp, const void *buf, u_int buflen)
+{
+	int result;
+
+	if (cwp->log_fd < 0)
+		return;
+
+	if (!buflen)
+		buflen = strlen(buf);
+	cwp->log_size += buflen;
+
+	result = write(cwp->log_fd, buf, buflen);
+	if (buflen == (u_int)result)
+		return;
+
+	if (result < 0)
+		dcc_error_msg("write(%s): %s", cwp->log_nm, ERROR_STR());
+	else
+		dcc_error_msg("write(%s,%d)=%d", cwp->log_nm, buflen, result);
+	dcc_log_close(0, cwp->log_nm, cwp->log_fd, &cwp->ldate);
+	cwp->log_fd = -1;
+}
+
+
+
+void
+log_body_write(CMN_WORK *cwp, const char *buf, u_int buflen)
+{
+	int trimlen;
+	const char *p, *lim;
+
+	if (cwp->log_fd < 0)
+		return;
+
+	/* just write if there is room */
+	trimlen = MAX_LOG_KBYTE*1024 - cwp->log_size;
+	if (trimlen >= (int)buflen) {
+		log_write(cwp, buf, buflen);
+		return;
+	}
+
+	/* do nothing if too much already written */
+	if (trimlen < 0)
+		return;
+
+	/* look for and end-of-line near the end of the buffer
+	 * so that we can make the truncation pretty */
+	lim = buf;
+	p = lim+trimlen;
+	if (trimlen > 90)
+		lim += trimlen-90;
+	while (--p > lim) {
+		if (*p == '\n') {
+			trimlen = p-buf+1;
+			break;
+		}
+	}
+	log_write(cwp, buf, trimlen);
+	if (buf[trimlen-1] != '\n')
+		LOG_CMN_EOL(cwp);
+	LOG_CMN_CAPTION(cwp, DCC_LOG_TRN_MSG_CR);
+	cwp->log_size = MAX_LOG_KBYTE*1024+1;
+}
+
+
+
+off_t
+log_lseek_get(CMN_WORK *cwp)
+{
+	off_t result;
+
+	if (cwp->log_fd < 0)
+		return 0;
+	result = lseek(cwp->log_fd, 0, SEEK_END);
+	if (result == -1) {
+		thr_error_msg(cwp, "lseek(%s, 0, SEEK_END): %s",
+			      cwp->log_nm, ERROR_STR());
+		dcc_log_close(0, cwp->log_nm, cwp->log_fd, &cwp->ldate);
+		cwp->log_fd = -1;
+		return 0;
+	}
+	return result;
+}
+
+
+
+static u_char
+log_lseek_set(CMN_WORK *cwp, off_t pos)
+{
+#ifdef DCC_DEBUG_CLNT_LOCK
+	if (user_log_owner != pthread_self())
+		dcc_logbad(EX_SOFTWARE, "don't have user_log lock");
+#endif
+
+	if (log_fd2 < 0)
+		return 0;
+
+	if (-1 == lseek(log_fd2, pos, SEEK_SET)) {
+		thr_error_msg(cwp, "lseek(%s,%d,SEEK_SET): %s",
+			      cwp->log_nm, (int)pos, ERROR_STR());
+		log_fd2_close(-2);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+/* put something into a log file
+ * does not append '\n' */
+static int				/* bytes written */
+vthr_log_print(CMN_WORK *cwp,
+	       u_char error,		/* 1=important enough to buffer */
+	       const char *p, va_list args)
+{
+	char logbuf[LOGBUF_SIZE*2];
+	int i;
+
+	if (cwp->log_fd < 0
+	    || (error && (cwp->cmn_fgs & CMN_FG_LOG_EARLY))) {
+		return dcc_vearly_log(&cwp->early_log, p, args);
+	}
+
+	i = vsnprintf(logbuf, sizeof(logbuf), p, args);
+	if (i < ISZ(logbuf)) {
+		log_write(cwp, logbuf, i);
+		return i;
+	}
+	log_write(cwp, logbuf, sizeof(logbuf));
+	log_write(cwp, "...", 3);
+	return i+3;
+}
+
+
+
+/* does not append '\n' */
+int PATTRIB(3,4)			/* bytes written */
+thr_log_print(void *cwp, u_char error, const char *pat, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, pat);
+	i = vthr_log_print(cwp, error, pat, args);
+	va_end(args);
+	return i;
+}
+
+
+
+int					/* bytes written */
+thr_error_msg(void *cwp0, const char *pat, ...)
+{
+	CMN_WORK *cwp = cwp0;
+	va_list args;
+	int i;
+
+	va_start(args, pat);
+	dcc_verror_msg(pat, args);
+	va_end(args);
+
+	va_start(args, pat);
+	i = vthr_log_print(cwp, 1, pat, args);
+	va_end(args);
+	thr_log_print(cwp, 1, "\n");
+
+	cwp->ask_st |= ASK_ST_LOGIT;
+
+	return i+1;
+}
+
+
+
+void
+thr_trace_msg(void *cwp0, const char *p, ...)
+{
+	va_list args;
+
+	va_start(args, p);
+	dcc_vtrace_msg(p, args);
+	va_end(args);
+
+	if (cwp0) {
+		CMN_WORK *cwp = cwp0;
+		va_start(args, p);
+		vthr_log_print(cwp, 1, p, args);
+		va_end(args);
+		thr_log_print(cwp, 1, "\n");
+
+		cwp->ask_st |= ASK_ST_LOGIT;
+	}
+}
+
+
+
+void
+thr_log_late(CMN_WORK *cwp)
+{
+	cwp->cmn_fgs &= ~CMN_FG_LOG_EARLY;
+	if (cwp->early_log.len) {
+		log_write(cwp, cwp->early_log.buf, cwp->early_log.len);
+		cwp->early_log.len = 0;
+	}
+}
+
+
+
+void
+thr_log_envelope(CMN_WORK *cwp, u_char ip_placekeeper)
+{
+	RCPT_ST *rcpt_st;
+	off_t cur_pos;
+	int i;
+
+	cwp->cmn_fgs |= CMN_FG_ENV_LOGGED;
+
+	/* install the sender in blank area in the log file that we skipped */
+	cwp->log_ip_pos = log_lseek_get(cwp) + LITZ(DCC_XHDR_TYPE_IP": ");
+	if (cwp->sender_str[0] != '\0') {
+		/* Dccm will not have computed the checksum
+		 * if there were no envelope Mail_From commands
+		 * On a second message in a session, dccm will not have
+		 * looked at cwp->clnt_addr */
+		if (cwp->cks.sums[DCC_CK_IP].type == DCC_CK_INVALID)
+			dcc_get_ipv6_ck(&cwp->cks, &cwp->clnt_addr);
+		i = thr_log_print(cwp, 0,
+				  DCC_XHDR_TYPE_IP": %s %s\n",
+				  cwp->sender_name, cwp->sender_str);
+
+	} else if (ip_placekeeper) {
+		/* log a blank, place keeping string for the IP address
+		 * so that it can be inserted later */
+		i = thr_log_print(cwp, 0,
+				  DCC_XHDR_TYPE_IP":  %*s\n",
+				  1+1+1+INET6_ADDRSTRLEN, "");
+	} else {
+		i = 0;
+	}
+	i -= LITZ(DCC_XHDR_TYPE_IP":  \n");
+	cwp->log_ip_len = i > 0 ? i : 0;
+
+	/* log HELO value if we have it
+	 * make checksum of it or of null string if we don't */
+	if (cwp->helo[0] != '\0')
+		thr_log_print(cwp, 0, "HELO: %s\n", cwp->helo);
+	dcc_ck_get_sub(&cwp->cks, "helo", cwp->helo);
+
+	if (cwp->env_from[0] != '\0') {
+		LOG_CMN_CAPTION(cwp, DCC_XHDR_TYPE_ENV_FROM": ");
+		log_write(cwp, cwp->env_from, 0);
+		dcc_get_cks(&cwp->cks, DCC_CK_ENV_FROM, cwp->env_from, 1);
+
+		if (cwp->mail_host[0] == '\0')
+			parse_mail_host(cwp->env_from, cwp->mail_host,
+					sizeof(cwp->mail_host));
+
+		LOG_CMN_CAPTION(cwp, "  mail_host=");
+		log_write(cwp, cwp->mail_host, 0);
+		if (cwp->mail_host[0] != '\0')
+			dcc_ck_get_sub(&cwp->cks, "mail_host", cwp->mail_host);
+		LOG_CMN_EOL(cwp);
+	}
+
+	cwp->log_pos_to_first = cur_pos = log_lseek_get(cwp);
+	for (rcpt_st = cwp->rcpt_st_first; rcpt_st; rcpt_st = rcpt_st->fwd) {
+		rcpt_st->log_pos_to = cur_pos;
+		LOG_CMN_CAPTION(cwp, DCC_XHDR_TYPE_ENV_TO": ");
+		log_write(cwp, rcpt_st->env_to, 0);
+		if (rcpt_st->fgs & RCPT_FG_BAD_USERNAME) {
+			LOG_CMN_CAPTION(cwp, "  "DCC_XHDR_MTA_REJECTION"\n");
+		} else {
+			LOG_CMN_CAPTION(cwp, "  addr=");
+			log_write(cwp, rcpt_st->user, 0);
+			LOG_CMN_CAPTION(cwp, "  dir=");
+			log_write(cwp, rcpt_st->dir, 0);
+			LOG_CMN_EOL(cwp);
+		}
+		cur_pos = log_lseek_get(cwp);
+	}
+	cwp->log_pos_to_end = cur_pos;
+
+	/* log the blank line between the log file header and mail message */
+	LOG_CMN_EOL(cwp);
+}
+
+
+
+/* open the connection to the nearest DCC server */
+u_char
+ck_dcc_ctxt(CMN_WORK *cwp)
+{
+	if (cwp->dcc_ctxt_sn != dcc_ctxt_sn) {
+		cwp->dcc_ctxt_sn = dcc_ctxt_sn;
+		cwp->dcc_ctxt = dcc_clnt_init(cwp->emsg, cwp->dcc_ctxt, 0,
+					      DCC_CLNT_FG_BAD_SRVR_OK
+					      | DCC_CLNT_FG_NO_PICK_SRVR
+					      | DCC_CLNT_FG_NO_FAIL);
+		if (!cwp->dcc_ctxt) {
+			/* failed to create context */
+			thr_error_msg(cwp, "%s", cwp->emsg);
+			cwp->dcc_ctxt_sn = 0;
+			return 0;
+		}
+		cwp->xhdr_fname_len = get_xhdr_fname(cwp->xhdr_fname,
+						     sizeof(cwp->xhdr_fname),
+						     dcc_clnt_info);
+	}
+	return 1;
+}
+
+
+
+/* find and lock a per-user DCC_WF
+ *	it is locked by grabbing the mutex for the main whiteclnt file */
+static CWF *
+find_cwf(RCPT_ST *rcpt_st)
+{
+	CWF *cwf;
+	DCC_PATH white_nm_buf;
+	const char *white_nm_ptr;
+
+	if (rcpt_st->dir[0] == '\0') {
+		rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT;
+		return 0;
+	}
+
+	/* canonicalize the key */
+	if (!fnm2rel(white_nm_buf, rcpt_st->dir, "/whiteclnt")) {
+		thr_error_msg(rcpt_st->cwp,
+			      "long user whiteclnt name \"%s/whiteclnt\"",
+			      rcpt_st->dir);
+		rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT;
+		return 0;
+	}
+	white_nm_ptr = path2fnm(white_nm_buf);
+
+	lock_wf();
+	cwf = cur_cwf;
+	for (;;) {
+		if (!strcmp(white_nm_ptr, cwf->wf.ascii_nm))
+			break;		/* found old DCC_WF for target file */
+
+		if (cwf->older == cur_cwf) {
+			/* We do not know this file.
+			 * Recycle the oldest DCC_WF */
+			if (!dcc_new_white_nm(rcpt_st->cwp->emsg, &cwf->wf,
+					      white_nm_ptr)) {
+				thr_error_msg(rcpt_st->cwp, "%s",
+					      rcpt_st->cwp->emsg);
+				unlock_wf();
+				rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT;
+				return 0;
+			}
+			break;
+		}
+
+		cwf = cwf->older;
+	}
+
+	/* move to front */
+	if (cwf != cur_cwf) {
+		cwf->older->newer = cwf->newer;
+		cwf->newer->older = cwf->older;
+		cwf->older = cur_cwf;
+		cwf->newer = cur_cwf->newer;
+		cwf->newer->older = cwf;
+		cwf->older->newer = cwf;
+		cur_cwf = cwf;
+	}
+
+	switch (dcc_rdy_white(rcpt_st->cwp->emsg, &cwf->wf, &cmn_tmp_wf)) {
+	case DCC_WHITE_CONTINUE:
+		thr_error_msg(rcpt_st->cwp, "%s", rcpt_st->cwp->emsg);
+		/* fall through */
+	case DCC_WHITE_OK:
+		/* notice if the file contains no checksums or CIDR blocks
+		 * or flag bits that make the file differ from an empty
+		 * or non-existent file */
+		if (cwf->wf.wtbl->hdr.entries == 0
+		    && cwf->wf.wtbl->hdr.cidr.len == 0
+		    && !(cwf->wf.wtbl_flags & DCC_WHITE_FG_TRAPS)) {
+			rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT;
+		} else {
+			rcpt_st->fgs &= ~RCPT_FG_NULL_WHITECLNT;
+			memcpy(rcpt_st->wf_sum, cwf->wf.wtbl->hdr.ck_sum,
+			       sizeof(rcpt_st->wf_sum));
+		}
+		return cwf;
+	case DCC_WHITE_NOFILE:
+		break;
+	case DCC_WHITE_SILENT:
+		if (dcc_clnt_debug)
+			thr_error_msg(rcpt_st->cwp, "%s", rcpt_st->cwp->emsg);
+		break;
+	case DCC_WHITE_COMPLAIN:
+		thr_error_msg(rcpt_st->cwp, "%s", rcpt_st->cwp->emsg);
+		break;
+	}
+
+	unlock_wf();
+	rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT;
+	return 0;
+}
+
+
+
+/* digest the results of one recipient's whitelist */
+static void
+white_results(RCPT_ST *rcpt_st,
+	      CMN_WORK *cwp,
+	      RCPT_FGS *fgsp,
+	      DCC_WHITE_RESULT result,
+	      const DCC_WHITE_LISTING *listingp)
+{
+
+	DCC_WHITE_LISTING listing;
+
+	/* call-by-reference parameter to resolve order of evaluation
+	 * in callers */
+	listing = *listingp;
+
+	/* override if the result of the whitelist lookup was bad */
+	switch (result) {
+	case DCC_WHITE_OK:
+		break;
+	case DCC_WHITE_SILENT:
+	case DCC_WHITE_NOFILE:
+		listing = DCC_WHITE_UNLISTED;
+		break;
+	case DCC_WHITE_COMPLAIN:
+	case DCC_WHITE_CONTINUE:
+		thr_error_msg(cwp, "%s", cwp->emsg);
+		return;
+	}
+
+	switch (listing) {
+	case DCC_WHITE_USE_DCC:
+		/* "OK2" for the env_to checksum in the local whitelist
+		 * does not mean the address is half whitelisted,
+		 * but that it is ok to reject or discard spam for it based
+		 * on DCC results.
+		 * It is the original, deprecated mechanism for turn DCC checks
+		 * on and off for individual targets
+		 * We get this value only from dcc_white_sum() */
+		if (rcpt_st)
+			rcpt_st->sws &= ~FLTR_SW_DCC_OFF;
+		/* fall through */
+	case DCC_WHITE_UNLISTED:
+		/* a spam trap rejects everything
+		 * or accepts but marks everything as spam */
+		if (rcpt_st && (rcpt_st->sws & FLTR_SW_TRAPS)) {
+			if (!(*fgsp & RCPT_FG_WHITE))
+				*fgsp |= RCPT_FG_BLACK;
+			/* remember this hit for the log */
+			*fgsp |= RCPT_FG_WLIST_ISSPAM;
+			cwp->rcpt_fgs |= RCPT_FG_WLIST_ISSPAM;
+		}
+		break;
+
+	case DCC_WHITE_LISTED:
+		*fgsp |= RCPT_FG_WHITE;
+		*fgsp &= ~RCPT_FG_BLACK;
+		/* remember this hit for the log */
+		*fgsp |= RCPT_FG_WLIST_NOTSPAM;
+		cwp->rcpt_fgs |= RCPT_FG_WLIST_NOTSPAM;
+		break;
+
+	case DCC_WHITE_BLACK:
+		if (!(*fgsp & RCPT_FG_WHITE))
+			*fgsp |= RCPT_FG_BLACK;
+		/* remember this hit for the log */
+		*fgsp |= RCPT_FG_WLIST_ISSPAM;
+		cwp->rcpt_fgs |= RCPT_FG_WLIST_ISSPAM;
+		break;
+	}
+}
+
+
+
+static void
+rcpt_fgs2ask_st(CMN_WORK *cwp, FLTR_SWS sws, RCPT_FGS fgs)
+{
+	/* We need to know if all targets are whitelisted for the DCC
+	 * before we ask the DCC server.  Mail sent only to whitelisted
+	 * targets should not be reported to the DCC server.
+	 * For that we need a count of whitelisted targets */
+	if (fgs & RCPT_FG_WHITE) {
+		++cwp->white_tgts;
+		return;
+	}
+
+	/* it is spam if it is blacklisted by any target */
+	if (fgs & RCPT_FG_BLACK) {
+		cwp->ask_st |= (ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT);
+		return;
+	}
+
+	/* If we had a DNS blacklist hit
+	 * and if this recipient believes the DNS blacklist,
+	 * then it is spam to report to DCC server.
+	 * We need to know if it is spam for at least one target before
+	 * deciding what to do for each target. */
+	if (0 != (FLTR_SW_DNSBL_BITS(sws) & ASK_ST_DNSBL_HIT_BITS(cwp->ask_st)))
+		cwp->ask_st |= (ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT);
+}
+
+
+
+/* set the global defaults for the switches or options */
+static void
+rcpt_sws_def(CMN_WORK *cwp, u_char locked)
+{
+	if (!locked)
+		lock_wf();
+
+	if (to_white_only)
+		cwp->init_sws |= FLTR_SW_DCC_OFF;
+	cwp->init_sws |= FLTR_SW_NO_DISCARD;
+	cwp->init_sws = wf2sws(cwp->init_sws, &cmn_wf);
+	if (cannot_discard)		/* enforce the default if necessary */
+		cwp->init_sws |= FLTR_SW_NO_DISCARD;
+	cwp->rcpts_sws = cwp->init_sws;
+
+	if (!locked)
+		unlock_wf();
+}
+
+
+
+/* merge global and per-user thresholds */
+static void
+make_tholds(DCC_CKSUM_THOLDS out, CWF *cwf)
+{
+	dcc_merge_tholds(out, dcc_tholds_rej, cmn_wf.wtbl);
+
+	if (cwf && cwf->wf.wtbl)
+		dcc_merge_tholds(out, out, cwf->wf.wtbl);
+}
+
+
+
+/* set per-user switches and compute env_to whitelisting
+ *	If we return a pointer, then we grabbed and have kept the lock */
+static CWF *
+rcpt_sws_env_to(CMN_WORK *cwp, RCPT_ST *rcpt_st)
+{
+	CWF *cwf;
+	DCC_WHITE_LISTING listing;
+
+	/* We are finished after finding the recipient's whitelist if we
+	 * have already checked its settings.
+	 *
+	 * In this case, we have been here before for this recipient
+	 * and fetched the global as well as this recipient's switch settings
+	 * and action.
+	 *
+	 * If we found that the file was empty of checksums or non-existent
+	 * before, then assume it is still is and so repeat the previous 0
+	 * answer without wasting time looking.  This is an extremely
+	 * common case that deserves optimizing.  */
+	if (rcpt_st->fgs & RCPT_FG_NULL_WHITECLNT)
+		return 0;
+
+	/* after mapping the per-recipient whiteclnt file,
+	 * do not repeat the work of examining it if we have already done it */
+	cwf = find_cwf(rcpt_st);
+	if (rcpt_st->sws & FLTR_SW_SET)
+		return cwf;
+
+	/* The first time for the first recipient,
+	 * we must get the global switch settings.
+	 * If we have a per-user whitelist, then we have locked cmn_wf
+	 * to protect the per-user whitelist data structures */
+	if (!(cwp->init_sws & FLTR_SW_SET))
+		rcpt_sws_def(cwp, cwf != 0);
+
+	/* get flags and filter settings for recipient,
+	 * including setting FLTR_SW_SET so that we won't do this again */
+	if (cwf)
+		rcpt_st->sws = wf2sws(cwp->init_sws, &cwf->wf);
+	else
+		rcpt_st->sws = cwp->init_sws;
+	if (cannot_discard)		/* dccifd cannot discard */
+		rcpt_st->sws |= FLTR_SW_NO_DISCARD;
+
+	/* if we have a per-user whitelist, then we have already locked cmn_wf
+	 * to protect the per-user whitelist data structures */
+	if (!cwf)
+		lock_wf();
+
+	/* check the env_to address in the global whitelist */
+	dcc_str2ck(rcpt_st->env_to_sum, 0, 0, rcpt_st->env_to);
+	rcpt_st->global_env_to_fgs = 0;
+	white_results(rcpt_st, cwp, &rcpt_st->global_env_to_fgs,
+		      dcc_white_sum(cwp->emsg, &cmn_wf,
+				    DCC_CK_ENV_TO, rcpt_st->env_to_sum,
+				    &rcpt_st->env_to_tgts, &listing),
+		      &listing);
+	if (listing != DCC_WHITE_UNLISTED)
+		cwp->cmn_fgs |= CMN_FG_LOG_ENV_TO;
+
+	/* check the mailbox name (after aliases etc.) in the global whitelist
+	 * if we did not just check it as the envelope Rcpt_To value */
+	if (rcpt_st->user[0] != '\0'
+	    && strcmp(rcpt_st->env_to, rcpt_st->user)) {
+		dcc_str2ck(rcpt_st->user_sum, 0, 0, rcpt_st->user);
+		white_results(rcpt_st, cwp, &rcpt_st->global_env_to_fgs,
+			      dcc_white_sum(cwp->emsg, &cmn_wf,
+					    DCC_CK_ENV_TO, rcpt_st->user_sum,
+					    &rcpt_st->user_tgts, &listing),
+			      &listing);
+		if (listing != DCC_WHITE_UNLISTED)
+			cwp->cmn_fgs |= CMN_FG_LOG_ENV_TO;
+	}
+	if (!cwf) {
+		unlock_wf();
+
+	} else {
+		/* save per-user envelope Rcpt_To value and mailbox name
+		 * (after aliases etc.) white- or blacklisting
+		 * and the DCC_WHITE_USE_DCC setting */
+		rcpt_st->env_to_fgs = 0;
+		white_results(rcpt_st, cwp, &rcpt_st->env_to_fgs,
+			      dcc_white_sum(cwp->emsg, &cwf->wf,
+					    DCC_CK_ENV_TO, rcpt_st->env_to_sum,
+					    &rcpt_st->env_to_tgts, &listing),
+			      &listing);
+		if (rcpt_st->user[0] != '\0'
+		    && strcmp(rcpt_st->env_to, rcpt_st->user)) {
+			white_results(rcpt_st, cwp, &rcpt_st->env_to_fgs,
+				      dcc_white_sum(cwp->emsg, &cwf->wf,
+						    DCC_CK_ENV_TO,
+						    rcpt_st->user_sum,
+						    &rcpt_st->user_tgts,
+						    &listing),
+				      &listing);
+		}
+	}
+
+	/* remove any reset _ON bits from the consensus */
+	cwp->rcpts_sws &= (rcpt_st->sws | ~FLTR_SWS_SETTINGS_ON);
+	/* add any set _OFF bits to the consensus */
+	cwp->rcpts_sws |= (rcpt_st->sws & FLTR_SWS_SETTINGS_OFF);
+
+	return cwf;
+}
+
+
+
+/* see if a recipient's whitelist decision is certain to be the same
+ * as all preceding recipients */
+u_char					/* 0=must reject this recipient */
+cmn_compat_whitelist(CMN_WORK *cwp,
+		     RCPT_ST *rcpt_st_new)
+{
+	RCPT_ST *rcpt_st2;
+	CWF *cwf;
+	FLTR_SWS save_rcpts_sws;
+	DCC_CKSUM_THOLDS tholds_rej;
+
+	/* everything is compatible if we won't reject
+	 * or if we are forced to reject for all if we reject for any */
+	if ((cwp->action != CMN_REJECT)
+	    || cannot_reject
+	    || (cwp->cmn_fgs & CMN_FG_FROM_SUBMIT))
+		return 1;
+
+	/* postpone poking at whitelists on the first not-rejected recipient */
+	rcpt_st2 = cwp->rcpt_st_first;
+	for (;;) {
+		/* wait until later if this is the first recipient  */
+		if (rcpt_st2 == rcpt_st_new)
+			return 1;
+
+		if (!(rcpt_st2->fgs & (RCPT_FG_REJ_FILTER
+				       | RCPT_FG_BAD_USERNAME)))
+			break;
+		rcpt_st2 = rcpt_st2->fwd;
+		if (!rcpt_st2)
+			return 1;
+	}
+
+	/* we are dealing with a second recipient
+	 * get the settings that we postponed when we saw the first recipient */
+	if (!(rcpt_st2->sws & FLTR_SW_SET)) {
+		cwf = rcpt_sws_env_to(cwp, rcpt_st2);
+		make_tholds(cwp->cks.tholds_rej, cwf);
+		cwp->cmn_fgs |= CMN_FG_THOLDS_SET;
+		if (cwf)
+			unlock_wf();
+	}
+
+	/* See if this message might be accepted for this recipient
+	 * but rejected for some other recipient that does not want
+	 * forced discarding or if this recipient does not want forced
+	 * discarding and has weaker filtering than other recipients.
+	 * If so, reject this recipient. */
+
+	save_rcpts_sws = cwp->rcpts_sws;
+	cwf = rcpt_sws_env_to(cwp, rcpt_st_new);
+	make_tholds(tholds_rej, cwf);
+	if (cwf)
+		unlock_wf();
+
+	/* Differing DCC thresholds conflict */
+	if (memcmp(tholds_rej, cwp->cks.tholds_rej, sizeof(tholds_rej))) {
+		cwp->rcpts_sws = save_rcpts_sws;
+		cwp->ask_st |= ASK_ST_LOGIT;
+		rcpt_st_new->fgs |= RCPT_FG_INCOMPAT_REJ;
+		return 0;
+	}
+
+	/* Accept-traps prefer to avoid all rejections including Mail_From
+	 * commands and implicitly discard but tolerate rejections.
+	 * Reject-traps require rejections. */
+	if (rcpt_st_new->sws & FLTR_SW_TRAP_ACC)
+		return 1;
+
+	/* everything other than differing thesholds is compatible
+	 * if discarding is ok for all recipients so far */
+	if (!(cwp->rcpts_sws & FLTR_SW_NO_DISCARD))
+		return 1;
+
+	do {
+		/* ignore already rejected recipients */
+		if (rcpt_st2->fgs & (RCPT_FG_REJ_FILTER | RCPT_FG_BAD_USERNAME))
+			continue;
+
+		/* Traps are fexible. */
+		if (rcpt_st2->sws & FLTR_SW_TRAP_ACC)
+			continue;
+
+		/* Differing whitelists make it possible that the message
+		 * could need to be rejected for one recipient but accepted
+		 * for the other */
+		if (((rcpt_st2->sws & FLTR_SW_NO_DISCARD)
+		     || (rcpt_st_new->sws & FLTR_SW_NO_DISCARD))
+		    && (((rcpt_st2->fgs ^ rcpt_st_new->fgs)
+			 & RCPT_FG_NULL_WHITECLNT) != 0
+			|| (!(rcpt_st2->fgs & RCPT_FG_NULL_WHITECLNT)
+			    && memcmp(rcpt_st2->wf_sum, rcpt_st_new->wf_sum,
+				      sizeof(rcpt_st2->wf_sum))))) {
+			cwp->rcpts_sws = save_rcpts_sws;
+			cwp->ask_st |= ASK_ST_LOGIT;
+			rcpt_st_new->fgs |= RCPT_FG_INCOMPAT_REJ;
+			return 0;
+		}
+
+		/* Stronger filter choices for a preceding recipient are
+		 * potential reasons to reject the message not shared by the
+		 * new recipient and that could force the message to be
+		 * discarded for the preceding recipient */
+		if ((rcpt_st2->sws & FLTR_SW_NO_DISCARD)
+		    && ((FLTR_SWS_ON(rcpt_st2->sws)
+			 & ~FLTR_SWS_ON(rcpt_st_new->sws)) != 0)) {
+			cwp->rcpts_sws = save_rcpts_sws;
+			cwp->ask_st |= ASK_ST_LOGIT;
+			rcpt_st_new->fgs |= RCPT_FG_INCOMPAT_REJ;
+			return 0;
+		}
+
+		/* weaker filter choices for a preceding recipient imply
+		 * potential reasons to reject the message not shared by the
+		 * preceding recipient and that could force the message to
+		 * be discarded for the new recipient */
+		if ((rcpt_st_new->sws & FLTR_SW_NO_DISCARD)
+		    && ((~FLTR_SWS_ON(rcpt_st2->sws)
+			 & FLTR_SWS_ON(rcpt_st_new->sws)) != 0)) {
+			cwp->rcpts_sws = save_rcpts_sws;
+			cwp->ask_st |= ASK_ST_LOGIT;
+			rcpt_st_new->fgs |= RCPT_FG_INCOMPAT_REJ;
+			return 0;
+		}
+	} while ((rcpt_st2 = rcpt_st2->fwd) != rcpt_st_new);
+
+	return 1;
+}
+
+
+
+/* check the whitelists for a single user or target */
+static void
+ask_white_rcpt(CMN_WORK *cwp,
+	       RCPT_ST *rcpt_st,
+	       RCPT_FGS global_fgs)
+{
+	DCC_WHITE_LISTING listing;
+	CWF *cwf;
+	DCC_PATH abs_nm;
+
+	rcpt_st->log_pos_white = log_lseek_get(cwp);
+
+	/* Quit after capturing the log position if the recipient has
+	 * been rejected.
+	 * We cannot do more because dccm does not have the mailer and so
+	 * cannot find the likely userdirs/local/user/whiteclnt file */
+	if (rcpt_st->fgs & (RCPT_FG_REJ_FILTER | RCPT_FG_BAD_USERNAME))
+		return;
+
+	/* Get the switch settings and the env_to whitelist results. */
+	cwf = rcpt_sws_env_to(cwp, rcpt_st);
+	if (!(cwp->cmn_fgs & CMN_FG_THOLDS_SET)) {
+		cwp->cmn_fgs |= CMN_FG_THOLDS_SET;
+		make_tholds(cwp->cks.tholds_rej, cwf);
+	}
+
+	/* Compute white- or blacklisting.
+	 *	The per-user whiteclnt file overrides the global file.
+	 *	The whiteclnt files override the MTA with "option MTA-first".
+	 *	    Without, the MTA controls.
+	 *	Within each category, whitelisting overrides blacklisting.
+	 *
+	 *	The global whitelist's answer for message's checksums other
+	 *	    than the env_to checksums have already been computed in
+	 *	    global_fgs. */
+
+	if (global_fgs != 0) {
+		rcpt_st->fgs &= ~(RCPT_FG_WHITE | RCPT_FG_BLACK);
+		rcpt_st->fgs |= global_fgs;
+	}
+
+	if (rcpt_st->sws & FLTR_SW_MTA_FIRST) {
+		if (cwp->ask_st & ASK_ST_MTA_NOTSPAM) {
+			rcpt_st->fgs |= RCPT_FG_WHITE;
+			rcpt_st->fgs &= ~RCPT_FG_BLACK;
+		} else if (cwp->ask_st & ASK_ST_MTA_ISSPAM) {
+			rcpt_st->fgs |= RCPT_FG_BLACK;
+			rcpt_st->fgs &= ~RCPT_FG_WHITE;
+		}
+	}
+
+	/* apply the previously computed env_to global whitelist results
+	 * as well as the other global whitelist results */
+	if ((rcpt_st->global_env_to_fgs | global_fgs) & RCPT_FG_WHITE) {
+		rcpt_st->fgs &= ~RCPT_FG_BLACK;
+		rcpt_st->fgs |= RCPT_FG_WHITE;
+	} else if ((rcpt_st->global_env_to_fgs | global_fgs) & RCPT_FG_BLACK) {
+		rcpt_st->fgs &= ~RCPT_FG_WHITE;
+		rcpt_st->fgs |= RCPT_FG_BLACK;
+	}
+
+	if (!cwf) {
+		/* Without a per-user whitelist or without any entries in
+		 * the per-user whitelist, we will be using the
+		 * global whitelist for the other messages checksums.
+		 * Arrange to include those results in the per-user log file */
+		memcpy(rcpt_st->wtgts, cwp->wtgts, sizeof(rcpt_st->wtgts));
+
+	} else {
+		/* Check other message checksums in per-user whitelist */
+		white_results(rcpt_st, cwp, &rcpt_st->env_to_fgs,
+			      dcc_white_cks(cwp->emsg, &cwf->wf, &cwp->cks,
+					    rcpt_st->wtgts, &listing),
+			      &listing);
+
+		if (rcpt_st->env_to_fgs == 0) {
+			/* Without an answer from the per-user whitelist,
+			 * we will be using the global whitelist.
+			 * So arrange to include those results in the per-user
+			 * log file */
+			memcpy(rcpt_st->wtgts, cwp->wtgts,
+			       sizeof(rcpt_st->wtgts));
+
+		} else {
+			thr_log_print(cwp, 0, "%s%s\n",
+				      fnm2abs_err(abs_nm, cwf->wf.ascii_nm),
+				      (rcpt_st->env_to_fgs & RCPT_FG_WHITE)
+				      ? DCC_XHDR_ISOK
+				      : (rcpt_st->sws & FLTR_SW_TRAP_ACC)
+				      ? "-->"DCC_XHDR_TRAP_ACC
+				      : (rcpt_st->sws & FLTR_SW_TRAP_REJ)
+				      ? "-->"DCC_XHDR_TRAP_REJ
+				      : DCC_XHDR_ISSPAM);
+			rcpt_st->fgs &= ~(RCPT_FG_WHITE | RCPT_FG_BLACK);
+			rcpt_st->fgs |= rcpt_st->env_to_fgs;
+		}
+
+		/* release common lock that protected the per-user whitelist
+		 * because we are finished with the per-user whitelist */
+		unlock_wf();
+	}
+
+	if (rcpt_st->env_to_tgts != 0
+	    || rcpt_st->user_tgts != 0)
+		cwp->cmn_fgs |= CMN_FG_LOG_ENV_TO;
+
+	if (!(rcpt_st->sws & FLTR_SW_MTA_FIRST)) {
+		if (cwp->ask_st & ASK_ST_MTA_NOTSPAM) {
+			rcpt_st->fgs |= RCPT_FG_WHITE;
+			rcpt_st->fgs &= ~RCPT_FG_BLACK;
+		} else if (cwp->ask_st & ASK_ST_MTA_ISSPAM) {
+			rcpt_st->fgs |= RCPT_FG_BLACK;
+			rcpt_st->fgs &= ~RCPT_FG_WHITE;
+		}
+	}
+}
+
+
+
+/* check the whitelists for all targets */
+void
+cmn_ask_white(CMN_WORK *cwp)
+{
+	RCPT_ST *rcpt_st;
+	RCPT_FGS global_fgs;
+	DCC_OPS grey_op;
+	DCC_WHITE_LISTING listing;
+
+	/* log sendmail access_db spam */
+	if (cwp->ask_st & ASK_ST_MTA_ISSPAM)
+		cwp->ask_st |= ASK_ST_LOGIT;
+
+	dcc_dnsbl_result(&cwp->ask_st, cwp->cks.dnsbl);
+
+	cwp->log_pos_white_first = log_lseek_get(cwp);
+
+	/* Use the main whitelist only for recipients whose individual
+	 * whitelists don't give a black or white answer.
+	 * Check the main whitelist first (and so even if not necessary)
+	 * so that problems with it are in all of the logs and to simplify
+	 * merging the global and per-user whitelist results. */
+	lock_wf();
+	global_fgs = 0;
+	white_results(0, cwp, &global_fgs,
+		      dcc_white_cks(cwp->emsg, &cmn_wf, &cwp->cks,
+				    cwp->wtgts, &listing),
+		      &listing);
+
+	/* get the defaults for the options */
+	if (!(cwp->init_sws & FLTR_SW_SET))
+		rcpt_sws_def(cwp, 1);
+	unlock_wf();
+
+	/* kludge similar to ask_white_rcpt() for no recipients for dccifd with
+	 * the ASCII protocol */
+	if ((rcpt_st = cwp->rcpt_st_first) == 0) {
+		if (cwp->init_sws & FLTR_SW_MTA_FIRST) {
+			if (cwp->ask_st & ASK_ST_MTA_NOTSPAM) {
+				cwp->rcpt_fgs |= RCPT_FG_WHITE;
+				cwp->rcpt_fgs &= ~RCPT_FG_BLACK;
+			} else if (cwp->ask_st & ASK_ST_MTA_ISSPAM) {
+				cwp->rcpt_fgs |= RCPT_FG_BLACK;
+				cwp->rcpt_fgs &= ~RCPT_FG_WHITE;
+			}
+		}
+		if (global_fgs != 0) {
+			cwp->rcpt_fgs &= ~(RCPT_FG_WHITE | RCPT_FG_BLACK);
+			cwp->rcpt_fgs |= global_fgs;
+		}
+		if (!(cwp->init_sws & FLTR_SW_MTA_FIRST)) {
+			if (cwp->ask_st & ASK_ST_MTA_NOTSPAM) {
+				cwp->rcpt_fgs |= RCPT_FG_WHITE;
+				cwp->rcpt_fgs &= ~RCPT_FG_BLACK;
+			} else if (cwp->ask_st & ASK_ST_MTA_ISSPAM) {
+				cwp->rcpt_fgs |= RCPT_FG_BLACK;
+				cwp->rcpt_fgs &= ~RCPT_FG_WHITE;
+			}
+		}
+
+		rcpt_fgs2ask_st(cwp, cwp->init_sws, cwp->rcpt_fgs);
+	}
+
+	for (; rcpt_st; rcpt_st = rcpt_st->fwd) {
+		/* maybe this recipient is whitelisted or a spam trap
+		 * or has per-user option settings */
+		ask_white_rcpt(cwp, rcpt_st, global_fgs);
+
+		rcpt_fgs2ask_st(cwp, rcpt_st->sws, rcpt_st->fgs);
+
+		/* no greylist check if it is off or should not be done */
+		if ((rcpt_st->sws & (FLTR_SW_GREY_OFF | FLTR_SW_TRAPS))
+		    || (cwp->cmn_fgs & (CMN_FG_FROM_MX | CMN_FG_FROM_SUBMIT))
+		    || (rcpt_st->fgs & (RCPT_FG_REJ_FILTER
+					| RCPT_FG_BAD_USERNAME))
+		    || (cwp->ask_st & ASK_ST_INVALID_MSG))
+			continue;
+
+		if (rcpt_st->fgs & RCPT_FG_BLACK) {
+			if (cwp->cks.sums[DCC_CK_IP].type != DCC_CK_IP
+			    || (cwp->cks.sums[DCC_CK_ENV_FROM].type
+				!= DCC_CK_ENV_FROM))
+				continue;
+			grey_op = DCC_OP_GREY_QUERY;
+		} else if (cwp->ask_st & ASK_ST_QUERY_GREY) {
+			grey_op = DCC_OP_GREY_QUERY;
+		} else if (rcpt_st->fgs & RCPT_FG_WHITE) {
+			grey_op = DCC_OP_GREY_WHITE;
+		} else {
+			grey_op = DCC_OP_GREY_REPORT;
+		}
+		rcpt_st->grey_result = ask_grey(cwp->emsg, cwp->dcc_ctxt,
+						grey_op,
+						rcpt_st->msg_sum,
+						rcpt_st->triple_sum,
+						&cwp->cks,
+						rcpt_st->env_to_sum,
+						&rcpt_st->embargo_num,
+						&cwp->early_grey_tgts,
+						&cwp->late_grey_tgts);
+
+		switch (rcpt_st->grey_result) {
+		case ASK_GREY_OFF:
+		case ASK_GREY_SPAM:
+			dcc_logbad(EX_SOFTWARE,
+				   "cmn_ask_white grey_result=%d",
+				   rcpt_st->grey_result);
+			break;
+		case ASK_GREY_FAIL:
+			thr_error_msg(cwp, "%s", cwp->emsg);
+			/* If we are trying hard, assume the
+			 * message would have been embargoed */
+			if (try_extra_hard)
+				cwp->ask_st |= (ASK_ST_GREY_EMBARGO
+						| ASK_ST_GREY_LOGIT
+						| ASK_ST_LOGIT);
+			break;
+		case ASK_GREY_EMBARGO:
+			if (rcpt_st->embargo_num == 1
+			    && (rcpt_st->fgs & RCPT_FG_BLACK)) {
+				/* don't bother revoking non-existent entry */
+				rcpt_st->grey_result = ASK_GREY_OFF;
+			} else {
+				cwp->ask_st |= (ASK_ST_GREY_EMBARGO
+						| ASK_ST_GREY_LOGIT
+						| ASK_ST_LOGIT);
+				if (cwp->max_embargo_num < rcpt_st->embargo_num)
+				    cwp->max_embargo_num = rcpt_st->embargo_num;
+			}
+			break;
+		case ASK_GREY_EMBARGO_END:
+			cwp->ask_st |= (ASK_ST_GREY_LOGIT | ASK_ST_LOGIT);
+			cwp->rcpt_fgs |= RCPT_FG_GREY_END;
+			break;
+		case ASK_GREY_PASS:
+			break;
+		case ASK_GREY_WHITE:
+			rcpt_st->fgs |= RCPT_FG_GREY_WHITE;
+			break;
+		}
+	}
+
+	cwp->log_pos_white_last = log_lseek_get(cwp);
+	cwp->log_pos_ask_error = cwp->log_pos_white_last;
+}
+
+
+
+/* ask a DCC server */
+int					/* <0=big problem, 0=retryable, 1=ok */
+cmn_ask_dcc(CMN_WORK *cwp)
+{
+	int i;
+
+	/* Talk to the DCC server and make the X-DCC header.
+	 * If we have blacklist entries for it, then we'll tell the DCC
+	 * server it is spam and say so in the X-DCC header.
+	 * Note that a target count of 0 is a query. */
+	if (cwp->ask_st & ASK_ST_QUERY) {
+		cwp->cmn_fgs &= ~CMN_FG_LOCAL_SPAM;
+		cwp->local_tgts = 0;
+	} else if (cwp->ask_st & ASK_ST_CLNT_ISSPAM) {
+		cwp->cmn_fgs |= CMN_FG_LOCAL_SPAM;
+		cwp->local_tgts = cwp->tgts + cwp->mta_rej_tgts;
+	} else if (cwp->ask_st & ASK_ST_GREY_EMBARGO) {
+		/* if the message is under a greylist embargo,
+		 * then report to the DCC only the targets for
+		 * which it is an initial transmission or embargo #1 */
+		cwp->cmn_fgs &= ~CMN_FG_LOCAL_SPAM;
+		cwp->local_tgts = cwp->early_grey_tgts + cwp->mta_rej_tgts;
+	} else {
+		/* if this is the end of a greylist embargo
+		 * then do not tell the DCC about targets that were
+		 * counted with previous transmissions.  Those targets
+		 * are counted in late_grey_tgts this time, but were
+		 * counted in early_grey_tgts for previous transmissions */
+		cwp->cmn_fgs &= ~CMN_FG_LOCAL_SPAM;
+		cwp->local_tgts = (cwp->tgts - cwp->late_grey_tgts
+				   + cwp->mta_rej_tgts);
+	}
+
+	/* talk to the DCC server */
+	i = ask_dcc(cwp->emsg, cwp->dcc_ctxt, try_extra_hard,
+		    &cwp->header, &cwp->cks, &cwp->ask_st,
+		    (cwp->cmn_fgs & CMN_FG_LOCAL_SPAM) != 0,
+		    cwp->local_tgts);
+	if (i <= 0) {
+		cwp->log_pos_ask_error += thr_error_msg(cwp, "%s", cwp->emsg);
+		return i;
+	}
+
+	/* if we are talking to a new server,
+	 * remember to fix the X-DCC headers of the other contexts */
+	if (cwp->xhdr_fname_len != cwp->header.start_len
+	    || strncmp(cwp->header.buf, cwp->xhdr_fname, cwp->xhdr_fname_len)) {
+		if (dcc_clnt_debug > 1)
+			thr_trace_msg(cwp, DCC_XHDR_START
+				      "header changed from %s to %.*s",
+				      cwp->xhdr_fname,
+				      (int)cwp->header.start_len,
+				      cwp->header.buf);
+		cwp->xhdr_fname_len = get_xhdr_fname(cwp->xhdr_fname,
+						     sizeof(cwp->xhdr_fname),
+						     dcc_clnt_info);
+		if (++dcc_ctxt_sn == 0)
+			dcc_ctxt_sn = 1;
+		cwp->dcc_ctxt_sn = dcc_ctxt_sn;
+	}
+
+	return 1;
+}
+
+
+#define USER_LOG_CAPTION(rcpt_st, s) user_log_write((rcpt_st), (s), LITZ(s))
+#define USER_LOG_EOL(rcpt_st) USER_LOG_CAPTION((rcpt_st), "\n")
+
+static u_char
+user_log_write(RCPT_ST *rcpt_st, const void *buf, u_int len)
+{
+	DCC_PATH abs_nm;
+	int result;
+
+	if (user_log_fd < 0)
+		return 0;
+
+	if (!len)
+		len = strlen(buf);
+	result = write(user_log_fd, buf, len);
+	if (result == (int)len)
+		return 1;
+
+	if (result < 0)
+		thr_error_msg(rcpt_st->cwp, "write(%s): %s",
+			      fnm2abs_err(abs_nm, rcpt_st->user_log_nm),
+			      ERROR_STR());
+	else
+		thr_error_msg(rcpt_st->cwp,
+			      "write(%s)=%d instead of %d",
+			      fnm2abs_err(abs_nm, rcpt_st->user_log_nm),
+			      result, (int)len);
+	dcc_log_close(0, rcpt_st->user_log_nm, user_log_fd,
+		      &rcpt_st->cwp->ldate);
+	user_log_fd = -1;
+	return 0;
+}
+
+
+
+static void PATTRIB(2,3)
+user_log_print(RCPT_ST *rcpt_st, const char *p, ...)
+{
+	char logbuf[LOGBUF_SIZE*2];
+	int i;
+	va_list args;
+
+	if (user_log_fd < 0)
+		return;
+
+	va_start(args, p);
+	i = vsnprintf(logbuf, sizeof(logbuf), p, args);
+	va_end(args);
+	if (i < ISZ(logbuf)) {
+		user_log_write(rcpt_st, logbuf, i);
+		return;
+	}
+	user_log_write(rcpt_st, logbuf, sizeof(logbuf));
+	user_log_write(rcpt_st, "...", 3);
+}
+
+
+
+static void
+user_log_block(RCPT_ST *rcpt_st,	/* copy from main log file to this */
+	       off_t start,		/* starting here */
+	       off_t stop)		/* and ending here */
+{
+	CMN_WORK *cwp;
+	char buf[4096];
+	int len;
+	int result;
+
+	if (user_log_fd < 0)
+		return;
+
+	if (start == stop)
+		return;
+
+	cwp = rcpt_st->cwp;
+
+	if (start == -1 || stop == -1 || start > stop) {
+		thr_error_msg(cwp, "bogus user_log_block position %d to %d",
+			      (int)start, (int)stop);
+		return;
+	}
+
+	if (!log_lseek_set(cwp, start))
+		return;
+
+	while ((len = stop - start) != 0) {
+		if (len > ISZ(buf))
+			len = ISZ(buf);
+		result = read(log_fd2, buf, len);
+		if (result != len) {
+			if (result < 0)
+				thr_error_msg(cwp,
+					      "user_log_block read(%s,%d): %s",
+					      cwp->log_nm, len, ERROR_STR());
+			else
+				thr_error_msg(cwp, "user_log_block"
+					      " read(%s,%d)=%d",
+					      cwp->log_nm, len, result);
+			log_fd2_close(-2);
+			return;
+		}
+		if (!user_log_write(rcpt_st, buf, len))
+			return;
+		start += len;
+	}
+}
+
+
+/* print
+ *	      env_To: user@example.com			    ok
+ *  user@example.com: f7a48ff4 70d29d39 4ed1e36f 104e4fa0
+ *		      86e0517b b455b130 4cfccc8c f1a1ff37 Pass
+ */
+static void
+print_addr_sum(LOG_WRITE_FNC out, void *arg,
+	       const char *addr,
+	       int addr_len,		/* trim trailing '>' from grey addr */
+	       const char *sum,
+	       int sum_len,		/* trim trailing '>' from env_To */
+	       const char *tgts,
+	       int tgts_width)		/* to right-justify env_To tgts */
+{
+	char buf[100];
+	int i;
+
+	i = snprintf(buf, sizeof(buf), PRINT_CK_PAT_LIM_CK" %*s\n",
+		     addr_len, addr,
+		     addr_len > 0 ? ':' : ' ',
+		     sum_len, sum,
+		     tgts_width, tgts);
+	if (i >= ISZ(buf)) {
+		i = sizeof(buf);
+		buf[i-1] = '\n';
+	}
+	out(arg, buf, i);
+}
+
+
+
+static void
+print_env_to(LOG_WRITE_FNC out, void *arg, const RCPT_ST *rcpt_st)
+{
+	const char *addr;
+	int addr_len;
+	char tgts_buf[16];
+
+	if (rcpt_st->env_to_tgts != 0) {
+		addr = rcpt_st->env_to;
+		addr_len = strlen(addr);
+		if (addr_len > 1 && addr[0] == '<' && addr[addr_len-1] == '>') {
+			++addr;
+			addr_len -= 2;
+		}
+		print_addr_sum(out, arg,
+			       DCC_XHDR_TYPE_ENV_TO, LITZ(DCC_XHDR_TYPE_ENV_TO),
+			       addr, addr_len,
+			       dcc_tgts2str(tgts_buf, sizeof(tgts_buf),
+					    rcpt_st->env_to_tgts, 0),
+			       PRINT_CK_PAT_SRVR_LEN+PRINT_CK_PAT_WLIST_LEN);
+	}
+
+	if (rcpt_st->user_tgts != 0) {
+		addr = rcpt_st->user;
+		addr_len = strlen(addr);
+		if (addr_len > 1 && addr[0] == '<' && addr[addr_len-1] == '>') {
+			++addr;
+			addr_len -= 2;
+		}
+		print_addr_sum(out, arg,
+			       DCC_XHDR_TYPE_ENV_TO, LITZ(DCC_XHDR_TYPE_ENV_TO),
+			       addr, addr_len,
+			       dcc_tgts2str(tgts_buf, sizeof(tgts_buf),
+					    rcpt_st->user_tgts, 0),
+			       PRINT_CK_PAT_SRVR_LEN+PRINT_CK_PAT_WLIST_LEN);
+	}
+}
+
+
+
+static void
+print_grey(LOG_WRITE_FNC out, void *arg,
+	   const RCPT_ST *rcpt_st, u_char *headed)
+{
+#define CK_HEADING	    "       "DCC_XHDR_GREY_RECIP"\n"
+#define CK_HEADING_QUERY    "       "DCC_XHDR_GREY_RECIP" query\n"
+	char cbuf[DCC_CK2STR_LEN];
+	char embargo_buf[20];
+	const char *addr;
+	int addr_len;
+	const char *embargo;
+
+	embargo = 0;
+	switch (rcpt_st->grey_result) {
+	case ASK_GREY_FAIL:
+		embargo = DCC_XHDR_EMBARGO_FAIL;
+		break;
+	case ASK_GREY_OFF:
+		return;
+	case ASK_GREY_EMBARGO:
+		if (rcpt_st->embargo_num > 0) {
+			snprintf(embargo_buf, sizeof(embargo_buf),
+				 DCC_XHDR_EMBARGO_NUM, rcpt_st->embargo_num);
+			embargo = embargo_buf;
+		} else {
+			embargo = DCC_XHDR_EMBARGO;
+		}
+		break;
+	case ASK_GREY_EMBARGO_END:
+		embargo = DCC_XHDR_EMBARGO_ENDED;
+		break;
+	case ASK_GREY_PASS:
+		embargo = DCC_XHDR_EMBARGO_PASS;
+		break;
+	case ASK_GREY_WHITE:
+		embargo = DCC_XHDR_EMBARGO_OK;
+		break;
+	case ASK_GREY_SPAM:
+		snprintf(embargo_buf, sizeof(embargo_buf),
+			 DCC_XHDR_EMBARGO_RESET, rcpt_st->embargo_num);
+		embargo = embargo_buf;
+		break;
+	}
+
+	if (!headed || !*headed) {
+		if (headed)
+			*headed = 1;
+		if (rcpt_st->cwp->ask_st & ASK_ST_QUERY)
+			out(arg, CK_HEADING_QUERY, LITZ(CK_HEADING_QUERY));
+		else
+			out(arg, CK_HEADING, LITZ(CK_HEADING));
+	}
+
+	addr = rcpt_st->env_to;
+	addr_len = strlen(addr);
+	if (addr_len > 1 && addr[0] == '<' && addr[addr_len-1] == '>') {
+		++addr;
+		addr_len -= 2;
+	}
+	print_addr_sum(out, arg, addr, addr_len,
+		       dcc_ck2str(cbuf, sizeof(cbuf),
+				  DCC_CK_GREY_MSG, rcpt_st->msg_sum, 0),
+		       PRINT_CK_SUM_LEN,
+		       "", 0);
+	print_addr_sum(out, arg, "", 0,
+		       dcc_ck2str(cbuf, sizeof(cbuf),
+				  DCC_CK_GREY3, rcpt_st->triple_sum, 0),
+		       PRINT_CK_SUM_LEN,
+		       embargo, 0);
+
+	if (!headed)
+		out(arg, "\n", 1);
+
+#undef CK_HEADING
+}
+
+
+
+/* log external header, X-DCC header, and DCC results */
+static void
+log_isspam(LOG_WRITE_FNC fnc, void *cp, CMN_WORK *cwp,
+	   u_char log_type,		/* 0="" 1="per-user" 2="global" */
+	   FLTR_SWS sws, RCPT_FGS rcpt_fgs)
+{
+	ASK_ST ask_st;
+
+	ask_st = cwp->ask_st;
+	if (rcpt_fgs & RCPT_FG_WLIST_NOTSPAM)
+		ask_st |= ASK_ST_WLIST_NOTSPAM;
+	if (rcpt_fgs & RCPT_FG_WLIST_ISSPAM)
+		ask_st |= ASK_ST_WLIST_ISSPAM;
+	log_ask_st(fnc, cp, ask_st, sws, log_type, &cwp->header);
+}
+
+
+
+static void
+user_log_msg(CMN_WORK *cwp, RCPT_ST *rcpt_st)
+{
+	DCC_PATH rcpt_logdir;
+	const char *old_log;
+
+	/* since the user wants a log file, make one for the system */
+	cwp->ask_st |= ASK_ST_LOGIT;
+
+	/* we need the main log file to create per-user log files */
+	if (rcpt_st->dir[0] == '\0'
+	    || dcc_main_logdir[0] == '\0')
+		return;
+
+	if (!log2_start(cwp))
+		return;
+
+	/* create the user's log file */
+	snprintf(rcpt_logdir, sizeof(rcpt_logdir), "%s/log", rcpt_st->dir);
+	/* try to use the same name as the main log file */
+	old_log = rindex(cwp->log_nm, '.');
+	if (old_log) {
+		if (strlen(++old_log) != DCC_MKSTEMP_LEN)
+			old_log = 0;
+	}
+	user_log_fd = dcc_log_open(cwp->emsg, rcpt_st->user_log_nm,
+				   cwp->id, sizeof(cwp->id), old_log,
+				   rcpt_logdir, DCC_FIN_LOG_PREFIX,
+				   (rcpt_st->sws & FLTR_SW_LOG_M)
+				   ? LOG_MODE_MINUTE
+				   : (rcpt_st->sws & FLTR_SW_LOG_H)
+				   ? LOG_MODE_HOUR
+				   : (rcpt_st->sws & FLTR_SW_LOG_D)
+				   ? LOG_MODE_DAY
+				   : LOG_MODE_FLAT);
+	if (user_log_fd < 0) {
+		if (user_log_fd == -1)
+			thr_error_msg(cwp, "%s", cwp->emsg);
+		return;
+	}
+
+	user_log_block(rcpt_st,		/* copy envelope before env_To line */
+		       0, cwp->log_pos_to_first);
+	user_log_block(rcpt_st,		/* copy this env_To line */
+		       rcpt_st->log_pos_to,
+		       rcpt_st->fwd
+		       ? rcpt_st->fwd->log_pos_to
+		       : cwp->log_pos_to_end);
+	user_log_block(rcpt_st,		/* copy the body of the message */
+		       cwp->log_pos_to_end,
+		       cwp->log_pos_white_first);
+	user_log_block(rcpt_st,		/* copy whitelist error messages */
+		       rcpt_st->log_pos_white,
+		       rcpt_st->fwd
+		       ? rcpt_st->fwd->log_pos_white
+		       : cwp->log_pos_white_last);
+	user_log_block(rcpt_st,		/* copy DCC error messages */
+		       cwp->log_pos_white_last,
+		       cwp->log_pos_ask_error);
+
+	/* log external header, X-DCC header, and DCC results */
+	log_isspam((LOG_WRITE_FNC)user_log_write, rcpt_st, cwp, 1,
+		   rcpt_st->sws, rcpt_st->fgs);
+
+	/* log the checksums and their counts */
+	dcc_print_cks((LOG_WRITE_FNC)user_log_write, rcpt_st,
+		      cwp->cmn_fgs & CMN_FG_LOCAL_SPAM, cwp->local_tgts,
+		      &cwp->cks, rcpt_st->wtgts,
+		      (cwp->cmn_fgs & CMN_FG_LOG_ENV_TO) != 0);
+	print_env_to((LOG_WRITE_FNC)user_log_write, rcpt_st, rcpt_st);
+	USER_LOG_EOL(rcpt_st);
+	print_grey((LOG_WRITE_FNC)user_log_write, rcpt_st, rcpt_st, 0);
+}
+
+
+
+void
+log_smtp_reply(CMN_WORK *cwp)
+{
+	thr_log_print(cwp, 1, DCC_XHDR_REJ_DATA_MSG);
+	log_write(cwp, cwp->reply.rcode, 0);
+	LOG_CMN_CAPTION(cwp, " ");
+	log_write(cwp, cwp->reply.xcode, 0);
+	LOG_CMN_CAPTION(cwp, " ");
+	log_write(cwp, cwp->reply.str, 0);
+	LOG_CMN_EOL(cwp);
+}
+
+
+
+static void
+user_log_smtp_reply(CMN_WORK *cwp, RCPT_ST *rcpt_st)
+{
+	user_log_print(rcpt_st, DCC_XHDR_REJ_DATA_MSG"%s %s %s\n",
+		       cwp->reply.rcode, cwp->reply.xcode, cwp->reply.str);
+}
+
+
+
+/* tell the grey list to restore the embargo on a triple */
+static void
+grey_spam(CMN_WORK *cwp, RCPT_ST *rcpt_st)
+{
+	DCC_GREY_SPAM gs;
+	DCC_OP_RESP resp;
+
+	switch (rcpt_st->grey_result) {
+	case ASK_GREY_FAIL:
+	case ASK_GREY_OFF:
+	case ASK_GREY_WHITE:
+		return;
+	case ASK_GREY_EMBARGO:
+		if (rcpt_st->embargo_num == 0) {
+			rcpt_st->grey_result = ASK_GREY_OFF;
+			return;
+		}
+		break;
+	case ASK_GREY_EMBARGO_END:
+	case ASK_GREY_PASS:
+		break;
+	case ASK_GREY_SPAM:
+		dcc_logbad(EX_SOFTWARE, "cmn_ask_white grey_result=%d",
+			   rcpt_st->grey_result);
+		return;
+	}
+
+	memset(&gs, 0, sizeof(gs));
+
+	if (cwp->cks.sums[DCC_CK_IP].type != DCC_CK_IP) {
+		thr_error_msg(cwp, "missing IP checksum for dcc_grey_spam()");
+		return;
+	}
+
+	gs.ip.type = DCC_CK_IP;
+	gs.ip.len = sizeof(gs.ip);
+	memcpy(&gs.ip.sum, &cwp->cks.sums[DCC_CK_IP].sum, sizeof(DCC_SUM));
+
+	gs.triple.type = DCC_CK_GREY3;
+	gs.triple.len = sizeof(gs.triple);
+	memcpy(&gs.triple.sum, rcpt_st->triple_sum, sizeof(DCC_SUM));
+
+	gs.msg.type = DCC_CK_GREY_MSG;
+	gs.msg.len = sizeof(gs.msg);
+	memcpy(&gs.msg.sum, rcpt_st->msg_sum, sizeof(DCC_SUM));
+
+	if (!dcc_clnt_op(cwp->emsg, cwp->dcc_ctxt, DCC_CLNT_FG_GREY,
+			   0, 0, 0, &gs.hdr, sizeof(gs),
+			   DCC_OP_GREY_SPAM, &resp, sizeof(resp))) {
+		thr_error_msg(cwp, "%s", cwp->emsg);
+	}
+
+	rcpt_st->grey_result = ASK_GREY_SPAM;
+}
+
+
+
+/* process the message for each user to decide what to do with it */
+void
+users_process(CMN_WORK *cwp)
+{
+	RCPT_ST *rcpt_st;
+	u_char need_eol;
+	int trap_acc_tgts;
+	DNSBL_GBITS dnsbl_sws, dnsbl_hits, common_dnsbl_hits;
+	int dnsbl_delay_tgts;
+	u_char dnsbl_timeo;
+	const DNSBL_GROUP *blg;
+	const REPLY_TPLT *reply;
+	int i;
+
+	/* log the DCC results and headers in the common log file */
+	log_isspam((LOG_WRITE_FNC)log_write, cwp, cwp, 2,
+		   cwp->rcpts_sws, cwp->rcpt_fgs);
+
+	/* log the checksums, DCC server counts and global whitelist values */
+	dcc_print_cks((LOG_WRITE_FNC)log_write, cwp,
+		      cwp->cmn_fgs & CMN_FG_LOCAL_SPAM, cwp->local_tgts,
+		      &cwp->cks, cwp->wtgts,
+		      (cwp->cmn_fgs & CMN_FG_LOG_ENV_TO) != 0);
+	if (cwp->cmn_fgs & CMN_FG_LOG_ENV_TO) {
+		for (rcpt_st = cwp->rcpt_st_first;
+		     rcpt_st;
+		     rcpt_st = rcpt_st->fwd) {
+			print_env_to((LOG_WRITE_FNC)log_write, cwp, rcpt_st);
+		}
+	}
+	LOG_CMN_EOL(cwp);
+
+	/* mark recipients who won't receive it */
+	need_eol = 0;
+	trap_acc_tgts = 0;
+	common_dnsbl_hits = -1;
+	dnsbl_delay_tgts = 0;
+	for (rcpt_st = cwp->rcpt_st_first;
+	     rcpt_st;
+	     rcpt_st = rcpt_st->fwd) {
+		/* Ignore recipients whose RCPT_TO commands were rejected */
+		if (rcpt_st->fgs & RCPT_FG_REJ_FILTER)
+			continue;
+
+		/* We cannot decide whether the message might be spam
+		 * for targets rejected by the MTA because dccm does not
+		 * have the mailer and so cannot find the likely
+		 * userdirs/local/user/whiteclnt file */
+		if (rcpt_st->fgs & RCPT_FG_BAD_USERNAME)
+			continue;
+
+		if (rcpt_st->fgs & RCPT_FG_WHITE) {
+			common_dnsbl_hits = 0;
+			dnsbl_timeo = 0;
+		} else {
+			/* decide whether it is spam for this target */
+			if ((rcpt_st->fgs & RCPT_FG_BLACK)
+			    || ((cwp->ask_st & ASK_ST_SRVR_ISSPAM)
+				&& !(rcpt_st->sws & FLTR_SW_DCC_OFF)))
+				rcpt_st->fgs |= RCPT_FG_ISSPAM;
+
+			dnsbl_sws = FLTR_SW_DNSBL_BITS(rcpt_st->sws);
+			dnsbl_hits = (dnsbl_sws
+				      & ASK_ST_DNSBL_HIT_BITS(cwp->ask_st));
+			common_dnsbl_hits &= dnsbl_hits;
+			if (dnsbl_hits != 0) {
+				dnsbl_timeo = 0;
+				rcpt_st->fgs |= RCPT_FG_ISSPAM;
+			} else if (0 != (ASK_ST_DNSBL_TFAIL_BITS(cwp->ask_st)
+					 & dnsbl_sws)) {
+				dnsbl_timeo = 1;
+			} else {
+				dnsbl_timeo = 0;
+			}
+		}
+
+		if (rcpt_st->fgs & RCPT_FG_ISSPAM) {
+			if (rcpt_st->sws & FLTR_SW_TRAP_ACC)
+				++trap_acc_tgts;
+			else
+				++cwp->reject_tgts;
+		} else if (dnsbl_timeo) {
+			++dnsbl_delay_tgts;
+		} else {
+			++cwp->deliver_tgts;
+		}
+
+		/* Tell greylist to restore the embargo for targets that believe
+		 * the message was spam and did not white- or blacklist it */
+		if ((rcpt_st->fgs & RCPT_FG_ISSPAM)
+		    && !(rcpt_st->sws & FLTR_SW_TRAPS))
+			grey_spam(cwp, rcpt_st);
+
+		print_grey((LOG_WRITE_FNC)log_write, cwp, rcpt_st, &need_eol);
+	}
+	if (need_eol)
+		LOG_CMN_EOL(cwp);
+
+	/* If real targets or rejection-traps need to reject the message,
+	 * then treat any accept-traps as reject-traps to avoid telling the
+	 * SMTP client that the message was accepted. */
+	if (cwp->reject_tgts != 0 && cwp->deliver_tgts == 0) {
+		cwp->reject_tgts += trap_acc_tgts;
+	} else {
+		cwp->deliver_tgts += trap_acc_tgts;
+	}
+
+	/* temp-fail ambiguous mail if DNSBL checks timed out */
+	if (dnsbl_delay_tgts != 0) {
+		if (cwp->deliver_tgts != 0) {
+			/* It is not ambiguous if it must be delivered to
+			 * at least one recipient. */
+			cwp->deliver_tgts += dnsbl_delay_tgts;
+			dnsbl_delay_tgts = 0;
+		} else {
+			cwp->reject_tgts += dnsbl_delay_tgts;
+		}
+	}
+
+	if (cwp->reject_tgts != 0 && cwp->deliver_tgts != 0) {
+		/* It is spam for some targets and not for others.
+		 * If we cannot discard, then reject it for all targets */
+		if ((cwp->cmn_fgs & CMN_FG_FROM_SUBMIT)
+		    || cannot_discard) {
+			thr_log_print(cwp, 0,
+				      "rejection forced for %d targets\n",
+				      cwp->deliver_tgts);
+			cwp->reject_tgts += cwp->deliver_tgts;
+			cwp->deliver_tgts = 0;
+		} else {
+			thr_log_print(cwp, 0,
+				      "discard forced for %d targets\n",
+				      cwp->reject_tgts);
+		}
+	}
+
+	/* do not embargo the message if no target wants it */
+	if (cwp->deliver_tgts == 0)
+		cwp->ask_st &= ~ASK_ST_GREY_EMBARGO;
+
+	if (cwp->ask_st & ASK_ST_GREY_EMBARGO) {
+		make_reply(&cwp->reply, &grey_reply, cwp, 0);
+		return;
+	}
+
+	/*  finished if it is not spam or we won't do anything about it */
+	if (cwp->reject_tgts == 0 || cwp->action == CMN_IGNORE)
+		return;
+
+	/* make an SMTP rejection message unless we have already have one */
+	if (cwp->reply.log_result)
+		return;
+
+	/* if possible, use a DNSBL rejection message that applies
+	 * to all recipients */
+	if (common_dnsbl_hits != 0) {
+		for (i = 0; i < MAX_DNSBL_GROUPS; ++i) {
+			if ((common_dnsbl_hits & DNSBL_G2B(i)) != 0
+			    && 0 != (blg = &cwp->cks.dnsbl->groups[i])
+			    && 0 != (reply = blg->dnsbl->reply)) {
+				make_reply(&cwp->reply, reply, cwp, blg);
+				return;
+			}
+		}
+	}
+
+	if (dnsbl_delay_tgts != 0) {
+		make_reply(&cwp->reply, &dnsbl_timeo_reply, cwp, 0);
+		return;
+	}
+
+	/* use the generic DCC rejection message */
+	make_reply(&cwp->reply, &reject_reply, cwp, 0);
+}
+
+
+
+/* after having checked each user or recipient,
+ *	dispose of the message for each */
+static void
+user_log_result(CMN_WORK *cwp, RCPT_ST *rcpt_st, const char *result)
+{
+	/* create the per-user log file */
+	if ((rcpt_st->fgs & RCPT_FG_ISSPAM)
+	    || ((cwp->ask_st & ASK_ST_GREY_LOGIT)
+		&& !(rcpt_st->sws & FLTR_SW_GREY_LOG_OFF))
+	    || (rcpt_st->sws & FLTR_SW_LOG_ALL))
+		user_log_msg(cwp, rcpt_st);
+
+	if (cwp->ask_st & ASK_ST_INVALID_MSG) {
+		user_log_print(rcpt_st, DCC_XHDR_RESULT"%s\n", result);
+		return;
+	}
+
+	if (rcpt_st->rej_msg[0] != '\0') {
+		/* rejection result for this recipient in the main log */
+		thr_log_print(cwp, 0,
+			      DCC_XHDR_RESULT_REJECT" %s: %s\n",
+			      rcpt_st->env_to, rcpt_st->rej_result);
+		/* per-user log file */
+		if (!(rcpt_st->fgs & RCPT_FG_INCOMPAT_REJ)
+		    || (rcpt_st->sws & FLTR_SW_LOG_ALL)) {
+			USER_LOG_CAPTION(rcpt_st, DCC_XHDR_REJ_RCPT_MSG);
+			user_log_write(rcpt_st, rcpt_st->rej_msg, 0);
+			if (rcpt_st->rej_msg[0] == '4')
+				USER_LOG_CAPTION(rcpt_st,
+						 "\n"DCC_XHDR_RESULT
+						 DCC_XHDR_RESULT_REJECT
+						 " temporarily\n");
+			else
+				USER_LOG_CAPTION(rcpt_st,
+						 "\n"DCC_XHDR_RESULT
+						 DCC_XHDR_RESULT_REJECT);
+		}
+		return;
+	}
+
+	if (cwp->ask_st & ASK_ST_GREY_EMBARGO) {
+		user_log_smtp_reply(cwp, rcpt_st);
+		if (rcpt_st->embargo_num != 0) {
+			user_log_print(rcpt_st,
+				       DCC_XHDR_RESULT"%s #%d\n",
+				       cwp->reply.log_result,
+				       rcpt_st->embargo_num);
+		} else {
+			user_log_print(rcpt_st, DCC_XHDR_RESULT"%s\n",
+				       cwp->reply.log_result);
+		}
+		return;
+	}
+
+	if (!(rcpt_st->fgs & RCPT_FG_ISSPAM)) {
+		/* It is not spam for this target.
+		 *
+		 * If it was rejected late, e.g. by the SMTP server for dccifd
+		 * in proxy, mode, then log the rejection message */
+		if (result) {
+			if (cwp->reply.str && cwp->reply.str[0] != '\0')
+				user_log_print(rcpt_st,
+					       DCC_XHDR_REJ_DATA_MSG"%s\n",
+					       cwp->reply.str);
+			user_log_print(rcpt_st, DCC_XHDR_RESULT"%s\n",
+				       result);
+			return;
+		}
+
+		/* If it was spam for some other target and we cannot
+		 * discard for that target, then log the rejection */
+		if (cwp->reject_tgts != 0
+		    && ((cwp->cmn_fgs & CMN_FG_FROM_SUBMIT)
+			|| cannot_discard)) {
+			user_log_smtp_reply(cwp, rcpt_st);
+			USER_LOG_CAPTION(rcpt_st,
+					 DCC_XHDR_RESULT
+					 DCC_XHDR_RESULT_REJECT
+					 DCC_XHDR_RESULT_FORCED"\n");
+			return;
+		}
+
+		if (cwp->ask_st & (ASK_ST_CLNT_ISSPAM
+				   | ASK_ST_SRVR_ISSPAM
+				   | ASK_ST_REP_ISSPAM)) {
+			if (cwp->rcpt_fgs & RCPT_FG_GREY_END)
+				USER_LOG_CAPTION(rcpt_st,
+						 DCC_XHDR_RESULT
+						 DCC_XHDR_RESULT_I_A
+						 " "DCC_XHDR_RESULT_A_GREY"\n");
+			else
+				USER_LOG_CAPTION(rcpt_st,
+						 DCC_XHDR_RESULT
+						 DCC_XHDR_RESULT_I_A"\n");
+			return;
+		}
+		if (cwp->rcpt_fgs & RCPT_FG_GREY_END) {
+			USER_LOG_CAPTION(rcpt_st,
+					 DCC_XHDR_RESULT
+					 DCC_XHDR_RESULT_ACCEPT
+					 " "DCC_XHDR_RESULT_A_GREY"\n");
+			return;
+		}
+		if (rcpt_st->fgs & RCPT_FG_GREY_WHITE) {
+			USER_LOG_CAPTION(rcpt_st,
+					 DCC_XHDR_RESULT
+					 DCC_XHDR_RESULT_ACCEPT
+					 ";  greylist whitelist\n");
+			return;
+		}
+		USER_LOG_CAPTION(rcpt_st,
+				 DCC_XHDR_RESULT DCC_XHDR_RESULT_ACCEPT"\n");
+		return;
+	}
+
+	/* It was spam for this target
+	 *
+	 * If some other target wanted the message, then we should discard it
+	 * for this target.  Dccifd in proxy mode cannot discard for an
+	 * individual target, and so must avoid the possibility by
+	 * recipients that might have differing choices with cannot_discard. */
+	if (cwp->deliver_tgts != 0) {
+		/* Prefer to discard for spam traps, but just accept
+		 * spam when we cannot discard it for dccifd */
+		if (rcpt_st->sws & FLTR_SW_TRAPS) {
+			if (!cannot_discard) {
+				user_reject_discard(cwp, rcpt_st);
+				USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT
+						 DCC_XHDR_RESULT_DISCARD"\n");
+			} else {
+				USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT
+						 DCC_XHDR_RESULT_ACCEPT"\n");
+			}
+
+		} else {
+			--cwp->reject_tgts;
+			++totals.tgts_discarded;
+			user_reject_discard(cwp, rcpt_st);
+			if (cwp->action == CMN_DISCARD) {
+				USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT
+						 DCC_XHDR_RESULT_DISCARD"\n");
+			} else {
+				thr_log_print(cwp, 0,
+					      DCC_XHDR_RESULT
+					      DCC_XHDR_RESULT_DISCARD
+					      " forced for %s\n",
+					      rcpt_st->env_to);
+				USER_LOG_CAPTION(rcpt_st,
+						 DCC_XHDR_RESULT
+						 DCC_XHDR_RESULT_DISCARD
+						 DCC_XHDR_RESULT_FORCED"\n");
+			}
+		}
+		return;
+	}
+
+	/* spam for all targets including this one */
+
+	if (cwp->action == CMN_DISCARD) {
+		USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT
+				 DCC_XHDR_RESULT_DISCARD"\n");
+		return;
+	}
+
+	if (cwp->action == CMN_IGNORE) {
+		USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT
+				 DCC_XHDR_RESULT_I_A"\n");
+		return;
+	}
+
+	user_log_smtp_reply(cwp, rcpt_st);
+	if (rcpt_st->sws & FLTR_SW_TRAP_ACC) {
+		user_log_print(rcpt_st, DCC_XHDR_RESULT
+			       "%s"DCC_XHDR_RESULT_FORCED"\n",
+			       cwp->reply.log_result);
+	} else {
+		user_log_print(rcpt_st, DCC_XHDR_RESULT"%s\n",
+			       cwp->reply.log_result);
+	}
+}
+
+
+
+/* log the consensus in each target's log file */
+void
+users_log_result(CMN_WORK *cwp,
+		 const char *result)	/* 0 or reject by dccifd's server */
+{
+	RCPT_ST *rcpt_st;
+	int error;
+
+	error = pthread_mutex_lock(&user_log_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(user_log): %s",
+			   ERROR_STR1(error));
+	user_log_owner = pthread_self();
+
+	/* create individual log files and trim target list */
+	for (rcpt_st = cwp->rcpt_st_first; rcpt_st; rcpt_st = rcpt_st->fwd) {
+		if (rcpt_st->fgs & RCPT_FG_BAD_USERNAME)
+			continue;
+		if ((cwp->ask_st & ASK_ST_INVALID_MSG)
+		    && !(rcpt_st->sws & FLTR_SW_LOG_ALL))
+			continue;
+
+		user_log_result(cwp, rcpt_st, result);
+
+		if (user_log_fd >= 0) {
+			dcc_log_close(0, rcpt_st->user_log_nm,
+				      user_log_fd, &cwp->ldate);
+			user_log_fd = -1;
+		}
+	}
+
+	log_fd2_close(-1);
+
+	user_log_owner = 0;
+	error = pthread_mutex_unlock(&user_log_mutex);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(user_log): %s",
+			   ERROR_STR1(error));
+}
diff -r 000000000000 -r c7f6b056b673 thrlib/cmn_defs.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thrlib/cmn_defs.h	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,297 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * common threaded client definitions
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.88 $Revision$
+ */
+
+#ifndef CLNT_DEFS_H
+#define CLNT_DEFS_H
+
+#include "dcc_ck.h"
+#include "dcc_xhdr.h"
+#include "dcc_heap_debug.h"
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#else
+#include <sys/pthread.h>
+#endif
+#include <sys/un.h>
+#include <sys/resource.h>
+#include <arpa/inet.h>
+#include <signal.h>
+
+
+/* what to do about checksums whose counts say "spam" */
+typedef enum {
+    CMN_REJECT = 0,			/* tell sendmail to reject spam */
+    CMN_DISCARD,			/* discard spam */
+    CMN_IGNORE				/* ignore spam reports */
+} CMN_ACTION;
+extern CMN_ACTION action;
+
+typedef enum {
+    SETHDR, ADDHDR, NOHDR
+} CHGHDR;
+extern CHGHDR chghdr;
+
+
+typedef struct {
+    int	    msgs;			/* total messages */
+    int	    tgts;			/* total addressees */
+    int	    tgts_discarded;		/* discarded for this many addressess */
+    int	    tgts_rejected;
+    int	    tgts_ignored;
+    int	    tgts_embargoed;
+    int	    msgs_embargoed;
+    int	    msgs_spam;
+    time_t  msg_prev, msg_next;
+} TOTALS;
+extern TOTALS totals;
+
+
+/* This is a wild guess of open files hidden in libraries and elsewhere.
+ * Some systems such as Solaris seem to have an amazing number of them
+ * Each whitelist context involves an open hash table file */
+#define EXTRA_FILES	(32 + NUM_CWFS)
+
+#define MAX_SELECT_WORK ((FD_SETSIZE-EXTRA_FILES)/FILES_PER_JOB)
+/*  dccm and dccifd have differing values for FILES_PER_JOB */
+#define MIN_MAX_WORK    2
+extern int max_max_work;
+extern const char *max_max_work_src;
+extern int max_work;
+extern int init_work;
+extern int total_work;
+
+
+typedef struct {
+    const char *log_result;		/* "reject" etc. for log */
+    const char *rcode;
+    const char *xcode;
+    const char *str;
+    char    str_buf[REPLY_BUF_LEN];
+} REPLY_STRS;
+#define DCC_RCODE   "550"
+#define DCC_XCODE   "5.7.1"
+extern REPLY_TPLT reject_reply;
+extern REPLY_TPLT grey_reply;
+extern REPLY_TPLT reputation_reply;
+extern REPLY_TPLT dcc_fail_reply;
+extern REPLY_TPLT dnsbl_timeo_reply;
+
+
+/* Some of these flags are computed for all recipients (e.g. white- or
+ *	blacklisting from the main whiteclnt file) in the common flags and
+ *	then copied to individual recipient flags words.
+ *	Others are kept only in the common word. */
+typedef u_short RCPT_FGS;
+#    define	 RCPT_FG_NULL_WHITECLNT	0x0001	/* no entries in it */
+#    define	 RCPT_FG_REJ_FILTER	0x0002	/* rejected by dccifd or dccm */
+#    define	 RCPT_FG_BAD_USERNAME	0x0004	/* user bad & rejected by MTA */
+#    define	 RCPT_FG_WLIST_ISSPAM	0x0008	/* whiteclnt blacklisted */
+#    define	 RCPT_FG_WLIST_NOTSPAM	0x0010	/* whiteclnt whitelisted */
+#    define	 RCPT_FG_WHITE		0x0020
+#    define	 RCPT_FG_BLACK		0x0040
+#    define	 RCPT_FG_ISSPAM		0x0080	/* result for this target */
+#    define	 RCPT_FG_GREY_END	0x0100	/* end of greylist embargo */
+#    define	 RCPT_FG_GREY_WHITE	0x0200	/* whitelisted for greylist */
+#    define	 RCPT_FG_INCOMPAT_REJ	0x0400  /* incompatible & so rejected */
+
+/* per-recipient state */
+typedef struct rcpt_st {
+    struct rcpt_st  *fwd;
+    struct cmn_work *cwp;
+    off_t	    log_pos_to;		/* env_To line in main log file */
+    off_t	    log_pos_white;
+    DCC_CKS_WTGTS   wtgts;
+    DCC_TGTS	    env_to_tgts, user_tgts;
+    ASK_GREY_RESULT grey_result;
+    u_int	    embargo_num;
+    DCC_SUM	    wf_sum;
+    DCC_CKSUM_THOLDS tholds_rej;
+    FLTR_SWS	    sws;
+    RCPT_FGS	    fgs;
+    RCPT_FGS	    global_env_to_fgs;
+    RCPT_FGS	    env_to_fgs;
+#    define	     RCTP_MAXNAME 257	/* sendmail MAXNAME limit */
+    char	    env_to[RCTP_MAXNAME];   /* env_to */
+    char	    user[RCTP_MAXNAME];	/* mailbox */
+    char	    rej_msg[REPLY_BUF_LEN];
+    const char	    *rej_result;
+    DCC_SUM	    env_to_sum;
+    DCC_SUM	    user_sum;
+    DCC_SUM	    msg_sum;
+    DCC_SUM	    triple_sum;
+    DCC_PATH	    dir;		/* recipient's whitelist and logdir */
+    DCC_PATH	    user_log_nm;
+} RCPT_ST;
+
+
+/* per message state common to threaded DCC clients */
+typedef struct cmn_work {
+    struct work	*wp;
+    DCC_CLNT_CTXT *dcc_ctxt;
+    u_int	dcc_ctxt_sn;
+    struct timeval ldate;
+    CMN_ACTION	action;
+    u_int	xhdr_fname_len;
+    char	xhdr_fname[sizeof(DCC_XHDR_START)+sizeof(DCC_BRAND)+1];
+    char	clnt_name[DCC_MAXDOMAINLEN];	/* SMTP client */
+    char	clnt_str[INET6_ADDRSTRLEN+1];
+    struct in6_addr clnt_addr;
+    char	sender_name[DCC_MAXDOMAINLEN];	/* source of mail message */
+    char	sender_str[INET6_ADDRSTRLEN+1];
+    char	helo[DCC_HELO_MAX];
+    char	env_from[DCC_ENV_FROM_MAX+1];
+    char	mail_host[DCC_MAXDOMAINLEN];  /* Mail_From host name */
+    char	id[DCC_MSG_ID_LEN+1];
+    DCC_EMSG	emsg;
+    DCC_PATH    tmp_nm;
+    DCC_PATH	log_nm;			/* log file for this message */
+    int		num_rcpts;
+#    define	 MAX_RCPTS 1024
+    RCPT_ST	*rcpt_st_first, *rcpt_st_last;
+    int		log_fd;			/* -1=none */
+    int		tmp_fd;			/* copy of entire message */
+    DCC_HEADER_BUF header;
+    EARLY_LOG	early_log;
+
+    u_short	cmn_fgs;
+#    define	 CMN_FG_ENV_LOGGED  0x0001  /* have logged the envelope */
+#    define	 CMN_FG_LOG_EARLY   0x0002  /* too early to write to log file */
+#    define	 CMN_FG_CHECK_REP   0x0004  /* check DCC reputations */
+#    define	 CMN_FG_FROM_MX	    0x0008  /* don't reject MX secondary */
+#    define	 CMN_FG_FROM_SUBMIT 0x0010  /* SMTP submission clients */
+#    define	 CMN_FG_LOCAL_SPAM  0x0020
+#    define	 CMN_FG_LOG_ENV_TO  0x0040  /* env_to checksum to log */
+#    define	 CMN_FG_THOLDS_SET  0x0080
+
+    DCC_GOT_CKS	cks;
+
+#define CMN_WORK_ZERO log_ip_pos	/* here down cleared for each msg */
+
+    off_t	log_ip_pos;		/* position and length of IP: line */
+    int		log_ip_len;
+    off_t	log_pos_to_first;	/* first env_To line in log file */
+    off_t	log_pos_to_end;		/* end of env_To lines in log file */
+    off_t	log_pos_white_first;	/* first whitelist result */
+    off_t	log_pos_white_last;	/* last whitelist result */
+    off_t	log_pos_ask_error;	/* final DCC errors */
+    u_int	max_embargo_num;
+    REPLY_STRS	reply;
+    DCC_TGTS	tgts;			/* total accepted Mail_From values */
+    DCC_TGTS	white_tgts;		/* # of ->tgts whitelisting message */
+    DCC_TGTS	reject_tgts;		/* # of ->tgts rejecting message */
+    DCC_TGTS	deliver_tgts;		/* # of ->tgts wanting the message */
+    DCC_TGTS	mta_rej_tgts;		/* not accepted by order of MTA */
+    DCC_TGTS	early_grey_tgts;	/* report to DCC if embargoed */
+    DCC_TGTS	late_grey_tgts;		/* don't report to DCC if delivered */
+    DCC_TGTS	local_tgts;		/* what we told the DCC server */
+    size_t	log_size;
+    DCC_CKS_WTGTS wtgts;
+    FLTR_SWS	init_sws;		/* initial value for rcpt_st->sws */
+    FLTR_SWS	rcpts_sws;		/* common among recipients */
+    RCPT_FGS	rcpt_fgs;
+    ASK_ST	ask_st;			/* ASK_ST_* */
+} CMN_WORK;
+
+
+extern u_int dcc_ctxt_sn;		/* change X-DCC header server name */
+
+extern RCPT_ST *rcpt_st_free;
+
+const char *userdirs;
+
+extern u_char dcc_query_only;
+extern u_char cannot_discard;		/* cannot trim targets after DATA */
+extern u_char cannot_reject;		/* cannot reject RCPT_TO targets */
+extern u_char try_extra_hard;		/* 0 or DCC_CLNT_FG_NO_FAIL */
+extern u_char to_white_only;
+extern const char *mapfile_nm;
+extern const char *main_white_nm;
+
+extern pthread_mutex_t user_log_mutex;
+extern pthread_t user_log_owner;
+
+extern void clnt_sigs_off(sigset_t *);
+
+extern void parse_userdirs(const char *);
+extern u_char get_user_dir(RCPT_ST *, const char *, int, const char *, int);
+extern void make_tplt(REPLY_TPLT *, u_char,
+		      const char *, const char *, const char *, const char *);
+extern void parse_reply_arg(const char *);
+extern void make_reply(REPLY_STRS *, const REPLY_TPLT *,
+		       const CMN_WORK *, const DNSBL_GROUP *);
+extern void finish_replies(void);
+extern void cmn_init(void);
+extern void cmn_create(CMN_WORK *);
+extern u_char cmn_open_tmp(CMN_WORK *);
+extern void cmn_close_tmp(CMN_WORK *);
+extern u_char cmn_write_tmp(CMN_WORK *cwp, const void *, int);
+extern u_char check_mx_listing(CMN_WORK *);
+extern void cmn_clear(CMN_WORK *, struct work *, u_char);
+extern void free_rcpt_sts(CMN_WORK *, u_char);
+extern RCPT_ST *alloc_rcpt_st(CMN_WORK *, u_char);
+extern void log_start(CMN_WORK *);
+extern void log_stop(CMN_WORK *);
+extern void log_write(CMN_WORK *, const void *, u_int);
+extern void log_body_write(CMN_WORK *, const char *, u_int);
+#define LOG_CMN_CAPTION(cwp, s) log_write(cwp, s, LITZ(s))
+#define LOG_CMN_EOL(cwp) LOG_CMN_CAPTION(cwp, "\n")
+#define LOG_CAPTION(wp, s) LOG_CMN_CAPTION(&(wp)->cw, s)
+#define LOG_EOL(wp) LOG_CAPTION(wp, "\n")
+extern off_t log_lseek_get(CMN_WORK *);
+extern void thr_log_late(CMN_WORK *);
+extern void thr_log_envelope(CMN_WORK *, u_char);
+extern u_char ck_dcc_ctxt(CMN_WORK *);
+extern u_char cmn_compat_whitelist(CMN_WORK *, RCPT_ST *);
+extern void cmn_ask_white(CMN_WORK *);
+extern int cmn_ask_dcc(CMN_WORK *);
+extern void users_process(CMN_WORK *);
+extern void users_log_result(CMN_WORK *, const char *);
+
+extern void user_reject_discard(CMN_WORK *, RCPT_ST *);
+extern void log_smtp_reply(CMN_WORK *);
+
+extern void totals_init(void);
+extern void totals_stop(void);
+extern void totals_msg(void);
+extern void work_clean(void);
+
+
+#endif /* CLNT_DEFS_H */
diff -r 000000000000 -r c7f6b056b673 thrlib/reply.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thrlib/reply.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,375 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * SMTP reply strings
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.30 $Revision$
+ */
+
+
+#include "cmn_defs.h"
+
+REPLY_TPLT reject_reply;
+REPLY_TPLT reputation_reply;
+REPLY_TPLT grey_reply;
+REPLY_TPLT dnsbl_timeo_reply;
+
+
+/* default SMTP message templates */
+typedef struct {
+    REPLY_TPLT *reply;
+    const char *rcode;			/* default value such as "550" */
+    const char *xcode;			/* default value such as "5.7.1" */
+    const char *def_pat;		/* default -r pattern */
+    const char *log_result;		/* "accept ..." etc. for log */
+} REPLY_DEF;
+
+#define	DEF_REJ_MSG "mail %ID from %CIP rejected by DCC"
+static REPLY_DEF rej_def = {&reject_reply, DCC_RCODE, DCC_XCODE,
+	DEF_REJ_MSG,
+	"reject"};
+
+static REPLY_DEF grey_def = {&grey_reply, "452", "4.2.1",
+	"mail %ID from %CIP temporary greylist embargoed",
+	"temporary greylist embargo"};
+
+static REPLY_DEF rep_def = {&reputation_reply, DCC_RCODE, DCC_XCODE,
+	"%ID bad reputation; see http://commercial-dcc.rhyolite.com/cgi-bin/reps.cgi?tgt=%CIP",
+	"reject by DCC reputation"};
+
+static REPLY_DEF dnsbl_def = {0, DCC_RCODE, DCC_XCODE,
+	DEF_REJ_MSG,
+	"reject"};
+
+static REPLY_DEF dnsbl_timeo_def = {&dnsbl_timeo_reply, "452", "4.2.1",
+	"mail %ID from %CIP temporary delayed for DNSBL",
+	"DNSBL check delay"};
+
+
+REPLY_TPLT dcc_fail_reply = {"temporary reject", {REPLY_TPLT_NULL},
+	"451", "4.4.0", 0, DCC_XHDR_RESULT_DCC_FAIL};
+
+
+/* Parse a string into RFC 821 and RFC 2034 codes and a pattern to make a
+ * message, or generate the codes for a string that has a message
+ * without codes.
+ * The string should be something like "5.7.1 550 spammer go home"
+ * or "spammer go home". */
+void
+make_tplt(REPLY_TPLT *tplt,		/* to here */
+	  u_char mode,			/* 0=sendmail 1=dccid 2=dnsbl */
+	  const char *xcode,		/* default value such as "5.7.1" */
+	  const char *rcode,		/* default value such as "550" */
+	  const char *arg,		/* using this pattern */
+	  const char *log_result)	/* "reject" etc. for log */
+{
+	const char *p;
+	char c, *new_pat;
+	char sb[5+1+3+1];
+	int p_cnt, i;
+
+	arg += strspn(arg, DCC_WHITESPACE"\"");
+
+	/* Does the message have a valid xcode and rcode?
+	 * First check for sendmail.cf ERROR:### and ERROR:D.S.N:### */
+	if (mode == 0 && !CLITCMP(arg, "ERROR:")) {
+		p = dcc_parse_word(0, sb, sizeof(sb), arg+LITZ("ERROR:"),
+				   0, 0, 0);
+		if (p) {
+			i = strlen(sb);
+			if (i == 5+1+3 && sb[5] == ':') {
+				memcpy(tplt->xcode, &sb[0], 5);
+				memcpy(tplt->rcode, &sb[6], 3);
+			} else if (i == 3) {
+				BUFCPY(tplt->xcode, xcode);
+				memcpy(tplt->rcode, &sb[0], 3);
+			} else {
+				p = 0;
+			}
+		}
+	} else {
+		p = dcc_parse_word(0, tplt->xcode, sizeof(tplt->xcode),
+				   arg, 0, 0, 0);
+		if (p)
+			p = dcc_parse_word(0, tplt->rcode, sizeof(tplt->rcode),
+					   p, 0, 0, 0);
+	}
+	if (!p
+	    || tplt->rcode[0] < '4' || tplt->rcode[0] > '5'
+	    || tplt->rcode[1] < '0' || tplt->rcode[1] > '9'
+	    || tplt->rcode[2] < '0' || tplt->rcode[2] > '9'
+	    || tplt->rcode[0] != tplt->xcode[0]
+	    || tplt->xcode[1] != '.'
+	    || tplt->xcode[2] < '0' || tplt->xcode[2] > '9'
+	    || tplt->xcode[3] != '.'
+	    || tplt->xcode[4] < '0' || tplt->xcode[4] > '9') {
+		BUFCPY(tplt->xcode, xcode);
+		BUFCPY(tplt->rcode, rcode);
+		p = arg;
+	} else {
+		p += strspn(p, DCC_WHITESPACE);
+	}
+
+	tplt->is_pat = 0;
+	p_cnt = 0;
+	new_pat = tplt->pat;
+	do {
+		c = *p++;
+		if (c == '\0')
+			break;
+		if (c == '%') {
+			/* parse %x for x=
+			 *  ID	    message queue ID
+			 *  CIP	    SMTP client IP address
+			 *  EMBARGO null or #%d of greylist embargo number
+			 *  BTYPE   type of blacklist hit
+			 *  BTGT    IP address or name sought in DNSBL
+			 *  BPROBE  domain name found in blacklist
+			 *  BRESULT value of domain name found in blacklist
+			 *
+			 *  BT	    obsolete equivalent of BTYPE
+			 *  BIP	    obsolete equivalent of BTGT
+			 *  s	    obsolete; 1st same as ID; 2nd same as CIP
+			 */
+			if (new_pat >= LAST(tplt->pat)-2)
+				continue;   /* no room for "%s\0" */
+
+			tplt->is_pat = 1;
+			*new_pat++ = '%';
+			if (mode == 0
+			    || p_cnt >= NUM_REPLY_TPLT_ARGS || *p == '%') {
+				/* translate %% and bad % to %% */
+				++p;
+
+			} else if (!LITCMP(p,"ID")) {
+					p += LITZ("ID");
+				tplt->args[p_cnt++] = REPLY_TPLT_ID;
+				c = 's';
+			} else if (!LITCMP(p,"CIP")) {
+				p += LITZ("CIP");
+				tplt->args[p_cnt++] = REPLY_TPLT_CIP;
+				c = 's';
+			} else if (mode == 2 && !LITCMP(p,"BTYPE")) {
+				p += LITZ("BTYPE");
+				tplt->args[p_cnt++] = REPLY_TPLT_BTYPE;
+				c = 's';
+			} else if (mode == 2 && !LITCMP(p,"BTGT")) {
+				p += LITZ("BTGT");
+				tplt->args[p_cnt++] = REPLY_TPLT_BTGT;
+				c = 's';
+			} else if (mode == 2 && !LITCMP(p,"BPROBE")) {
+				p += LITZ("BPROBE");
+				tplt->args[p_cnt++] = REPLY_TPLT_BPROBE;
+				c = 's';
+			} else if (mode == 2 && !LITCMP(p,"BRESULT")) {
+				p += LITZ("BRESULT");
+				tplt->args[p_cnt++] = REPLY_TPLT_BRESULT;
+				c = 's';
+
+			} else if (*p == 's' && p_cnt < 2) {
+				/* obsolete
+				 * translate 1st "%s" to REPLY_TPLT_ID
+				 * 2nd to REPLY_TPLT_CIP */
+				++p;
+				tplt->args[p_cnt] = (p_cnt == 0
+						     ? REPLY_TPLT_ID
+						     : REPLY_TPLT_CIP);
+				++p_cnt;
+				c = 's';
+			} else if (mode == 2 && !LITCMP(p,"BIP")) {
+				/* obsolete */
+				p += LITZ("BIP");
+				tplt->args[p_cnt++] = REPLY_TPLT_BTGT;
+				c = 's';
+			} else if (mode == 2 && !LITCMP(p,"BT")) {
+				/* obsolete */
+				p += LITZ("BT");
+				tplt->args[p_cnt++] = REPLY_TPLT_BTYPE;
+				c = 's';
+			}
+
+		} else if (c == '"' && mode == 0) {
+			/* Strip double quotes from sendmail rejection
+			 * message. Sendmail needs quotes around the message
+			 * so it won't convert blanks to dots. */
+			continue;
+
+		} else if (c < ' ' || c > '~') {
+			/* sendmail does not like control characters in
+			 * SMTP status messages */
+			c = '.';
+		}
+
+		*new_pat++ = c;
+	} while (new_pat < LAST(tplt->pat));
+	*new_pat = '\0';
+	while (p_cnt < NUM_REPLY_TPLT_ARGS)
+		tplt->args[p_cnt++] = REPLY_TPLT_NULL;
+
+	tplt->log_result = log_result;
+}
+
+
+
+static inline void
+finish_reply(const REPLY_DEF *def, const char *pat)
+{
+	make_tplt(def->reply, 1, def->xcode, def->rcode, pat,
+		  def->log_result);
+}
+
+
+
+void
+finish_replies(void)
+{
+	/* make SMTP status strings for cases not set with -rPATTERN */
+	if (reject_reply.rcode[0] == '\0')
+		finish_reply(&rej_def, rej_def.def_pat);
+	if (reputation_reply.rcode[0] == '\0')
+		finish_reply(&rep_def, rep_def.def_pat);
+	if (grey_reply.rcode[0] == '\0')
+		finish_reply(&grey_def, grey_def.def_pat);
+	if (dnsbl_timeo_reply.rcode[0] == '\0')
+		finish_reply(&dnsbl_timeo_def, dnsbl_timeo_def.def_pat);
+}
+
+
+
+/* parse another "-r pattern" argument */
+void
+parse_reply_arg(const char *arg)
+{
+	/* a blank string implies the default */
+	arg += strspn(arg, DCC_WHITESPACE);
+
+	/* each -r finishes the next SMTP rejection message */
+
+	if (reject_reply.rcode[0] == '\0') {
+		if (*arg == '\0')
+			finish_reply(&rej_def, rej_def.def_pat);
+		else
+			finish_reply(&rej_def, arg);
+		return;
+	}
+
+	if (grey_reply.rcode[0] == '\0') {
+		if (*arg == '\0') {
+			finish_reply(&grey_def, grey_def.def_pat);
+			return;
+		}
+		finish_reply(&grey_def, arg);
+		if (grey_reply.rcode[0] != '4'
+		    || grey_reply.xcode[0] != '4') {
+			dcc_error_msg("invalid greylist message: %s", arg);
+			finish_reply(&grey_def, grey_def.def_pat);
+		}
+		return;
+	}
+
+	if (reputation_reply.rcode[0] == '\0') {
+		if (*arg == '\0')
+			finish_reply(&rep_def, rep_def.def_pat);
+		else
+			finish_reply(&rep_def, arg);
+		return;
+	}
+
+	dcc_error_msg("more than 3 -r settings");
+}
+
+
+
+/* set up the DNSBL SMTP error message template for all threads based on
+ *	a -B arg.  Allow % patterns and so forth */
+const REPLY_TPLT *
+dnsbl_parse_reply(const char *pat)
+{
+	REPLY_TPLT *tplt;
+
+	tplt = malloc(sizeof(REPLY_TPLT));
+	memset(tplt, 0, sizeof(REPLY_TPLT));
+	make_tplt(tplt, 2, dnsbl_def.xcode, dnsbl_def.rcode, pat,
+		  dnsbl_def.log_result);
+	return tplt;
+}
+
+
+
+void
+make_reply(REPLY_STRS *strs, const REPLY_TPLT *tplt,
+	   const CMN_WORK *cwp, const DNSBL_GROUP *blg)
+{
+	const char *args[NUM_REPLY_TPLT_ARGS];
+	int i;
+
+	strs->rcode = tplt->rcode;
+	strs->xcode = tplt->xcode;
+	if (!tplt->is_pat) {
+		strs->str = tplt->pat;
+	} else {
+		for (i = 0; i < NUM_REPLY_TPLT_ARGS; ++i) {
+			switch (tplt->args[i]) {
+			case REPLY_TPLT_NULL:
+				args[i] = "";
+				break;
+			case REPLY_TPLT_ID:
+				args[i] = cwp->id;
+				break;
+			case REPLY_TPLT_CIP:
+				args[i] = dcc_trim_ffff(cwp->clnt_str);
+				break;
+			case REPLY_TPLT_BTYPE:
+				args[i] = (blg && blg->btype) ? blg->btype : "";
+				break;
+			case REPLY_TPLT_BTGT:
+				args[i] = blg ? blg->tgt.c : "";
+				break;
+			case REPLY_TPLT_BPROBE:
+				args[i] = blg ? blg->probe.c : "";
+				break;
+			case REPLY_TPLT_BRESULT:
+				args[i] = blg ? blg->result : "";
+				break;
+			}
+		}
+		snprintf(strs->str_buf, sizeof(strs->str_buf), tplt->pat,
+			 args[0], args[1], args[2], args[3], args[4], args[5]);
+		strs->str = strs->str_buf;
+	}
+
+	strs->log_result = tplt->log_result;
+}
diff -r 000000000000 -r c7f6b056b673 thrlib/totals.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thrlib/totals.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,210 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * count work
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * 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.
+ *
+ * Rhyolite Software DCC 1.3.103-1.19 $Revision$
+ */
+
+
+#include "cmn_defs.h"
+#include "helper.h"
+#include <signal.h>
+
+
+TOTALS totals;
+static time_t signaled;
+static pthread_t totals_tid;
+static u_char stopping;
+
+static void totals_msg_signal(int);
+static void *totals_msg_thread(void *);
+
+
+
+static void
+totals_msg_signal(int sig UATTRIB)
+{
+	time_t now;
+
+	signal(SIGUSR1, totals_msg_signal);
+
+	now = time(0);
+	/* ignore signals faster than 60 seconds */
+	if (!signaled || now > signaled+60) {
+		signaled = now;
+		totals.msg_next = signaled;
+	}
+}
+
+
+
+static void * NRATTRIB
+totals_msg_thread(void *ign UATTRIB)
+{
+	time_t now, next, last_clean;
+	struct tm tm, signaled_tm;
+	int secs;
+
+	clnt_sigs_off(0);
+
+	last_clean = time(0);
+	for (;;) {
+		if (stopping)
+			pthread_exit(0);
+
+		reap_helpers(0);
+
+		now = time(0);
+		next = totals.msg_next;
+		if (now >= next && next != 0) {
+			totals_msg();
+			next = 0;
+		}
+
+		if (!next) {
+			/* make a note to announce totals at midnight
+			 * or 24 hours after the signal */
+			dcc_localtime(totals.msg_prev, &tm);
+			if (signaled != 0) {
+				dcc_localtime(signaled, &signaled_tm);
+				tm.tm_hour = signaled_tm.tm_hour;
+				tm.tm_min = signaled_tm.tm_min;
+			} else {
+				tm.tm_hour = 0;
+				tm.tm_min = 0;
+			}
+			tm.tm_sec = 0;
+			++tm.tm_mday;
+			next = mktime(&tm);
+			if (next == -1) {
+				dcc_error_msg("mktime() failed");
+				next = now + 24*60*60;
+			}
+			totals.msg_next = next;
+
+		}
+
+		/* tell dccifd or dccm to close old sockets to recover
+		 * from dictionary attacks */
+		if (last_clean > now || last_clean+5*60 < now) {
+			last_clean = now;
+			work_clean();
+		}
+
+		secs = next - now;
+		if (secs > 0) {
+			/* reap helpers regularly */
+			if (secs > HELPER_AUTO_REAP)
+				secs = HELPER_AUTO_REAP;
+			sleep(secs);
+		}
+	}
+}
+
+
+
+void
+totals_init(void)
+{
+	int i;
+
+	signal(SIGUSR1, totals_msg_signal);
+	totals.msg_prev = time(0);
+
+	i = pthread_create(&totals_tid, 0, totals_msg_thread, 0);
+	if (i)
+		dcc_logbad(EX_SOFTWARE, "pthread_create(totals msg): %s",
+			   ERROR_STR1(i));
+	i = pthread_detach(totals_tid);
+	if (i)
+		dcc_error_msg("pthread_detach(totals msg): %s",
+			      ERROR_STR1(i));
+}
+
+
+
+void
+totals_stop(void)
+{
+	stopping = 1;
+	totals_msg();
+}
+
+
+
+/* brag about our accomplishments */
+void
+totals_msg(void)
+{
+	time_t now;
+	char tbuf[20];
+	char gbuf[80];
+	sigset_t sigsold;
+	int error;
+
+	clnt_sigs_off(&sigsold);
+
+	lock_work();
+	now = time(0);
+
+	if (grey_on)
+		snprintf(gbuf, sizeof(gbuf),
+			 " greylist embargoed %d messages to %d targets;",
+			 totals.msgs_embargoed, totals.tgts_embargoed);
+	else
+		gbuf[0] = '\0';
+
+	dcc_trace_msg(DCC_VERSION
+		      "%s detected %d spam, ignored for %d, rejected for %d,"
+		      " and discarded for %d targets among"
+		      " %d total messages for %d targets since %s",
+		      gbuf,
+		      totals.msgs_spam, totals.tgts_ignored,
+		      totals.tgts_rejected, totals.tgts_discarded,
+		      totals.msgs, totals.tgts,
+		      dcc_time2str(tbuf, sizeof(tbuf), "%x %X",
+				   totals.msg_prev));
+
+	memset(&totals, 0, sizeof(totals));
+	totals.msg_prev = now;
+	unlock_work();
+
+	error = pthread_sigmask(SIG_SETMASK, &sigsold, 0);
+	if (error)
+		dcc_logbad(EX_SOFTWARE, "pthread_sigmask(totals): %s",
+			   ERROR_STR1(error));
+}
diff -r 000000000000 -r c7f6b056b673 win32.mak
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/win32.mak	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,59 @@
+# Makefile for cdcc, dccproc, and someday dccifd for WIN32
+
+#   This assumes Borland's free command line tools FreeCommandLineTools.exe 
+#   available in 2004 at
+#	http://www.borland.com/products/downloads/download_cbuilder.html
+#   and elsewhere
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.6 $Revision$
+
+!include "win32.makinc1"
+
+SUBDIRS= dcclib cdcc dccproc
+
+all:	$(SUBDIRS)
+
+$(SUBDIRS):
+	cd $@
+	$(MAKE) -f win32.mak
+	cd ..
+
+SUBCMD	=for %d IN ($(SUBDIRS)) DO cd %d && $(MAKE) -f win32.mak $* && cd ..
+
+.precious: $(SUBDIRS)
+
+!include "win32.makinc2"
diff -r 000000000000 -r c7f6b056b673 win32.makinc1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/win32.makinc1	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,72 @@
+# Makefile for dcclib for WIN32.
+
+#   This assumes Borland's free command line tools FreeCommandLineTools.exe 
+#   available in 2004 at
+#	http://www.borland.com/products/downloads/download_cbuilder.html
+#   and elsewhere
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.7 $Revision$
+
+!if "$(MAKE)" == "NMAKE"
+__NMAKE__=yes
+!endif
+
+!ifndef __NMAKE__
+# Borland make
+.AUTODEPEND
+
+PATH	=$(PATH);c:/borland/bcc55/bin
+CC	=Bcc32
+LINK	=Tlink32
+
+INCS	=-Ic:/borland/bc55/include  -I../include
+CFLAGS	=-WM -WC -H=../win32.csm -O1 -w -w-par -wdef -wnod -wamb -w-sig $(INCS)
+LFLAGS	=-lTpe -lap -lc
+
+!else
+# Microsoft nmake
+PATH	=$(PATH)
+CC	=cl
+LINK	=link
+
+INCS	=-I../include
+CFLAGS	=/nologo /ML /W3 /O2 /I "..\include" /D "WIN32" /D "_CONSOLE"
+LFLAGS	=/nologo /subsystem:console /machine:I386 ws2_32.lib
+!endif
+
+
+DIRT	=*.obj *.lib *.csm
diff -r 000000000000 -r c7f6b056b673 win32.makinc2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/win32.makinc2	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,50 @@
+# Makefile for dcclib for WIN32.
+
+#   This assumes Borland's free command line tools FreeCommandLineTools.exe 
+#   available in 2004 at
+#	http://www.borland.com/products/downloads/download_cbuilder.html
+#   and elsewhere
+
+# Copyright (c) 2008 by Rhyolite Software, LLC
+#
+# This agreement is not applicable to any entity which sells anti-spam
+# solutions to others or provides an anti-spam solution as part of a
+# security solution sold to other entities, or to a private network
+# which employs the DCC or uses data provided by operation of the DCC
+# but does not provide corresponding data to other users.
+#
+# Permission to use, copy, modify, and distribute this software without
+# changes for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear in all
+# copies and any distributed versions or copies are either unchanged
+# or not called anything similar to "DCC" or "Distributed Checksum
+# Clearinghouse".
+#
+# Parties not eligible to receive a license under this agreement can
+# obtain a commercial license to use DCC by contacting Rhyolite Software
+# at sales@rhyolite.com.
+#
+# A commercial license would be for Distributed Checksum and Reputation
+# Clearinghouse software.  That software includes additional features.  This
+# free license for Distributed ChecksumClearinghouse Software does not in any
+# way grant permision to use Distributed Checksum and Reputation Clearinghouse
+# software
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+# 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.
+
+# Rhyolite Software DCC 1.3.103-1.2 $Revision$
+
+clean:
+	del $(DIRT)
+	$(SUBCMD)
+
+clobber:
+	del $(DIRT) $(TARGET)
+	$(SUBCMD)