# 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 & 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 -t 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 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 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 -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 -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 -h dir</A> +or +<A HREF="dccproc.html#OPTION-m">dccproc -m 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 -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 info</A> +or +<A HREF="cdcc.html#OPERATION-RTT">cdcc RTT</A> operations. +Add to the list with +<A HREF="cdcc.html#OPERATION-add">cdcc add</A> +or <A HREF="cdcc.html#OPERATION-load">cdcc 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 -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 add</A> +and <A HREF="cdcc.html#OPERATION-load">cdcc 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 -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 -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 ,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 -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 -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 -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 -w 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 -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 -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 <<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 -S</A> or +<A HREF="dccm.html#OPTION-S">dccm -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 -S</A> or +<A HREF="dccm.html#OPTION-S">dccm -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 "<>" 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 <> +</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 -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 -a</A> +or <A HREF="dccproc.html#OPTION-R">dccproc -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" jsmith@example.com</I>. +The file must contain <I>"J.Smith" 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 -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 -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 -t 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 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 -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 -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 trace</A> operation +or by restarting the server with +<A HREF="dccd.html#OPTION-T">dccd -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 "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 -Hv</A>. + + +<P><DT><A NAME="rtt"> +Why didn't the RTT reported by the</A> +<A HREF="cdcc.html#OPERATION-info">cdcc 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 stats</A>, +<A HREF="cdcc.html#OPERATION-flood-list">cdcc "id X; flood list"</A> +and +<A HREF="dblist.html#OPTION-H">@libexecdir@/dblist -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 "<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 -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> + <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> + <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> + <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>  + <TD><EM>./configure</EM> + <TD>/usr/local/man + <TD>installation directory for man pages<SUP>3</SUP> +<TR><TD class=env> + <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> + <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> + <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> + <TD><EM>./configure</EM> + <TD>root + <TD>user name and set-UID for DCC programs and data +<TR><TD class=env> + <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> + <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> + <TD class=conf>DCC_MODE<SUP>1</SUP> + <TD>make + <TD>555 + <TD>mode of most installed programs +<TR><TD class=env> + <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> + <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> + <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> + <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> + <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> + <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> + <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> + <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> + <TD><EM>./configure</EM> + <TD>@dcc_rundir@ + <TD>"run" directory for PIDs and sockets +<TR><TD class=env> + <TD class=conf>CFLAGS<SUP>1</SUP> + <TD>make & <EM>./configure</EM> + <TD> + <TD>global compiler options such as -g or -O2 +<TR><TD class=env> + <TD class=conf>DCC_CFLAGS<SUP>2</SUP> + <TD><EM>./configure</EM> + <TD>depends on target + <TD>global compiler options +<TR><TD class=env> + <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> + <TD class=conf>LDFLAGS<SUP>1</SUP> + <TD>make & <EM>./configure</EM> + <TD> + <TD>global linker options +<TR><TD class=env> + <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> + <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> + <TD class=conf><A NAME="envtbl-LIBS">LIBS</A><SUP>2</SUP> + <TD><EM>./configure</EM> + <TD> + <TD>additional libraries linked with all programs +<TR><TD class=env> + <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> + <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> + <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> + <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> + <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> + <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> + <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> + <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> + <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> + <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> + <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> + <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> + <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> +</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 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 >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 = '&'; + $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/&/&/g; + $out =~ s/</</g; + $out =~ s/>/>/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 " " 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 .= " ..."; + 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&val="; + $msg_hdrs .= url_encode($$cur_hdr); + $msg_hdrs .= "&msg=$msg&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 .= "&val=%3c%3e&msg=$msg&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 : " "; + $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> \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> "); + 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> + <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 : " "; + 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} . "&" 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 = " ¦\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}) : " "; +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> \n <TH>Subject\n"; + } elsif ($last_date ne $next_date) { + print "<TR><TH> \n"; + print " <TH>$next_date"; + print "\n <TH> \n <TH> \n <TH> \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> + <TH>Next Day + <TH> + <TH> + <TH> +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 + <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&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> \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 ": \n <TD>"; + if (! $enable) { + print html_str_encode($text . $value); + print "\n"; + return; + } + + if ($text) { + print $text; + print " "; + } + print $edit_link; + print "${url_ques}type="; + print url_encode($type); + print "&val="; + print url_encode($value); + print "&msg=$query{msg}" if ($query{msg}); + print "&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 .= "&val="; + $str .= url_encode($sum); + $str .= "&msg=$msg&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&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 > 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<-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 <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><string></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->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->ok,all->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 " 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   no-break space */ + FC_SP, /* iexcl ¡ inverted ! */ + FC_JK, /* cent ¢ cent sign */ + FC_JK, /* pound £ pound sign */ + FC_JK, /* curren ¤ currency sign */ + FC_JK, /* yen ¥ yen sign */ + FC_JK, /* brvbar ¦ broken bar */ + FC_JK, /* sect § section sign */ + + FC_SK, /* uml ¨ diaeresis */ + FC_SP, /* copy © copyright sign */ + FC_JK, /* ordf ª feminine ordinal indicator */ + FC_SP, /* laquo « << quotation mark */ + FC_JK, /* not ¬ not sign */ + FC_SK, /* shy ­ soft hyphen */ + FC_SP, /* reg ® registered sign */ + FC_SK, /* macr ¯ macron */ + + FC_JK, /* deg ° degree sign */ + FC_JK, /* plusmn ± plus-minus sign */ + FC_JK, /* sup2 ² superscript two */ + FC_JK, /* sup3 ³ superscript three */ + FC_SK, /* acute ´ acute accent */ + FC_JK, /* micro µ micro sign */ + FC_JK, /* para ¶ pilcrow sign */ + FC_JK, /* middot · middle dot */ + + FC_SK, /* cedil ¸ cedilla */ + FC_JK, /* sup1 ¹ superscript one */ + FC_JK, /* ordm º masculine ordinal indicator */ + FC_SP, /* raquo » >> quotation mark */ + FC_JK, /* 188 */ + FC_JK, /* 189 */ + FC_JK, /* 190 */ + FC_SP, /* iquest ¿ inverted question mark */ + + 224, /* Agrave À capital A with grave */ + 225, /* Aacute Á capital A with acute */ + 226, /* Acirc  capital A with circumflex */ + 227, /* Atilde à capital A with tilde */ + 228, /* Auml Ä capital A with diaeresis */ + 229, /* Aring Å capital A with ring above */ + 230, /* AElig Æ capital AE */ + 231, /* Ccedil Ç capital C with cedilla */ + + 232, /* Egrave È capital E with grave */ + 233, /* Eacute É capital E with acute */ + 234, /* Ecirc Ê capital E with circumflex */ + 235, /* Euml Ë capital E with diaeresis */ + 236, /* Igrave Ì capital I with grave */ + 237, /* Iacute Í capital I with acute */ + 238, /* Icirc Î capital I with circumflex */ + 239, /* Iuml Ï capital I with diaeresis */ + + 240, /* ETH Ð capital ETH */ + 241, /* Ntilde Ñ capital N with tilde */ + 242, /* Ograve Ò capital O with grave */ + 243, /* Oacute Ó capital O with acute */ + 244, /* Ocirc Ô capital O with circumflex */ + 245, /* Otilde Õ capital O with tilde */ + 246, /* Ouml Ö capital O with diaeresis */ + FC_JK, /* times × multiplication sign */ + + 248, /* Oslash Ø capital O with stroke */ + 249, /* Ugrave Ù capital U with grave */ + 250, /* Uacute Ú capital U with acute */ + 251, /* Ucirc Û capital U with circumflex */ + 252, /* Uuml Ü capital U with diaeresis */ + 253, /* Yacute Ý capital Y with acute */ + 254, /* THORN Þ capital THORN */ + 223, /* szlig ß small sharp s */ + + 224, /* agrave à small a with grave */ + 225, /* aacute á small a with acute */ + 226, /* acirc â small a with circumflex */ + 227, /* atilde ã small a with tilde */ + 228, /* auml ä small a with diaeresis */ + 229, /* aring å small a with ring above */ + 230, /* aelig æ small ae */ + 231, /* ccedil ç small c with cedilla */ + + 232, /* egrave è small e with grave */ + 233, /* eacute é small e with acute */ + 234, /* ecirc ê small e with circumflex */ + 235, /* euml ë small e with diaeresis */ + 236, /* igrave ì small i with grave */ + 237, /* iacute í small i with acute */ + 238, /* icirc î small i with circumflex */ + 239, /* iuml ï small i with diaeresis */ + + 240, /* eth ð small eth */ + 241, /* ntilde ñ small n with tilde */ + 242, /* ograve ò small o with grave */ + 243, /* oacute ó small o with acute */ + 244, /* ocirc ô small o with circumflex */ + 245, /* otilde õ small o with tilde */ + 246, /* ouml ö small o with diaeresis */ + FC_JK, /* divide ÷ division sign */ + + 248, /* oslash ø small o with stroke */ + 249, /* ugrave ù small u with grave */ + 250, /* uacute ú small u with acute */ + 251, /* ucirc û small u with circumflex */ + 252, /* uuml ü small u with diaeresis */ + 253, /* yacute ý small y with acute */ + 254, /* thorn þ small thorn */ + 255 /* yuml ÿ 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 • bullet = black small circle + * FC_SP hellip … horizontal ellipsis + * FC_SP ensp   en space + * FC_SP emsp   em space + * FC_SP thinsp   thin space + * FC_SP zwj ‍ zero width joiner + * FC_SP lsquo ‘ left single quotation mark + * FC_SP rsquo ’ right single quotation mark + * FC_SP sbquo ‚ single low-9 quotation mark + * FC_SP ldquo “ left double quotation mark + * FC_SP rdquo ” right double quotation mark + * FC_SP bdquo „ double low-9 quotation mark + * FC_SP dagger † dagger + * FC_SP Dagger ‡ double dagger + * FC_SP lsaquo ‹ single left-pointing angle " + * FC_SP rsaquo › 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 " 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   no-break space */ + 0xa1, /* A' LATIN CAPITAL A WITH OGONEK */ + FC_SK, /* '( BREVE */ + 0xa3, /* L/ LATIN CAPITAL L WITH STROKE */ + FC_JK, /* curren ¤ currency sign */ + 0xa5, /* L< */ + 0xa6, /* S' LATIN CAPITAL S WITH ACUTE */ + FC_JK, /* sect § 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 ­ soft hyphen */ + 0xae, /* Z< LATIN CAPITAL Z WITH CARON */ + 0xaf, /* Z. LATIN CAPITAL Z WITH DOT ABOVE */ + + FC_JK, /* deg ° degree sign */ + 0xb1, /* a; LATIN SMALL A WITH OGONEK */ + FC_SK, /* '; OGONEK */ + 0xb3, /* l/ LATIN SMALL L WITH STROKE */ + FC_SK, /* acute ´ acute accent */ + 0xb5, /* l< LATIN SMALL L WITH CARON */ + 0xb6, /* s' LATIN SMALL S WITH ACUTE */ + FC_SK, /* '< CARON */ + + FC_SK, /* cedil ¸ 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 × 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 ÷ 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, + $&{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 <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} $@ $&{rcpt_mailer}/$&{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 + "<>". + + <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 >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/&/\&/g; + $$str =~ s/</\</g; + $$str =~ s/>/\>/g; + $$str; +} + +##--------------------------------------------------------------------------- +## htmlize2() is used by entitize. +## +sub htmlize2 { + my($str) = shift; + $str =~ s/&/\&/g; + $str =~ s/</\</g; + $str =~ s/>/\>/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)