echolot-2.1.9/0002755000175000017500000000000012422222122012473 5ustar weaselweaselecholot-2.1.9/debian/0002755000175000017500000000000012422221767013733 5ustar weaselweaselecholot-2.1.9/debian/rules0000755000175000017500000000316412422221767015015 0ustar weaselweasel#!/usr/bin/make -f # Sample debian/rules that uses debhelper. # GNU copyright 1997 to 1999 by Joey Hess. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 version=`grep 'VERSION =' pingd | sed -e "s/.* '//" -e "s/'.*//"` REMAILERS=`tr '\n' ' ' < debian/remailers` ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) CFLAGS += -g endif ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) INSTALL_PROGRAM += -s endif build: build-stamp build-stamp: touch build-stamp clean: dh_testdir dh_testroot rm -f build-stamp dh_clean install: build dh_testdir dh_testroot dh_clean -k dh_installdirs tar c Echolot | tar xv -C $(CURDIR)/debian/echolot/usr/share/perl5/ tar c --exclude LICENSE templates | tar xv -C $(CURDIR)/debian/echolot/etc/echolot install -m 755 pingd $(CURDIR)/debian/echolot/usr/bin/ install -m 644 debian/pingd.conf $(CURDIR)/debian/echolot/etc/echolot install -m 644 debian/echolot.default $(CURDIR)/debian/echolot/etc/default/echolot binary-indep: build install dh_testdir dh_testroot dh_installdebconf dh_installdocs dh_installmenu dh_installlogrotate dh_installman dh_installchangelogs NEWS dh_installinit dh_link dh_strip dh_compress dh_fixperms dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb binary-arch: # We have nothing to do by default. binary: binary-indep binary-arch update-remailers: dh_testdir wget -O - http://stats.melontraffickers.com/mlist2.txt | grep '@' | sed -e 's/.*.*//' > debian/remailers debian/remailers: update-remailers .PHONY: build clean binary-indep binary-arch binary install configure remailers echolot-2.1.9/debian/README.Debian0000644000175000017500000000237412422221767016000 0ustar weaselweaselEcholot for Debian ------------------ Make sure mail to the Echolot pinger reaches /var/mail/echolot (or configure echolot to read it from any other location). Please really check that mail to the configured domain works. Any bounces will annoy dozends of remailer operators. To send commands to pingd it's best to use the /etc/init.d/echolot script. It takes care that pingd is only called as the correct user. See pingd(1) for a list of commands and their description. If you run this pinger please consider publishing the results so that other people benefit from it. Announcing the URL to the remailer operators' list , the alt.privacy.anon-server Usenet newsgroup and sending a mail to pingers@palfrader.org would be appreciated. Since many users installed Echolot without considering its implications the default setup is now to no longer start the pingd in the default installation. To actually enable it please modify /etc/default/echolot. If you want to run a pinger, please configure /etc/echolot/pingd.conf and /etc/default/echolot. Then start echolot using /etc/init.d/echolot start and add some addresses to it: /etc/init.d/echolot add remailer@example.com. -- Peter Palfrader , Sun, 14 Nov 2004 23:31:24 +0100 echolot-2.1.9/debian/echolot.default0000644000175000017500000000036112422221767016734 0ustar weaselweasel# Defaults for echolot initscript # sourced by /etc/init.d/echolot # installed at /etc/default/echolot # # This is a POSIX shell fragment # VERBOSE=0 # Only enable this after you've read /usr/share/doc/echolot/README.Debian RUN_ECHOLOT=0 echolot-2.1.9/debian/control0000644000175000017500000000167612422221767015346 0ustar weaselweaselSource: echolot Section: mail Priority: extra Maintainer: Peter Palfrader Build-Depends: debhelper (>= 4.1.16) Standards-Version: 3.7.2 Package: echolot Architecture: all Depends: ${shlibs:Depends}, gnupg (>= 1.0.7), postfix | mail-transport-agent, mixmaster, libdigest-md5-perl, libhtml-template-perl, libgnupg-interface-perl (>= 0.33), adduser Description: Pinger for anonymous remailers such as Mixmaster A Pinger in the context of anonymous remailers is a program that regularly sends messages through remailers to determine their status. Based on the responses, the Pinger calculates reliability statistics which may be used by remailer clients to choose a chain of remailers to use. . Furthermore, Echolot collects configuration parameters and keys of remailers and offers the collected information in a format readable by remailer clients. This helps reduce the administration effort required to use or host remailers. echolot-2.1.9/debian/pingd.conf0000644000175000017500000000141612422221767015703 0ustar weaselweasel# vim:set syntax=perl: # see man pingd.conf(5) for a list of all available configuration options. $CONFIG = { 'separate_rlists' => 1, 'combined_list' => 1, }; $CONFIG->{'mailin' } = '/var/mail/echolot'; # You probably want to modify these: $CONFIG->{'my_localpart' } = 'echolot'; $CONFIG->{'my_domain' } = 'pinger.example.org'; $CONFIG->{'operator_address' } = 'abuse@pinger.example.org'; $CONFIG->{'sitename' } = 'example'; #$CONFIG->{'recipient_delimiter' } = '+'; $CONFIG->{'recipient_delimiter' } = ''; $CONFIG->{'mixmaster'} = 'mixmaster'; $CONFIG->{'homedir' } = '/var/lib/echolot'; $CONFIG->{'pidfile' } = '/var/run/echolot/pingd.pid'; $CONFIG->{'logfile' } = '/var/log/echolot/echolot.log'; 1; echolot-2.1.9/debian/echolot.links0000644000175000017500000000006212422221767016426 0ustar weaselweasel/etc/echolot/templates /var/lib/echolot/templates echolot-2.1.9/debian/echolot.postrm0000755000175000017500000000106312422221767016637 0ustar weaselweasel#!/bin/sh -e # postrm for Echolot case "$1" in purge) rm -rf /var/lib/echolot rm -rf /var/log/echolot rm -rf /var/run/echolot rmdir /etc/echolot/templates 2>/dev/null || true rmdir /etc/echolot 2>/dev/null || true dpkg-statoverride --remove /var/run/echolot >/dev/null 2>&1 || true dpkg-statoverride --remove /var/lib/echolot >/dev/null 2>&1 || true ;; remove|upgrade|deconfigure) ;; failed-upgrade) ;; abort-upgrade) ;; *) echo "unknown argument --> $1" >&2 exit 0 ;; esac #DEBHELPER# # vim:set ts=2: # vim:set shiftwidth=2: echolot-2.1.9/debian/echolot.manpages0000644000175000017500000000003512422221767017101 0ustar weaselweaseldoc/pingd.1 doc/pingd.conf.5 echolot-2.1.9/debian/echolot.init0000755000175000017500000001003712422221767016257 0ustar weaselweasel#!/bin/sh # # pingcntl: echolot control + wrapper # Written by admin@arancio.net, peter@palfrader.org # ### BEGIN INIT INFO # Provides: echolot # Required-Start: $local_fs $remote_fs $time # Required-Stop: $local_fs $remote_fs # Should-Start: mail-transport-agent $syslog # Should-Stop: mail-transport-agent $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Pinger for anonymous remailers # Description: Echolot is a pinger for anonymous remailers # such as Mixmaster. As the name implies it # regularly sends pings to remailers to # collect performance statistics on each node. ### END INIT INFO set -e VERBOSE=0 # You probably don't want to mess with stuff below this line ################################################################ RUNDIR=/var/run/echolot PIDFILE="$RUNDIR/pingd.pid" CHECKULIMIT=1 CHECKUID=1 USER=echolot GROUP=echolot DAEMON=/usr/bin/pingd DESC="Echolot Ping Daemon" NAME="pingd" test -f $DAEMON || exit 0 PATH=/bin:/usr/bin:/sbin:/usr/sbin export PATH # Reads config file (will override defaults above) [ -r /etc/default/echolot ] && . /etc/default/echolot if [ ! -d ${RUNDIR} ]; then mkdir "$RUNDIR" chown root:"$GROUP" "$RUNDIR" chmod 02770 "$RUNDIR" fi wait_for_deaddaemon () { PID=$1 sleep 3 if test -n "$PID" then if kill -0 $PID 2>/dev/null then echo -n "Waiting for pid $PID ." cnt=0 while kill -0 $PID 2>/dev/null do cnt=`expr $cnt + 1` if [ $cnt -gt 30 ] then echo " Failed.. " exit 1 fi sleep 2 echo -n "." done rm -f $PIDFILE else rm -f $PIDFILE fi fi } # Check for evil ulimits if [ "$CHECKULIMIT" -gt "0" ]; then FDs=`ulimit -n` HFDs=`ulimit -H -n` if [ "$FDs" -lt "512" ]; then if [ "$HFDs" -lt "512" ]; then echo "Hardlimit for open File Descriptors is less than 512." >&2 echo "Please consider raising it." >&2 if [ "$FDs" -lt "$HFDs" ]; then echo "Raising it to $HFDs" >&2 ulimit -n $HFDs fi else if [ "$HFDs" -lt "1024" ]; then FDs=$HFDs else FDs=1024 fi echo "Softlimit for open File Descriptors is less than 512." >&2 echo "Raising it to $FDs" >&2 ulimit -n $FDs fi fi fi # set VERBOSE if [ $VERBOSE -gt 0 ]; then VERBOSE="--verbose" else VERBOSE="" fi case $1 in start) if [ -f $PIDFILE ] ; then PID=`cat $PIDFILE 2>/dev/null` || true if kill -0 $PID 2>/dev/null then echo "$DESC already running." exit 0 else echo -n "Removing stale pid file: " rm -f $PIDFILE echo "$PIDFILE." fi fi if [ $RUN_ECHOLOT -gt 0 ]; then echo -n "Starting $DESC: " start-stop-daemon \ --start \ --quiet \ --pidfile $PIDFILE \ --chuid $USER:$GROUP \ --exec $DAEMON -- --detach $VERBOSE --process --quiet start echo "$NAME." else echo "Not starting $DESC: disabled in configuration" fi ;; stop) echo -n "Stopping $DESC: " PID=`cat $PIDFILE 2>/dev/null` || true start-stop-daemon \ --stop \ --quiet \ --oknodo \ --pidfile $PIDFILE \ --user $USER wait_for_deaddaemon $PID echo "$NAME." ;; reload|force-reload|restart) PID=`cat $PIDFILE 2>/dev/null` || true $0 stop wait_for_deaddaemon $PID $0 start ;; process|add|delete|set|setremailercaps|deleteremailercaps|getkeyconf|sendpings|sendchainpings|buildstats|buildkeys|buildthesaurus|buildfromlines|dumpconf|summary|enable|disable) # Check for right User if [ "$CHECKUID" -gt "0" ]; then CUID=`id -u` CUIDNAME=`id -nu` if [ "$CUIDNAME" = "$USER" ]; then "$DAEMON" "$@" elif [ "$CUID" = "0" ]; then command="$DAEMON" while [ "$#" -gt 0 ]; do command="$command \"$1\"" shift done su "$USER" -c "$command" else echo "You are neither $USER nor root. Aborting." >&2 exit 1; fi fi echo "Running $DESC: $NAME $1..." echo "done." ;; *) echo "Usage: $0 (start|stop|reload|force-reload|restart)" >&2 echo " $0 [parameters]" >&2 echo "See the pingd(1) manual page for valid commands" >&2 exit 1 ;; esac exit 0 # vim:set ts=2: # vim:set shiftwidth=2: echolot-2.1.9/debian/changelog0000644000175000017500000002474212422221767015614 0ustar weaselweaselecholot (2.1.9-1) unstable; urgency=medium * New upstream version. -- Peter Palfrader Wed, 22 Oct 2014 21:04:17 +0200 echolot (2.1.8-8) unstable; urgency=low * Really fix the markup typo in the pingd manpage. -- Peter Palfrader Mon, 15 Oct 2012 17:01:32 +0200 echolot (2.1.8-7) unstable; urgency=low * No longer ship /var/run/echolot in the package. Based upon work by Thomas Goirand (closes: #689889). * Fix a markup typo in the pingd manpage. * Fix an error in README.Debian where we say /etc/default/echolot when we mean /etc/init.d/echolot. * Fix a spelling mistake in README.Debian (closes: #459926). * Fix the name of an option in an example in pingd.conf(5) (closes: #459938). * Update the address for the remops mailinglist in README.Debian (closes: #563810). -- Peter Palfrader Mon, 15 Oct 2012 16:24:47 +0200 echolot (2.1.8-6) unstable; urgency=low * In postrm during purge remove (rm -rf) /var/lib/echolot instead of /var/lib/echolot/*. (This seems cleaner, even tho /var/lib/echolot, the directory, should be removed a bit later by dpkg anyway since it's part of the package. Since it is the home directory of the echolot user it quite possible that some admin action caused dot files to appear in that directory.) * Also rm -rf /var/log/echolot in postrm purge (closes: #455105). * Move DH_COMPAT from the rules file into debian/compat. * Add LSB tags to init script. -- Peter Palfrader Mon, 10 Dec 2007 10:39:41 +0100 echolot (2.1.8-5) unstable; urgency=low * Increase Standards-Version from 3.6.2 to 3.7.2 without any additional changes. * Change the build time dependency on debhelper from a Build-Depends-Indep to Build-Depends. -- Peter Palfrader Sat, 17 Jun 2006 20:51:59 +0200 echolot (2.1.8-4) unstable; urgency=low * Properly quote arguments to su -c, since its behaviour has changed. * Update to debhelper compatibility level 4, no other changes required (we were already build depending on debhelper >= 4.1.16). -- Peter Palfrader Mon, 6 Mar 2006 15:54:57 +0100 echolot (2.1.8-3) unstable; urgency=low * Increase Standards-Version to 3.6.2 from 3.6.1. * Add dependency on adduser since we need it in postinst. * Fix format of chown argument from echolot.adm to echolot:adm. * Update FSF address in debian/copyright. -- Peter Palfrader Sun, 9 Oct 2005 23:26:28 +0200 echolot (2.1.8-2) unstable; urgency=low * Since we removed all the debconf templates in 2.1.7-1 we don't need to depend on debconf at all. If it is installed however we still purge our debconf database on upgrades (closes: #331811). -- Peter Palfrader Sun, 9 Oct 2005 23:17:48 +0200 echolot (2.1.8-1) unstable; urgency=low * New upstream version: - redirect init script output to /dev/null in logrotate. -- Peter Palfrader Mon, 25 Apr 2005 02:13:29 +0200 echolot (2.1.7-1) unstable; urgency=low * New upstream version: - remove debconf stuff. - create echolot group in proper gid space. - modify init script to allow 'enable' and 'disable' command. -- Peter Palfrader Sun, 14 Nov 2004 23:33:41 +0100 echolot (2.1.6-2) unstable; urgency=low * Add disable and enable to allowed commands in init script. -- Peter Palfrader Sat, 25 Sep 2004 16:49:11 +0200 echolot (2.1.6-1) unstable; urgency=medium * New upstream version, catch one use of an undefined variable in logging. -- Peter Palfrader Sat, 7 Aug 2004 16:00:52 +0200 echolot (2.1.5-1) unstable; urgency=low * New upstream version. -- Peter Palfrader Tue, 22 Jun 2004 11:40:01 +0200 echolot (2.1.4-1) unstable; urgency=low * New upstream version. * Update debian/copyright to include 2004. * Add dutch translation for debconf templates (closes: #249406). * Update Standards-Version to 3.6.1. * debian/rules: there is no lib/ to copy any longer. -- Peter Palfrader Thu, 10 Jun 2004 15:47:52 +0200 echolot (2.1.3-1) unstable; urgency=low * New upstream version. closes: #243504 echolot: typo in month abbreviations * Document exim's local_part_suffix in the recipient_delimiter debconf question. closes: #243496 Exim4 mailbox delimiter -- Peter Palfrader Tue, 20 Apr 2004 13:42:32 +0200 echolot (2.1.2-1) unstable; urgency=low * New upstream version. -- Peter Palfrader Tue, 4 Nov 2003 05:16:07 +0100 echolot (2.1.1-1) unstable; urgency=low * New upstream version. -- Peter Palfrader Sun, 2 Nov 2003 07:03:43 +0100 echolot (2.1-2) unstable; urgency=low * debconf internationialization. Thanks to Christian Perrier for providing the patches and information. closes: #212885 [INTL:fr] French translation of gettext debconf templates closes: #211633 [INTL] Please switch to gettext-based debconf templates -- Peter Palfrader Sun, 12 Oct 2003 12:15:38 +0200 echolot (2.1-1) unstable; urgency=low * New upstream version. -- Peter Palfrader Mon, 3 Mar 2003 16:42:42 +0100 echolot (2.0.10-1) unstable; urgency=low * New upstream version. -- Peter Palfrader Mon, 3 Feb 2003 21:11:42 +0100 echolot (2.0.9-1) unstable; urgency=medium * New upstream version. * Remove link from output to /var/log/echolot/echolot.log since pngd now has some sort of saner logging. -- Peter Palfrader Tue, 14 Jan 2003 08:08:41 +0100 echolot (2.0.8-1) unstable; urgency=medium * New upstream version. Fixes evil bug where pingd receives SIGPIPE because GnuPG already exited because of unuseable keys. -- Peter Palfrader Mon, 13 Jan 2003 15:25:27 +0100 echolot (2.0.7-1) unstable; urgency=low * New upstream version. -- Peter Palfrader Wed, 18 Dec 2002 19:37:08 +0100 echolot (2.0.6-1) unstable; urgency=low * New upstream version. * Fixed init.d script: s,devnull,dev/null, * lintian: init-script-suggests-versioned-depends postinst on debconf. -- Peter Palfrader Wed, 18 Dec 2002 18:44:27 +0100 echolot (2.0.5-2) unstable; urgency=low * Add sendpings command to init script. * Have Echolot disabled by default in /etc/default/echolot. * Add some more comments to README.Debian. * Also process pending commands upon pingd startup. -- Peter Palfrader Tue, 12 Nov 2002 02:06:17 +0100 echolot (2.0.5-1) unstable; urgency=low * New upstream version (supports use without user defined mailboxes). * Make recipient delimiter configured by debconf. -- Peter Palfrader Fri, 25 Oct 2002 12:54:44 +0200 echolot (2.0.4-3) unstable; urgency=low * Add a debconf note about user defined mailboxes being required (foo+bar@example.com). -- Peter Palfrader Thu, 24 Oct 2002 20:31:04 +0200 echolot (2.0.4-2) unstable; urgency=low * Change Depends from libdigest-MD5-perl to libdigest-md5-perl (capitalization). * Changed Standards-Version to 3.5.7 (no changes). -- Peter Palfrader Thu, 24 Oct 2002 13:40:41 +0200 echolot (2.0.4-1) unstable; urgency=low * New upstream version. * Now that mixmaster got added to the main archive, finally upload Echolot to Debian as well (closes: #164459). -- Peter Palfrader Wed, 16 Oct 2002 10:25:41 +0200 echolot (2.0.3-3) unstable; urgency=low * Remove Suggests: procmail. * Copy new description from README to control. * Updated remailer list. * Use a more correct directory structure (usr/lib/echolot -> usr/share/perl5/) -- Peter Palfrader Sat, 12 Oct 2002 14:43:28 +0200 echolot (2.0.3-2) unstable; urgency=low * Fix config script to not screw up domainname on upgrades. -- Peter Palfrader Sat, 12 Oct 2002 12:05:28 +0200 echolot (2.0.3-1) unstable; urgency=low * Changed Section from contrib/mail to (main/)mail as mimxaster is in main now. * New upstream version. -- Peter Palfrader Sat, 12 Oct 2002 01:10:18 +0200 echolot (2.0.2-1) unstable; urgency=low * New upstream version. -- Peter Palfrader Sat, 21 Sep 2002 05:11:37 +0200 echolot (2.0.1-1) unstable; urgency=low * New upstream version. -- Peter Palfrader Sat, 21 Sep 2002 03:45:14 +0200 echolot (2.0-1) unstable; urgency=low * New upstream version: This one's called 2.0. -- Peter Palfrader Tue, 17 Sep 2002 06:17:43 +0200 echolot (000.2.0rc3-1) unstable; urgency=low * New upstream version. -- Peter Palfrader Fri, 13 Sep 2002 01:16:21 +0200 echolot (000.2.0rc2-1) unstable; urgency=low * New upstream version. -- Peter Palfrader Sun, 8 Sep 2002 20:16:31 +0200 echolot (000.2.0rc1-1) unstable; urgency=low * New version number. -- Peter Palfrader Thu, 5 Sep 2002 16:51:42 +0200 echolot (000.2.0beta34-1) unstable; urgency=low * New upstream version -- Peter Palfrader Wed, 4 Sep 2002 13:22:13 +0200 echolot (000.2.0beta33-1) unstable; urgency=low * New upstream version -- Peter Palfrader Fri, 23 Aug 2002 09:54:10 +0200 echolot (000.2.0beta32-1) unstable; urgency=low * New upstream version -- Peter Palfrader Fri, 23 Aug 2002 08:05:26 +0200 echolot (000.2.0beta31-1) unstable; urgency=low * New upstream version -- Peter Palfrader Wed, 21 Aug 2002 21:45:37 +0200 echolot (000.2.0beta30-1) unstable; urgency=low * New upstream version -- Peter Palfrader Thu, 15 Aug 2002 05:28:11 +0200 echolot (000.2.0beta29-1) unstable; urgency=low * New upstream version -- Peter Palfrader Thu, 15 Aug 2002 00:54:31 +0200 echolot (000.2.0beta28-1) unstable; urgency=low * New upstream version -- Peter Palfrader Tue, 13 Aug 2002 07:58:31 +0200 echolot (000.2.0beta27-1) unstable; urgency=low * New upstream version * Set the mixmaster binary path. * Use --quiet when starting echolot. -- Peter Palfrader Tue, 13 Aug 2002 02:49:45 +0200 echolot (000.2.0beta26-1) unstable; urgency=low * Initial Release. -- Peter Palfrader Mon, 12 Aug 2002 05:44:24 +0200 echolot-2.1.9/debian/copyright0000644000175000017500000000207212422221767015665 0ustar weaselweaselThis package was debianized by Peter Palfrader on Mon, 22 Jul 2002 10:30:40 +0200. It was downloaded from http://www.palfrader.org/echolot/ Upstream Authors: Peter Palfrader Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader Echolot 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian systems you can find a copy of the GNU General Public License in /usr/share/common-licenses/GPL. echolot-2.1.9/debian/echolot.postinst0000755000175000017500000000207012422221767017175 0ustar weaselweasel#!/bin/sh -e # postinst for Echolot # remove old debconf stuff if [ "$1" = "configure" ] && [ -e /usr/share/debconf/confmodule ]; then if dpkg --compare-versions "$2" lt "2.1.7" ; then . /usr/share/debconf/confmodule db_purge db_stop fi fi if [ "$1" = "configure" ] && dpkg --compare-versions "$2" lt "2.1.8-7"; then if ( dpkg-statoverride --list /var/run/echolot > /dev/null ); then dpkg-statoverride --remove /var/run/echolot fi fi # Make sure the echolot user exists adduser --quiet \ --quiet \ --system \ --disabled-password \ --shell /bin/bash \ --home /var/lib/echolot \ --no-create-home \ --group \ --gecos "Echolot Pinger" \ echolot # Give the echolot user write permissions to /var/log/echolot/echolot.log touch /var/log/echolot/echolot.log chown echolot:adm /var/log/echolot/echolot.log chmod 640 /var/log/echolot/echolot.log # and /var/lib/echolot if ( ! dpkg-statoverride --list /var/lib/echolot > /dev/null ); then dpkg-statoverride --update --add root echolot 02775 /var/lib/echolot fi #DEBHELPER# # vim:set ts=4: # vim:set shiftwidth=4: echolot-2.1.9/debian/compat0000644000175000017500000000000212422221767015127 0ustar weaselweasel4 echolot-2.1.9/debian/echolot.logrotate0000644000175000017500000000025512422221767017312 0ustar weaselweasel/var/log/echolot/echolot.log { weekly rotate 5 compress missingok notifempty postrotate /etc/init.d/echolot process > /dev/null endscript create 640 echolot adm } echolot-2.1.9/debian/echolot.docs0000644000175000017500000000002412422221767016234 0ustar weaselweaselREADME TODO UPGRADE echolot-2.1.9/debian/echolot.dirs0000644000175000017500000000012012422221767016242 0ustar weaselweaselusr/bin usr/share/perl5 var/lib/echolot var/log/echolot etc/echolot etc/default echolot-2.1.9/Echolot/0002755000175000017500000000000012422221767014106 5ustar weaselweaselecholot-2.1.9/Echolot/Thesaurus.pm0000644000175000017500000001011412422221767016422 0ustar weaselweaselpackage Echolot::Thesaurus; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Thesaurus - build thesaurus pages =head1 DESCRIPTION This package provides necessary functions for the thesaurus. =cut use strict; use English; use Echolot::Log; sub save_thesaurus($$$) { my ($otype, $oid, $data) = @_; return 1 unless Echolot::Config::get()->{'thesaurus'}; my ($type) = $otype =~ /^([a-z-]+)$/; Echolot::Log::cluck("type '$otype' is not clean in save_thesaurus."), return 0 unless defined $type; my ($id) = $oid =~ /^([0-9]+)$/; Echolot::Log::cluck("id '$oid' is not clean in save_thesaurus."), return 0 unless defined $id; my $file = Echolot::Config::get()->{'thesaurusdir'}.'/'.$id.'.'.$type; open (F, ">$file") or Echolot::Log::warn ("Cannot open '$file': $!."), return 0; print F $data; close (F); return 1; }; sub build_thesaurus() { return 1 unless Echolot::Config::get()->{'thesaurus'}; my $dir = Echolot::Config::get()->{'thesaurusdir'}; opendir(DIR, $dir) or Echolot::Log::warn ("Cannot open '$dir': $!."), return 0; my @files = grep { ! /^\./ } readdir(DIR); closedir(DIR); my $expire_date = time() - Echolot::Config::get()->{'expire_thesaurus'}; my $data; for my $filename (@files) { my ($id, $what) = $filename =~ /^(\d+)\.(adminkey|conf|help|key|stats)$/; next unless (defined $id && defined $what); my $remailer = Echolot::Globals::get()->{'storage'}->get_address_by_id($id); next unless defined $remailer; next unless $remailer->{'showit'}; my $caps = Echolot::Globals::get()->{'storage'}->get_capabilities($remailer->{'address'}); next unless defined $caps; next unless $caps !~ m/\btesting\b/i; my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($dir.'/'.$filename); if ($mtime < $expire_date) { unlink ($dir.'/'.$filename) or Echolot::Log::warn("Cannot unlink expired $filename."); Echolot::Log::info("Expired thesaurus file $filename."); next; }; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($mtime); my $date = sprintf("%04d-%02d-%02d", $year+1900, $mon+1, $mday); my $time = sprintf("%02d:%02d", $hour, $min); $data->{$remailer->{'address'}}->{$what.'_href'} = $filename; $data->{$remailer->{'address'}}->{$what.'_date'} = $date; $data->{$remailer->{'address'}}->{$what.'_time'} = $time; $data->{$remailer->{'address'}}->{'id'} = $id; }; for my $addr (keys (%$data)) { my $nick = Echolot::Globals::get()->{'storage'}->get_nick($addr); if (defined $nick) { $data->{$addr}->{'nick'} = $nick; $data->{$addr}->{'address'} = $addr; } else { delete $data->{$addr}; }; }; my @data = map {$data->{$_}} (sort { $data->{$a}->{'nick'} cmp $data->{$b}->{'nick'} } keys (%$data)); Echolot::Tools::write_HTML_file( Echolot::Config::get()->{'thesaurusindexfile'}, 'thesaurusindexfile', Echolot::Config::get()->{'buildthesaurus'}, remailers => \@data); open(F, ">$dir/index.txt") or Echolot::Log::warn ("Cannot open '$dir/index.txt': $!."), return 0; for my $remailer (@data) { printf F "%s\t%s\t%s\n", $remailer->{'nick'}, $remailer->{'id'}, $remailer->{'address'}; }; close(F) or Echolot::Log::warn ("Cannot close '$dir/index.txt': $!."), return 0; }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Mailin.pm0000644000175000017500000001431512422221767015657 0ustar weaselweaselpackage Echolot::Mailin; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Mailin - Incoming Mail Dispatcher for Echolot =head1 DESCRIPTION =cut use strict; use English; use Echolot::Globals; use Echolot::Log; use Fcntl ':flock'; # import LOCK_* constants use POSIX; # import SEEK_* constants (older perls don't have SEEK_ in Fcntl) sub make_sane_name() { my $result = time().'.'.$PROCESS_ID.'_'.Echolot::Globals::get()->{'internal_counter'}++.'.'.Echolot::Globals::get()->{'hostname'}; return $result; }; sub sane_move($$) { my ($from, $to) = @_; my $link_success = link($from, $to); $link_success or Echolot::Log::warn("Cannot link $from to $to: $!."), return 0; #- Trying move"), #rename($from, $to) or # cluck("Renaming $from to $to didn't work either: $!"), # return 0; $link_success && (unlink($from) or Echolot::Log::warn("Cannot unlink $from: $!.") ); return 1; }; sub handle($) { my ($lines) = @_; my $i=0; my $body = ''; my $header = ''; my $to; for ( ; $i < scalar @$lines; $i++) { my $line = $lines->[$i]; chomp($line); last if $line eq ''; $header .= $line."\n"; if ($line =~ m/^To:\s*(.*?)\s*$/) { $to = $1; }; }; for ( ; $i < scalar @$lines; $i++) { $body .= $lines->[$i]; }; (defined $to) or Echolot::Log::info("No To header found in mail."), return 0; my $address_result = Echolot::Tools::verify_address_tokens($to) or Echolot::Log::debug("Verifying '$to' failed."), return 0; my $type = $address_result->{'token'}; my $timestamp = $address_result->{'timestamp'}; Echolot::Conf::remailer_conf($body, $type, $timestamp), return 1 if ($type =~ /^conf\./); Echolot::Conf::remailer_key($body, $type, $timestamp), return 1 if ($type =~ /^key\./); Echolot::Conf::remailer_help($body, $type, $timestamp), return 1 if ($type =~ /^help\./); Echolot::Conf::remailer_stats($body, $type, $timestamp), return 1 if ($type =~ /^stats\./); Echolot::Conf::remailer_adminkey($body, $type, $timestamp), return 1 if ($type =~ /^adminkey\./); Echolot::Pinger::receive($header, $body, $type, $timestamp), return 1 if ($type eq 'ping'); Echolot::Chain::receive($header, $body, $type, $timestamp), return 1 if ($type eq 'chainping'); Echolot::Log::warn("Didn't know what to do with '$to'."), return 0; }; sub handle_file($) { my ($file) = @_; open (FH, $file) or Echolot::Log::warn("Cannot open file $file: $!,"), return 0; my @lines = ; my $body = join('', ); close (FH) or Echolot::Log::warn("Cannot close file $file: $!."); return handle(\@lines); }; sub read_mbox($) { my ($file) = @_; my @mail; my $mail = []; my $blank = 1; open(FH, '+<'. $file) or Echolot::Log::warn("cannot open '$file': $!."), return undef; flock(FH, LOCK_EX) or Echolot::Log::warn("cannot gain lock on '$file': $!."), return undef; while() { if($blank && /\AFrom .*\d{4}/) { push(@mail, $mail) if scalar(@{$mail}); $mail = [ $_ ]; $blank = 0; } else { $blank = m#\A\Z# ? 1 : 0; push @$mail, $_; } } push(@mail, $mail) if scalar(@{$mail}); seek(FH, 0, SEEK_SET) or Echolot::Log::warn("cannot seek to start of '$file': $!."), return undef; truncate(FH, 0) or Echolot::Log::warn("cannot truncate '$file' to zero size: $!."), return undef; flock(FH, LOCK_UN) or Echolot::Log::warn("cannot release lock on '$file': $!."), return undef; close(FH); return \@mail; } sub read_maildir($) { my ($dir) = @_; my @mail; my @files; for my $sub (qw{new cur}) { opendir(DIR, $dir.'/'.$sub) or Echolot::Log::warn("Cannot open direcotry '$dir/$sub': $!."), return undef; push @files, map { $sub.'/'.$_ } grep { ! /^\./ } readdir(DIR); closedir(DIR) or Echolot::Log::warn("Cannot close direcotry '$dir/$sub': $!."); }; for my $file (@files) { $file =~ /^(.*)$/s or Echolot::Log::confess("I really should match here. ('$file')."); $file = $1; my $mail = []; open(FH, $dir.'/'.$file) or Echolot::Log::warn("cannot open '$dir/$file': $!."), return undef; @$mail = ; close(FH); push @mail, $mail; }; for my $file (@files) { unlink $dir.'/'.$file or Echolot::Log::warn("cannot unlink '$dir/$file': $!."); }; return \@mail; } sub storemail($$) { my ($path, $mail) = @_; my $tmpname = $path.'/tmp/'.make_sane_name(); open (F, '>'.$tmpname) or Echolot::Log::warn("Cannot open $tmpname: $!."), return undef; print F join ('', @$mail); close F; my $i; for ($i = 0; $i < 5; $i++ ) { my $targetname = $path.'/cur/'.make_sane_name(); sane_move($tmpname, $targetname) or sleep 1, next; last; }; return undef if ($i == 5); return 1; }; sub process() { my $inmail = Echolot::Config::get()->{'mailin'}; my $mailerrordir = Echolot::Config::get()->{'mailerrordir'}; my $mails = (-d $inmail) ? read_maildir($inmail) : ( ( -e $inmail ) ? read_mbox($inmail) : [] ); Echolot::Globals::get()->{'storage'}->delay_commit(); for my $mail (@$mails) { unless (handle($mail)) { if (Echolot::Config::get()->{'save_errormails'}) { Echolot::Log::info("Saving mail with unknown destination (probably a bounce) to mail-errordir."); my $name = make_sane_name(); storemail($mailerrordir, $mail) or Echolot::Log::warn("Could not store a mail."); } else { Echolot::Log::info("Trashing mail with unknown destination (probably a bounce)."); }; }; }; Echolot::Globals::get()->{'storage'}->enable_commit(); }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Globals.pm0000644000175000017500000000274112422221767016031 0ustar weaselweaselpackage Echolot::Globals; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Globals - echolot global variables =head1 DESCRIPTION =cut use strict; use Carp; my $GLOBALS; sub init(%) { my (%args) = @_; my $hostname = `hostname`; $hostname =~ /^([a-zA-Z0-9_.-]*)$/; $hostname = $1 || 'unknown'; $GLOBALS->{'hostname'} = $hostname; $GLOBALS->{'internalcounter'} = 1; $GLOBALS->{'version'} = $args{'version'}; }; sub initStorage { $GLOBALS->{'storage'} = new Echolot::Storage::File ( datadir => Echolot::Config::get()->{'storage'}->{'File'}->{'basedir'} ); }; sub get() { return $GLOBALS; }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Conf.pm0000644000175000017500000004444412422221767015341 0ustar weaselweaselpackage Echolot::Conf; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Conf - remailer Configuration/Capabilities =head1 DESCRIPTION This package provides functions for requesting, parsing, and analyzing remailer-conf and remailer-key replies. =head1 CAVEATS When parsing OpenPGP keys only the address of the primary user id is taken into account (This is the one with the latest self signature I think). =cut use strict; use Echolot::Log; use GnuPG::Interface; sub is_not_a_remailer($) { my ($reply) = @_; if ($reply =~ /^\s* not \s+ a \s+ remailer\b/xi) { return 1; } else { return 0; }; }; sub send_requests($;$) { my ($scheduled_for, $which) = @_; $which = '' unless defined $which; my $call_intervall = Echolot::Config::get()->{'getkeyconf_interval'}; my $send_every_n_calls = Echolot::Config::get()->{'getkeyconf_every_nth_time'}; my $timemod = int ($scheduled_for / $call_intervall); my $this_call_id = $timemod % $send_every_n_calls; my $session_id = int ($scheduled_for / ($call_intervall * $send_every_n_calls)); Echolot::Globals::get()->{'storage'}->delay_commit(); for my $remailer (Echolot::Globals::get()->{'storage'}->get_addresses()) { next unless ($remailer->{'status'} eq 'active'); next unless ($remailer->{'fetch'}); my $address = $remailer->{'address'}; next unless ( $which eq 'all' || $which eq $address || $which eq ''); for my $type (qw{conf key help stats adminkey}) { next unless ( $which eq $address || $which eq 'all' || (($which eq '') && ($this_call_id == (Echolot::Tools::makeShortNumHash($address.$type.$session_id) % $send_every_n_calls)))); Echolot::Log::debug("Sending $type request to ".$address."."); my $source_text = Echolot::Config::get()->{'remailerxxxtext'}; my $template = HTML::Template->new( scalarref => \$source_text, strict => 0, global_vars => 1 ); $template->param ( address => $address ); $template->param ( operator_address => Echolot::Config::get()->{'operator_address'} ); my $body = $template->output(); Echolot::Tools::send_message( 'To' => $address, 'Subject' => 'remailer-'.$type, 'Token' => $type.'.'.$remailer->{'id'}, 'Body' => $body); Echolot::Globals::get()->{'storage'}->decrease_ttl($address) if (($type eq 'conf') && ($which eq '')); }; }; Echolot::Globals::get()->{'storage'}->enable_commit(); }; sub check_resurrection() { Echolot::Globals::get()->{'storage'}->delay_commit(); for my $remailer (Echolot::Globals::get()->{'storage'}->get_addresses()) { next unless ($remailer->{'status'} eq 'ttl timeout'); next unless ($remailer->{'fetch'}); next unless ($remailer->{'resurrection_ttl'}); Echolot::Log::debug("Sending request to ".$remailer->{'address'}." to check for resurrection."); for my $type (qw{conf key help stats adminkey}) { Echolot::Tools::send_message( 'To' => $remailer->{'address'}, 'Subject' => 'remailer-'.$type, 'Token' => $type.'.'.$remailer->{'id'}) }; Echolot::Globals::get()->{'storage'}->decrease_resurrection_ttl($remailer->{'address'}); }; Echolot::Globals::get()->{'storage'}->enable_commit(); }; sub remailer_caps($$$;$) { my ($conf, $token, $time, $dontexpire) = @_; my ($id) = $token =~ /^conf\.(\d+)$/; (defined $id) or Echolot::Log::info("Returned token '$token' has no id at all."), return 0; Echolot::Log::info("Could not find id in token '$token'."), return 0 unless defined $id; my ($remailer_type) = ($conf =~ /^\s*Remailer-Type:\s* (.*?) \s*$/imx); Echolot::Log::info("No remailer type found in remailer_caps from '$token'."), return 0 unless defined $remailer_type; my ($remailer_caps) = ($conf =~ /^\s*( \$remailer{".*"} \s*=\s* "<.*@.*>.*"; )\s*$/imx); Echolot::Log::info("No remailer caps found in remailer_caps from '$token'."), return 0 unless defined $remailer_caps; my ($remailer_nick, $remailer_address) = ($remailer_caps =~ /^\s* \$remailer{"(.*)"} \s*=\s* "<(.*@.*)>.*"; \s*$/ix); Echolot::Log::info("No remailer nick found in remailer_caps from '$token': '$remailer_caps'."), return 0 unless defined $remailer_nick; Echolot::Log::info("No remailer address found in remailer_caps from '$token': '$remailer_caps'."), return 0 unless defined $remailer_address; my $remailer = Echolot::Globals::get()->{'storage'}->get_address_by_id($id); Echolot::Log::info("No remailer found for id '$id'."), return 0 unless defined $remailer; if ($remailer->{'address'} ne $remailer_address) { # Address mismatch -> Ignore reply and add $remailer_address to prospective addresses Echolot::Log::info("Remailer address mismatch $remailer->{'address'} vs $remailer_address. Adding latter to prospective remailers."); Echolot::Globals::get()->{'storage'}->add_prospective_address($remailer_address, 'self-capsstring-conf', $remailer_address); } else { Echolot::Log::debug("Setting capabilities for $remailer_address"); Echolot::Globals::get()->{'storage'}->restore_ttl( $remailer->{'address'} ); Echolot::Globals::get()->{'storage'}->set_caps($remailer_type, $remailer_caps, $remailer_nick, $remailer_address, $time, $dontexpire); # if remailer is cpunk and not pgponly if (($remailer_caps =~ /\bcpunk\b/) && !($remailer_caps =~ /\bpgponly\b/)) { Echolot::Globals::get()->{'storage'}->set_key( 'cpunk-clear', $remailer_nick, $remailer->{'address'}, 'N/A', 'none', 'N/A', 'N/A', 'N/A', $time); } } # Fetch prospective remailers from reliable's remailer-conf reply: my @lines = split /\r?\n/, $conf; while (1) { my $head; while (@lines) { $head = $lines[0]; chomp $head; shift @lines; last if ($head eq 'SUPPORTED CPUNK (TYPE I) REMAILERS' || $head eq 'SUPPORTED MIXMASTER (TYPE II) REMAILERS'); }; last unless defined $head; my $wanting = $head eq 'SUPPORTED CPUNK (TYPE I) REMAILERS' ? 1 : $head eq 'SUPPORTED MIXMASTER (TYPE II) REMAILERS' ? 2 : undef; last unless defined $wanting; while (@lines) { $head = $lines[0]; chomp $head; shift @lines; if ($wanting == 1) { last unless ($head =~ /<(.*?@.*?)>/); Echolot::Globals::get()->{'storage'}->add_prospective_address($1, 'reliable-caps-reply-type1', $remailer_address); } elsif ($wanting == 2) { last unless ($head =~ /\s(.*?@.*?)\s/); Echolot::Globals::get()->{'storage'}->add_prospective_address($1, 'reliable-caps-reply-type2', $remailer_address); } else { Echolot::Log::confess("Shouldn't be here. wanting == $wanting."); }; }; }; return 1; }; sub remailer_conf($$$) { my ($reply, $token, $time) = @_; my ($id) = $token =~ /^conf\.(\d+)$/; (defined $id) or Echolot::Log::info ("Returned token '$token' has no id at all."), return 0; my $remailer = Echolot::Globals::get()->{'storage'}->get_address_by_id($id); Echolot::Log::info("No remailer found for id '$id'."), return 0 unless defined $remailer; Echolot::Log::debug("Received remailer-conf reply for $remailer."), Echolot::Globals::get()->{'storage'}->not_a_remailer($id), return 1 if (is_not_a_remailer($reply)); Echolot::Thesaurus::save_thesaurus('conf', $id, $reply); remailer_caps($reply, $token, $time); }; sub set_caps_manually($$) { my ($addr, $caps) = @_; defined $addr or Echolot::Log::info("Address not defined."), return 0; defined $caps or Echolot::Log::info("Caps not defined."), return 0; Echolot::Log::info("Setting caps for $addr manually to $caps."); my $remailer = Echolot::Globals::get()->{'storage'}->get_address($addr); defined $remailer or Echolot::Log::info("Remailer address $addr did not give a valid remailer."), return 0; my $id = $remailer->{'id'}; defined $id or Echolot::Log::info("Remailer address $addr did not give a remailer with an id."), return 0; my $token = 'conf.'.$id; my $conf = "Remailer-Type: set-manually\n$caps"; remailer_caps($conf, $token, time, 1); return 1; }; sub parse_mix_key($$$) { my ($reply, $time, $remailer) = @_; # -----Begin Mix Key----- # 7f6d997678b19ccac110f6e669143126 # 258 # AASyedeKiP1/UKyfrBz2K6gIhv4jfXIaHo8dGmwD # KqkG3DwytgSySSY3wYm0foT7KvEnkG2aTi/uJva/ # gymE+tsuM8l8iY1FOiXwHWLDdyUBPbrLjRkgm7GD # Y7ogSjPhVLeMpzkSyO/ryeUfLZskBUBL0LxjLInB # YBR3o6p/RiT0EQAAAAAAAAAAAAAAAAAAAAAAAAAA # AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA # AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA # AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA # AAAAAAAAAAAAAAAAAAAAAQAB # -----End Mix Key----- my %mixmasters; # rot26 rot26@mix.uucico.de 7f6d997678b19ccac110f6e669143126 2.9b33 MC my @mix_confs = ($reply =~ /^ [a-z0-9]+ \s+ \S+\@\S+ \s+ [0-9a-f]{32} .*?$/xmg); my @mix_keys = ($reply =~ /^-----Begin \s Mix \s Key-----\r?\n [0-9a-f]{32}\r?\n \d+\r?\n (?:[a-zA-Z0-9+\/]*\r?\n)+ -----End \s Mix \s Key-----$/xmg ); for (@mix_confs) { my ($nick, $address, $keyid, $version, $caps, $created, $expires) = /^ ([a-z0-9]+) \s+ (\S+@\S+) \s+ ([0-9a-f]{32}) (?: [ \t]+ (\S+) (?: [ \t]+ (\S+) (?: [ \t]+ (\d{4}-\d{2}-\d{2}) (?: [ \t]+ (\d{4}-\d{2}-\d{2}) )? )? )? )? .*?/x; $mixmasters{$keyid} = { nick => $nick, address => $address, version => $version, caps => $caps, created => $created, expires => $expires, summary => $_ }; }; for (@mix_keys) { my ($keyid) = /^-----Begin \s Mix \s Key-----\r?\n ([0-9a-f]{32})\r?\n \d+\r?\n (?:[a-zA-Z0-9+\/]*\r?\n)+ -----End \s Mix \s Key-----$/xmg; $mixmasters{$keyid}->{'key'} = $_; }; for my $keyid (keys %mixmasters) { my $remailer_address = $mixmasters{$keyid}->{'address'}; (defined $mixmasters{$keyid}->{'nick'}) or Echolot::Log::info("Could not parse a remailer-key reply."), next; (defined $mixmasters{$keyid}->{'nick'} && ! defined $mixmasters{$keyid}->{'key'}) and Echolot::Log::info("Mixmaster key header without key in reply from $remailer_address."), next; (! defined $mixmasters{$keyid}->{'nick'} && defined $mixmasters{$keyid}->{'key'}) and Echolot::Log::info("Mixmaster key without key header in reply from $remailer_address."), next; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime(); my $today = sprintf("%04d-%02d-%02d", $year+1900, $mon+1, $mday); (defined $mixmasters{$keyid}->{'created'} && ($today lt $mixmasters{$keyid}->{'created'})) and Echolot::Log::info("Mixmaster key for $remailer_address created in the future ($today < ".$mixmasters{$keyid}->{'created'}.")."), next; (defined $mixmasters{$keyid}->{'expires'} && ($mixmasters{$keyid}->{'expires'} lt $today)) and Echolot::Log::info("Mixmaster key for $remailer_address expired (".$mixmasters{$keyid}->{'expires'}." < $today)."), next; if ($remailer->{'address'} ne $remailer_address) { # Address mismatch -> Ignore reply and add $remailer_address to prospective addresses Echolot::Log::info("Remailer address mismatch $remailer->{'address'} vs $remailer_address. Adding latter to prospective remailers."); Echolot::Globals::get()->{'storage'}->add_prospective_address($remailer_address, 'self-capsstring-key', $remailer_address); } else { Echolot::Log::debug("Setting mix key for $remailer_address: $keyid"); Echolot::Globals::get()->{'storage'}->restore_ttl( $remailer->{'address'} ); Echolot::Globals::get()->{'storage'}->set_key( 'mix', $mixmasters{$keyid}->{'nick'}, $mixmasters{$keyid}->{'address'}, $mixmasters{$keyid}->{'key'}, $keyid, $mixmasters{$keyid}->{'version'}, $mixmasters{$keyid}->{'caps'}, $mixmasters{$keyid}->{'summary'}, $time); } }; return 1; }; sub parse_cpunk_key($$$) { my ($reply, $time, $remailer) = @_; my $GnuPG = new GnuPG::Interface; $GnuPG->call( Echolot::Config::get()->{'gnupg'} ) if (Echolot::Config::get()->{'gnupg'}); $GnuPG->options->hash_init( homedir => Echolot::Config::get()->{'gnupghome'} ); $GnuPG->options->meta_interactive( 0 ); my %cypherpunk; my @pgp_keys = ($reply =~ /^-----BEGIN \s PGP \s PUBLIC \s KEY \s BLOCK-----\r?\n (?:.+\r?\n)* \r?\n (?:[a-zA-Z0-9+\/=]*\r?\n)+ -----END \s PGP \s PUBLIC \s KEY \s BLOCK-----$/xmg ); for my $key (@pgp_keys) { my ( $stdin_fh, $stdout_fh, $stderr_fh, $status_fh, $handles ) = Echolot::Tools::make_gpg_fds(); my $pid = $GnuPG->wrap_call( commands => [qw{--with-colons --no-options --no-secmem-warning --no-default-keyring --fast-list-mode}], handles => $handles ); my ($stdout, $stderr, $status) = Echolot::Tools::readwrite_gpg($key, $stdin_fh, $stdout_fh, $stderr_fh, $status_fh); waitpid $pid, 0; ($stderr eq '') or Echolot::Log::info("GnuPG returned something in stderr: '$stderr' when checking key '$key'; So what?"); ($status eq '') or Echolot::Log::info("GnuPG returned something in status '$status' when checking key '$key': So what?"); my @included_keys = $stdout =~ /^pub:.*$/mg; (scalar @included_keys >= 2) && # FIXME handle more than one key per block nicely Echolot::Log::debug ("Cannot handle more than one key per block nicely (correctly) yet. Found ".(scalar @included_keys)." in one block from ".$remailer->{'address'}."."); for my $included_key (@included_keys) { my ($type, $keyid, $uid) = $included_key =~ /pub::\d+:(\d+):([0-9A-F]+):[^:]+:[^:]*:::([^:]+):/; (defined $uid) or Echolot::Log::info ("Unexpected format of '$included_key' by ".$remailer->{'address'}."; Skipping."), next; my ($address) = $uid =~ /<(.*?)>/; $cypherpunk{$keyid} = { address => $address, type => $type, key => $key # FIXME handle more than one key per block correctly }; }; }; for my $keyid (keys %cypherpunk) { my $remailer_address = $cypherpunk{$keyid}->{'address'}; if ($remailer->{'address'} ne $remailer_address) { # Address mismatch -> Ignore reply and add $remailer_address to prospective addresses Echolot::Log::info("Remailer address mismatch $remailer->{'address'} vs $remailer_address id key $keyid. Adding latter to prospective remailers."); Echolot::Globals::get()->{'storage'}->add_prospective_address($remailer_address, 'self-capsstring-key', $remailer_address); } else { Echolot::Globals::get()->{'storage'}->restore_ttl( $remailer->{'address'} ); # 1 .. RSA # 17 .. DSA if ($cypherpunk{$keyid}->{'type'} == 1 || $cypherpunk{$keyid}->{'type'} == 17 ) { Echolot::Log::debug("Setting cpunk key for $remailer_address: $keyid; type ".$cypherpunk{$keyid}->{'type'}); Echolot::Globals::get()->{'storage'}->set_key( (($cypherpunk{$keyid}->{'type'} == 1) ? 'cpunk-rsa' : (($cypherpunk{$keyid}->{'type'} == 17) ? 'cpunk-dsa' : 'ERROR')), $keyid, # as nick $cypherpunk{$keyid}->{'address'}, $cypherpunk{$keyid}->{'key'}, $keyid, 'N/A', 'N/A', 'N/A', $time); } else { Echolot::Log::info("$keyid from $remailer_address has algoid ".$cypherpunk{$keyid}->{'type'}.". Cannot handle those."); }; } }; return 1; }; sub remailer_key($$$) { my ($reply, $token, $time) = @_; my $cp_reply = $reply; $cp_reply =~ s/^- -/-/gm; # PGP Signed messages my ($id) = $token =~ /^key\.(\d+)$/; (defined $id) or Echolot::Log::info ("Returned token '$token' has no id at all."), return 0; my $remailer = Echolot::Globals::get()->{'storage'}->get_address_by_id($id); Echolot::Log::info("No remailer found for id '$id'."), return 0 unless defined $remailer; Echolot::Log::debug("Received remailer-keys reply for $remailer."), Echolot::Globals::get()->{'storage'}->not_a_remailer($id), return 1 if (is_not_a_remailer($reply)); Echolot::Thesaurus::save_thesaurus('key', $id, $reply); parse_mix_key($cp_reply, $time, $remailer); parse_cpunk_key($cp_reply, $time, $remailer); return 1; }; sub remailer_stats($$$) { my ($reply, $token, $time) = @_; my ($id) = $token =~ /^stats\.(\d+)$/; (defined $id) or Echolot::Log::info ("Returned token '$token' has no id at all."), return 0; my $remailer = Echolot::Globals::get()->{'storage'}->get_address_by_id($id); Echolot::Log::info("No remailer found for id '$id'."), return 0 unless defined $remailer; Echolot::Log::debug("Received remailer-stats reply for $remailer."), Echolot::Globals::get()->{'storage'}->not_a_remailer($id), return 1 if (is_not_a_remailer($reply)); Echolot::Thesaurus::save_thesaurus('stats', $id, $reply); }; sub remailer_help($$$) { my ($reply, $token, $time) = @_; my ($id) = $token =~ /^help\.(\d+)$/; (defined $id) or Echolot::Log::info ("Returned token '$token' has no id at all."), return 0; my $remailer = Echolot::Globals::get()->{'storage'}->get_address_by_id($id); Echolot::Log::info("No remailer found for id '$id'."), return 0 unless defined $remailer; Echolot::Log::debug("Received remailer-help reply for $remailer."), Echolot::Globals::get()->{'storage'}->not_a_remailer($id), return 1 if (is_not_a_remailer($reply)); Echolot::Thesaurus::save_thesaurus('help', $id, $reply); }; sub remailer_adminkey($$$) { my ($reply, $token, $time) = @_; my ($id) = $token =~ /^adminkey\.(\d+)$/; (defined $id) or Echolot::Log::info ("Returned token '$token' has no id at all."), return 0; my $remailer = Echolot::Globals::get()->{'storage'}->get_address_by_id($id); Echolot::Log::info("No remailer found for id '$id'."), return 0 unless defined $remailer; Echolot::Log::debug("Received remailer-adminkey reply for $remailer."), Echolot::Globals::get()->{'storage'}->not_a_remailer($id), return 1 if (is_not_a_remailer($reply)); Echolot::Thesaurus::save_thesaurus('adminkey', $id, $reply); }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Chain.pm0000644000175000017500000002120512422221767015464 0ustar weaselweaselpackage Echolot::Chain; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Chain - actual sending and receiving of Chain-Pings. =head1 DESCRIPTION This package provides functions for sending out and receiving chain-pings. =cut use strict; use English; use Echolot::Log; use Echolot::Pinger::Mix; use Echolot::Pinger::CPunk; my %INTENSIVE_CARE; sub do_mix_chainping($$$$$$$$) { my ($addr1, $type1, $keyid1, $addr2, $type2, $keyid2, $to, $body) = @_; ($type1 eq 'mix' && $type2 eq 'mix') or Echolot::Log::warn("both types should really be mix ($type1, $type2)."), return 0; my %key1 = Echolot::Globals::get()->{'storage'}->get_key($addr1, $type1, $keyid1); my %key2 = Echolot::Globals::get()->{'storage'}->get_key($addr2, $type2, $keyid2); Echolot::Pinger::Mix::ping( $body, $to, 0, [ $key1{'nick'} , $key2{'nick'} ], { $keyid1 => \%key1, $keyid2 => \%key2 } ) or return 0; return 1; }; sub do_cpunk_chainping($$$$$$$$) { my ($addr1, $type1, $keyid1, $addr2, $type2, $keyid2, $to, $body) = @_; my $keyhash = {}; if ($type1 ne 'cpunk-clear') { my %key = Echolot::Globals::get()->{'storage'}->get_key($addr1, $type1, $keyid1); $keyhash->{$keyid1} = \%key; }; if ($type2 ne 'cpunk-clear') { my %key = Echolot::Globals::get()->{'storage'}->get_key($addr2, $type2, $keyid2); $keyhash->{$keyid2} = \%key; }; Echolot::Pinger::CPunk::ping( $body, $to, 0, [ { address => $addr1, keyid => $keyid1, encrypt => ($type1 ne 'cpunk-clear'), pgp2compat => ($type1 eq 'cpunk-rsa') }, { address => $addr2, keyid => $keyid2, encrypt => ($type2 ne 'cpunk-clear'), pgp2compat => ($type2 eq 'cpunk-rsa') } ], $keyhash ) or return 0; return 1; }; sub do_chainping($$$$$$$) { my ($chaintype, $addr1, $type1, $key1, $addr2, $type2, $key2) = @_; my $now = time(); my $token = join(':', $chaintype, $addr1, $type1, $key1, $addr2, $type2, $key2, $now); my $mac = Echolot::Tools::make_mac($token); my $body = "chaintype: $chaintype\n". "remailer1: $addr1\n". "type1: $type1\n". "key1: $key1\n". "remailer2: $addr2\n". "type2: $type2\n". "key2: $key2\n". "sent: $now\n". "mac: $mac\n". Echolot::Tools::make_garbage(); $body = Echolot::Tools::crypt_symmetrically($body, 'encrypt'); my $to = Echolot::Tools::make_address('chainping'); if ($chaintype eq 'mix') { do_mix_chainping($addr1, $type1, $key1, $addr2, $type2, $key2, $to, $body); } elsif ($chaintype eq 'cpunk') { do_cpunk_chainping($addr1, $type1, $key1, $addr2, $type2, $key2, $to, $body); } else { Echolot::Log::warn("Don't know how to handle chain ping type $chaintype."); return 0; }; Echolot::Globals::get()->{'storage'}->register_chainpingout($chaintype, $addr1, $type1, $key1, $addr2, $type2, $key2, $now); return 1; }; sub send_pings($;$$) { return 1 unless Echolot::Config::get()->{'do_chainpings'}; my ($scheduled_for, $which1, $which2) = @_; $which1 = '' unless defined $which1; $which2 = '' unless defined $which2; my $call_intervall = Echolot::Config::get()->{'chainpinger_interval'}; my $send_every_n_calls = Echolot::Config::get()->{'chainping_every_nth_time'}; my $timemod = int ($scheduled_for / $call_intervall); my $this_call_id = $timemod % $send_every_n_calls; my $session_id = int ($scheduled_for / ($call_intervall * $send_every_n_calls)); # Same thing for Intensive Care -- yet unknown or already broken chains my $send_every_n_calls_ic = Echolot::Config::get()->{'chainping_ic_every_nth_time'}; my $timemod_ic = int ($scheduled_for / $call_intervall); my $this_call_id_ic = $timemod_ic % $send_every_n_calls_ic; my $session_id_ic = int ($scheduled_for / ($call_intervall * $send_every_n_calls_ic)); my @remailers = Echolot::Globals::get()->{'storage'}->get_addresses(); for my $chaintype (keys %{Echolot::Config::get()->{'which_chainpings'}}) { my @thisrems; for my $rem (@remailers) { next unless $rem->{'pingit'}; my $addr = $rem->{'address'}; my $type; my %supports = map { $_ => 1 } Echolot::Globals::get()->{'storage'}->get_types($addr); for my $thistype (@{Echolot::Config::get()->{'which_chainpings'}->{$chaintype}}) { $type = $thistype, last if $supports{$thistype}; }; next unless $type; my $key; my $latest = 0; for my $keyid (Echolot::Globals::get()->{'storage'}->get_keys($addr, $type)) { my %key = Echolot::Globals::get()->{'storage'}->get_key($addr, $type, $keyid); $key = $keyid, $latest = $key{'last_update'} if $latest < $key{'last_update'}; }; push @thisrems, { addr => $addr, type => $type, key => $key }; }; for my $rem1 (@thisrems) { my $addr1 = $rem1->{'addr'}; next unless ( $which1 eq 'all' || $which1 eq $addr1 || $which1 eq ''); my $type1 = $rem1->{'type'}; my $key1 = $rem1->{'key'}; for my $rem2 (@thisrems) { my $addr2 = $rem2->{'addr'}; next if $rem1 eq $rem2 && (! ($which1 eq $addr2 && $which2 eq $addr2)); next unless ( $which2 eq 'all' || $which2 eq $addr2 || $which2 eq ''); my $type2 = $rem2->{'type'}; my $key2 = $rem2->{'key'}; my $call_id = Echolot::Tools::makeShortNumHash($addr1.$addr2.$chaintype.$session_id ) % $send_every_n_calls; my $call_id_ic = Echolot::Tools::makeShortNumHash($addr1.$addr2.$chaintype.$session_id_ic) % $send_every_n_calls_ic; next unless ( (($which1 eq $addr1 || $which1 eq 'all' ) && ($which2 eq $addr2 || $which2 eq 'all')) || (($which1 eq '' && $which2 eq '') && ( $this_call_id eq $call_id || (defined $INTENSIVE_CARE{$chaintype}->{$addr1.' '.$addr2} && $this_call_id_ic eq $call_id_ic)))); Echolot::Log::debug("chainping calling $chaintype, $addr1 ($type1, $key1) - $addr2 ($type2, $key2)"); do_chainping($chaintype, $addr1, $type1, $key1, $addr2, $type2, $key2); }; }; }; return 1; }; sub set_intensive_care($@) { my ($chaintype, $intensive_care) = @_; %{$INTENSIVE_CARE{$chaintype}} = map { ($_->{'addr1'}.' '.$_->{'addr2'}) => $_->{'reason'} } @$intensive_care; if (scalar @$intensive_care) { Echolot::Log::debug("intensive care $chaintype:\n" . join("\n", sort { $a cmp $b } map { "$_: $INTENSIVE_CARE{$chaintype}->{$_}" } keys %{$INTENSIVE_CARE{$chaintype}} )); } else { Echolot::Log::debug("intensive care $chaintype: (none)"); }; }; sub receive($$$$) { my ($header, $msg, $token, $timestamp) = @_; my $now = time(); my $body; if ($msg =~ /^-----BEGIN PGP MESSAGE-----/m) { # work around borken middleman remailers that have a problem with some # sort of end of line characters and randhopping them through reliable # remailers.. # they add an empty line between each usefull line $msg =~ s/(\r?\n)\r?\n/$1/g if ($msg =~ /^-----BEGIN PGP MESSAGE-----\r?\n\r?\n/m); $body = Echolot::Tools::crypt_symmetrically($msg, 'decrypt'); }; $body = $msg unless defined $body; my ($chaintype) = $body =~ /^chaintype: (.*)$/m; my ($addr1) = $body =~ /^remailer1: (.*)$/m; my ($type1) = $body =~ /^type1: (.*)$/m; my ($key1) = $body =~ /^key1: (.*)$/m; my ($addr2) = $body =~ /^remailer2: (.*)$/m; my ($type2) = $body =~ /^type2: (.*)$/m; my ($key2) = $body =~ /^key2: (.*)$/m; my ($sent) = $body =~ /^sent: (.*)$/m; my ($mac) = $body =~ /^mac: (.*)$/m; my @values = ($chaintype, $addr1, $type1, $key1, $addr2, $type2, $key2, $sent, $mac); my $cleanstring = join ":", map { defined() ? $_ : "undef" } @values; (grep { ! defined() } @values) and Echolot::Log::warn("Received chainping at $timestamp has undefined values: $cleanstring."), return 0; pop @values; Echolot::Tools::verify_mac(join(':', @values), $mac) or Echolot::Log::warn("Received chainping at $timestamp has wrong mac; $cleanstring."), return 0; Echolot::Globals::get()->{'storage'}->register_chainpingdone($chaintype, $addr1, $type1, $key1, $addr2, $type2, $key2, $sent, $now - $sent) or return 0; return 1; }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Storage/0002755000175000017500000000000012422221767015512 5ustar weaselweaselecholot-2.1.9/Echolot/Storage/File.pm0000644000175000017500000016224012422221767016732 0ustar weaselweaselpackage Echolot::Storage::File; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Storage::File - Storage backend for echolot =head1 DESCRIPTION This package provides several functions for data storage for echolot. =over =cut use strict; use Data::Dumper; use IO::Handle; use English; use Carp; use Fcntl ':flock'; # import LOCK_* constants #use Fcntl ':seek'; # import SEEK_* constants use POSIX; # import SEEK_* constants (older perls don't have SEEK_ in Fcntl) use Echolot::Tools; use Echolot::Log; my $CONSTANTS = { 'metadatafile' => 'metadata' }; delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; my $METADATA_VERSION = 1; =item B (I<%args>) Creates a new storage backend object. args keys: =over =item I The basedir where this module may store it's configuration and pinging data. =back =cut sub new { my ($class, %params) = @_; my $self = {}; bless $self, $class; $self->{'METADATA_FILE_IS_NEW'} = 0; defined($params{'datadir'}) or confess ('No datadir option passed to new'); $self->{'datadir'} = $params{'datadir'}; $self->{'DELAY_COMMIT'} = 0; $self->delay_commit(); $self->metadata_open() or confess ('Opening Metadata failed. Exiting'); $self->metadata_read() or confess ('Reading Metadata from Storage failed. Exiting'); $self->pingdata_open() or confess ('Opening Ping files failed. Exiting'); $self->chainpingdata_open() or confess ('Opening Ping files failed. Exiting'); $self->enable_commit(); return $self; }; =item $storage->B( ) Write metadata unless B is set. =cut sub commit($) { my ($self) = @_; if ($self->{'DELAY_COMMIT'}) { $self->{'COMMIT_PENDING'} = 1; return; }; $self->metadata_write(); $self->{'COMMIT_PENDING'} = 0; }; =item $storage->B( ) Increase B by one. =cut sub delay_commit($) { my ($self) = @_; $self->{'DELAY_COMMIT'}++; }; =item $storage->B( I<$set_> ) Decrease B by one and call C if B is zero and I<$set_pending> is true. =cut sub enable_commit($;$) { my ($self, $set_pending) = @_; $self->{'DELAY_COMMIT'}--; $self->commit() if (($self->{'COMMIT_PENDING'} || (defined $set_pending && $set_pending)) && ! $self->{'DELAY_COMMIT'}); }; =item $storage->B( ) Shut down cleanly. =cut sub finish($) { my ($self) = @_; $self->pingdata_close(); $self->chainpingdata_close(); $self->metadata_write(); $self->metadata_close(); }; =item $storage->B( ) Open metadata. Returns 1 on success, undef on errors. =cut sub metadata_open($) { my ($self) = @_; $self->{'METADATA_FH'} = new IO::Handle; my $filename = $self->{'datadir'} .'/'. $CONSTANTS->{'metadatafile'}; if ( -e $filename ) { open($self->{'METADATA_FH'}, '+<' . $filename) or Echolot::Log::warn("Cannot open $filename for reading: $!."), return undef; } else { $self->{'METADATA_FILE_IS_NEW'} = 1; open($self->{'METADATA_FH'}, '+>' . $filename) or Echolot::Log::warn("Cannot open $filename for reading: $!."), return undef; }; flock($self->{'METADATA_FH'}, LOCK_EX) or Echolot::Log::warn("Cannot get exclusive lock on $filename: $!."), return undef; return 1; }; =item $storage->B( ) Close metadata. Returns 1 on success, undef on errors. =cut sub metadata_close($) { my ($self) = @_; flock($self->{'METADATA_FH'}, LOCK_UN) or Echolot::Log::warn("Error when releasing lock on metadata file: $!."), return undef; close($self->{'METADATA_FH'}) or Echolot::Log::warn("Error when closing metadata file: $!."), return undef; return 1; }; =item $storage->B( ) Write metadata. Returns 1 on success, undef on errors. =cut sub metadata_read($) { my ($self) = @_; if ($self->{'METADATA_FILE_IS_NEW'}) { $self->{'METADATA'}->{'version'} = $METADATA_VERSION; $self->{'METADATA'}->{'addresses'} = {}; $self->{'METADATA'}->{'remailers'} = {}; $self->{'METADATA_FILE_IS_NEW'} = 0; $self->commit(); } else { $self->{'METADATA'} = (); seek($self->{'METADATA_FH'}, 0, SEEK_SET) or Echolot::Log::warn("Cannot seek to start of metadata file: $!."), return 0; { local $/ = undef; my $fh = $self->{'METADATA_FH'}; my $metadata_code = <$fh>; ($metadata_code) = $metadata_code =~ /^(.*)$/s; my $METADATA; eval ($metadata_code); $self->{'METADATA'} = $METADATA; }; $EVAL_ERROR and confess("Error when reading from metadata file: $EVAL_ERROR"), return undef; defined($self->{'METADATA'}->{'version'}) or confess("Stored data lacks version header"), return undef; ($self->{'METADATA'}->{'version'} == ($METADATA_VERSION)) or Echolot::Log::warn("Metadata version mismatch ($self->{'METADATA'}->{'version'} vs. $METADATA_VERSION)."), return undef; }; defined($self->{'METADATA'}->{'secret'}) or $self->{'METADATA'}->{'secret'} = Echolot::Tools::make_random ( 16, armor => 1 ), $self->commit(); return 1; }; =item $storage->B( ) Write metadata. Returns 1 on success, undef on errors. =cut sub metadata_write($) { my ($self) = @_; my $data = Data::Dumper->Dump( [ $self->{'METADATA'} ], [ 'METADATA' ] ); my $fh = $self->{'METADATA_FH'}; seek($fh, 0, SEEK_SET) or Echolot::Log::warn("Cannot seek to start of metadata file: $!."), return undef; truncate($fh, 0) or Echolot::Log::warn("Cannot truncate metadata file to zero length: $!."), return undef; print($fh "# vim:set syntax=perl:\n") or Echolot::Log::warn("Error when writing to metadata file: $!."), return undef; print($fh $data) or Echolot::Log::warn("Error when writing to metadata file: $!."), return undef; $fh->flush(); return 1; }; =item $storage->B( ) Rotate metadata files and create a backup. Returns 1 on success, undef on errors. =cut sub metadata_backup($) { my ($self) = @_; my $filename = $self->{'datadir'} .'/'. $CONSTANTS->{'metadatafile'}; for (my $i=Echolot::Config::get()->{'metadata_backup_count'} - 1; $i>=0; $i--) { rename ($filename.'.'.($i) , $filename.'.'.($i+1)); rename ($filename.'.'.($i).'.gz', $filename.'.'.($i+1).'.gz'); }; $filename .= '.1'; my $data = Data::Dumper->Dump( [ $self->{'METADATA'} ], [ 'METADATA' ] ); my $fh = new IO::Handle; open ($fh, '>'.$filename) or Echolot::Log::warn("Cannot open $filename for writing: $!."), return undef; print($fh "# vim:set syntax=perl:\n") or Echolot::Log::warn("Error when writing to metadata file: $!."), return undef; print($fh $data) or Echolot::Log::warn("Error when writing to metadata file: $!."), return undef; $fh->flush(); close($fh) or Echolot::Log::warn("Error when closing metadata file: $!."), return undef; if (Echolot::Config::get()->{'gzip'}) { system(Echolot::Config::get()->{'gzip'}, $filename) and Echolot::Log::warn("Gziping $filename failed."), return undef; }; return 1; }; =item $storage->B( I<$remailer_addr>, I<$type>, I<$key> ) Open the pingdata file for the I<$remailer_addr>, I<$type>, and I<$key>. Returns 1 on success, undef on errors. =cut sub pingdata_open_one($$$$) { my ($self, $remailer_addr, $type, $key) = @_; defined ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}) or Echolot::Log::cluck ("$remailer_addr does not exist in Metadata."), return undef; defined ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}) or Echolot::Log::cluck ("$remailer_addr has no keys in Metadata."), return undef; defined ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}) or Echolot::Log::cluck ("$remailer_addr type $type does not exist in Metadata."), return undef; defined ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}->{$key}) or Echolot::Log::cluck ("$remailer_addr type $type key $key does not exist in Metadata."), return undef; my $basename = $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}->{$key}->{'stats'}; defined($basename) or $basename = $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}->{$key}->{'stats'} = $remailer_addr.'.'.$type.'.'.$key.'.'.time.'.'.$PROCESS_ID.'_'.Echolot::Globals::get()->{'internalcounter'}++, $self->commit(); my $filename = $self->{'datadir'} .'/'. $basename; for my $direction ('out', 'done') { my $fh = new IO::Handle; if ( -e $filename.'.'.$direction ) { open($fh, '+<' . $filename.'.'.$direction) or Echolot::Log::warn("Cannot open $filename.$direction for reading: $!."), return undef; $self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}->{$direction} = $fh; } else { open($fh, '+>' . $filename.'.'.$direction) or Echolot::Log::warn("Cannot open $filename.$direction for reading: $!."), return undef; $self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}->{$direction} = $fh; }; flock($fh, LOCK_EX) or Echolot::Log::warn("Cannot get exclusive lock on $remailer_addr $type $key $direction pings: $!."), return undef; }; return 1; }; =item $storage->B( ) Open all pingdata files. Returns 1. =cut sub pingdata_open($) { my ($self) = @_; for my $remailer_addr ( keys %{$self->{'METADATA'}->{'remailers'}} ) { for my $type ( keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}} ) { for my $key ( keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}} ) { $self->pingdata_open_one($remailer_addr, $type, $key); }; }; }; return 1; }; =item $storage->B( I<$remailer_addr>, I<$type>, I<$key>, I<$direction>, I<$oknodo> ) Return the FH for the pingdata file of I<$remailer_addr>, I<$type>, I<$key>, and I<$direction>. If $ is set, the absense of a defined filehandle does not cause it to be opened/created. Instead -1 is returned. Returns undef on error; =cut sub get_ping_fh($$$$$;$) { my ($self, $remailer_addr, $type, $key, $direction, $oknodo) = @_; defined ($self->{'METADATA'}->{'addresses'}->{$remailer_addr}) or Echolot::Log::cluck("$remailer_addr does not exist in Metadata."), return undef; my $fh = $self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}->{$direction}; unless (defined $fh) { return -1 if (defined $oknodo && $oknodo); $self->pingdata_open_one($remailer_addr, $type, $key), $fh = $self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}->{$direction}; defined ($fh) or Echolot::Log::warn ("$remailer_addr; type=$type; key=$key has no assigned filehandle for $direction pings."), return undef; } return $fh; }; =item $storage->B( I<$remailer_addr>, I<$type>, I<$key> ) Close the pingdata file for the I<$remailer_addr>, I<$type>, and I<$key>. Returns 1 on success, undef on errors. =cut sub pingdata_close_one($$$$;$) { my ($self, $remailer_addr, $type, $key, $delete) = @_; for my $direction ( keys %{$self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}} ) { my $fh = $self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}->{$direction}; flock($fh, LOCK_UN) or Echolot::Log::warn("Error when releasing lock on $remailer_addr type $type key $key direction $direction pings: $!."), return undef; close ($fh) or Echolot::Log::warn("Error when closing $remailer_addr type $type key $key direction $direction pings: $!."), return undef; if ((defined $delete) && ($delete eq 'delete')) { my $basename = $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}->{$key}->{'stats'}; my $filename = $self->{'datadir'} .'/'. $basename; unlink ($filename.'.'.$direction) or carp ("Cannot unlink $filename.'.'.$direction: $!"); }; }; delete $self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}; delete $self->{'PING_FHS'}->{$remailer_addr}->{$type} unless (scalar keys %{$self->{'PING_FHS'}->{$remailer_addr}->{$type}}); delete $self->{'PING_FHS'}->{$remailer_addr} unless (scalar keys %{$self->{'PING_FHS'}->{$remailer_addr}}); return 1; }; =item $storage->B( ) Close all pingdata files. Returns 1 on success, undef on errors. =cut sub pingdata_close($) { my ($self) = @_; for my $remailer_addr ( keys %{$self->{'PING_FHS'}} ) { for my $type ( keys %{$self->{'PING_FHS'}->{$remailer_addr}} ) { for my $key ( keys %{$self->{'PING_FHS'}->{$remailer_addr}->{$type}} ) { $self->pingdata_close_one($remailer_addr, $type, $key) or Echolot::Log::debug("Error when calling pingdata_close_one with $remailer_addr type $type key $key."), return undef; }; }; }; return 1; }; =item $storage->B( I<$remailer_addr>, I<$type>, I<$key>, I<$direction> ) Return an array of ping data for I<$remailer_addr>, I<$type>, I<$key>, and I<$direction>. If direction is B then it's an array of scalar (the send timestamps). If direction is B then it's an array of array references each having two items: the send time and the latency. Returns undef on error; =cut sub get_pings($$$$$) { my ($self, $remailer_addr, $type, $key, $direction) = @_; my @pings; my $fh = $self->get_ping_fh($remailer_addr, $type, $key, $direction, 1); (defined $fh) or Echolot::Log::cluck ("$remailer_addr; type=$type; key=$key has no assigned filehandle for out pings."); ($fh == -1) and Echolot::Log::info ("$remailer_addr; type=$type; key=$key has no assigned filehandle for $direction pings (key has expired, or not available yet)."), return (); seek($fh, 0, SEEK_SET) or Echolot::Log::warn("Cannot seek to start of $remailer_addr type $type key $key direction $direction pings: $! ($fh)."), return undef; if ($direction eq 'out') { @pings = map {chomp; $_; } <$fh>; } elsif ($direction eq 'done') { @pings = map {chomp; my @arr = split (/\s+/, $_, 2); \@arr; } <$fh>; } else { confess("What the hell am I doing here? $remailer_addr; $type; $key; $direction"), return undef; }; return @pings; }; =item $storage->B( I<$remailer_addr>, I<$type>, I<$key>, I<$sent_time> ) Register a ping sent to I<$remailer_addr>, I<$type>, I<$key> and I$. Returns 1 on success, undef on errors. =cut sub register_pingout($$$$$) { my ($self, $remailer_addr, $type, $key, $sent_time) = @_; my $fh = $self->get_ping_fh($remailer_addr, $type, $key, 'out') or Echolot::Log::cluck ("$remailer_addr; type=$type; key=$key has no assigned filehandle for out pings."), return undef; seek($fh, 0, SEEK_END) or Echolot::Log::warn("Cannot seek to end of $remailer_addr; type=$type; key=$key; out pings: $!."), return undef; print($fh $sent_time."\n") or Echolot::Log::warn("Error when writing to $remailer_addr; type=$type; key=$key; out pings: $!."), return undef; $fh->flush(); Echolot::Log::debug("registering pingout for $remailer_addr ($type; $key)."); return 1; }; =item $storage->B( I<$remailer_addr>, I<$type>, I<$key>, I<$sent_time>, I<$latency> ) Register that the ping sent to I<$remailer_addr>, I<$type>, I<$key> at I$ has returned with latency I<$latency>. Returns 1 on success, undef on errors. =cut sub register_pingdone($$$$$$) { my ($self, $remailer_addr, $type, $key, $sent_time, $latency) = @_; defined ($self->{'METADATA'}->{'addresses'}->{$remailer_addr}) or Echolot::Log::warn ("$remailer_addr does not exist in Metadata."), return undef; my @outpings = $self->get_pings($remailer_addr, $type, $key, 'out'); my $origlen = scalar (@outpings); @outpings = grep { $_ != $sent_time } @outpings; ($origlen == scalar (@outpings)) and Echolot::Log::info("No ping outstanding for $remailer_addr, $key, ".(scalar localtime $sent_time)."."), return 1; # write ping to done my $fh = $self->get_ping_fh($remailer_addr, $type, $key, 'done') or Echolot::Log::cluck ("$remailer_addr; type=$type; key=$key has no assigned filehandle for done pings."), return undef; seek($fh, 0, SEEK_END) or Echolot::Log::warn("Cannot seek to end of $remailer_addr done pings: $!."), return undef; print($fh $sent_time." ".$latency."\n") or Echolot::Log::warn("Error when writing to $remailer_addr done pings: $!."), return undef; $fh->flush(); # rewrite outstanding pings $fh = $self->get_ping_fh($remailer_addr, $type, $key, 'out') or Echolot::Log::cluck ("$remailer_addr; type=$type; key=$key has no assigned filehandle for out pings."), return undef; seek($fh, 0, SEEK_SET) or Echolot::Log::warn("Cannot seek to start of outgoing pings file for remailer $remailer_addr; key=$key: $!."), return undef; truncate($fh, 0) or Echolot::Log::warn("Cannot truncate outgoing pings file for remailer $remailer_addr; key=$key file to zero length: $!."), return undef; print($fh (join "\n", @outpings), (scalar @outpings ? "\n" : '') ) or Echolot::Log::warn("Error when writing to outgoing pings file for remailer $remailer_addr; key=$key file: $!."), return undef; $fh->flush(); Echolot::Log::debug("registering pingdone from ".(scalar localtime $sent_time)." with latency $latency for $remailer_addr ($type; $key)."); return 1; }; =item $storage->B( I<$chaintype> ) Open the pingdata file for I<$chaintype> type chain pings. Returns 1 on success, undef on errors. =cut sub chainpingdata_open_one($$) { my ($self, $type) = @_; my $filename = $self->{'datadir'} .'/chainpings.'.$type; for my $direction ('out', 'done') { my $fh = new IO::Handle; if ( -e $filename.'.'.$direction ) { open($fh, '+<' . $filename.'.'.$direction) or Echolot::Log::warn("Cannot open $filename.$direction for reading: $!."), return undef; $self->{'CHAINPING_FHS'}->{$type}->{$direction} = $fh; } else { open($fh, '+>' . $filename.'.'.$direction) or Echolot::Log::warn("Cannot open $filename.$direction for reading: $!."), return undef; $self->{'CHAINPING_FHS'}->{$type}->{$direction} = $fh; }; flock($fh, LOCK_EX) or Echolot::Log::warn("Cannot get exclusive lock on $filename.$direction pings: $!."), return undef; }; return 1; }; =item $storage->B( ) Open all chainpingdata files. Returns 1. =cut sub chainpingdata_open($) { my ($self) = @_; for my $type ( keys %{Echolot::Config::get()->{'which_chainpings'}} ) { $self->chainpingdata_open_one($type); }; return 1; }; =item $storage->B( I<$type>, I<$direction> ) Return the FH for the chainpingdata file of I<$type>, and I<$direction>. Returns undef on error; =cut sub get_chainping_fh($$$) { my ($self, $type, $direction) = @_; my $fh = $self->{'CHAINPING_FHS'}->{$type}->{$direction}; defined ($fh) or $self->chainpingdata_open_one($type), $fh = $self->{'CHAINPING_FHS'}->{$type}->{$direction}; defined ($fh) or Echolot::Log::warn ("chainping $type has no assigned filehandle for $direction chainpings."), return undef; return $fh; }; =item $storage->B( I<$type> ) Close the chainpingdata file for I<$type>. Returns 1 on success, undef on errors. =cut sub chainpingdata_close_one($) { my ($self, $type) = @_; for my $direction ( keys %{$self->{'CHAINPING_FHS'}->{$type}} ) { my $fh = $self->{'CHAINPING_FHS'}->{$type}->{$direction}; flock($fh, LOCK_UN) or Echolot::Log::warn("Error when releasing lock on $type direction $direction chainpings: $!."), return undef; close ($fh) or Echolot::Log::warn("Error when closing $type direction $direction chainpings: $!."), return undef; }; delete $self->{'CHAINPING_FHS'}->{$type}; return 1; }; =item $storage->B( ) Close all chainpingdata files. Returns 1 on success, undef on errors. =cut sub chainpingdata_close($) { my ($self) = @_; for my $type ( keys %{$self->{'CHAINPING_FHS'}} ) { $self->chainpingdata_close_one($type) or Echolot::Log::debug("Error when calling chainpingdata_close_one with type $type."), return undef; }; return 1; }; =item $storage->B( I<$chaintype> ) Return chainping data for I<$chaintype>. The result is a reference to a hash having two entries: out and done. Each of them is a reference to an array of single pings. Each ping is a hash reference with the hash having the keys B, B, B, B, B, B, B, and in case of received pings B. Out currently includes all sent pings - also those that allready arrived. This is different from the get_pings() function above. Returns undef on error. =cut sub get_chainpings($$) { my ($self, $chaintype) = @_; my $fh = $self->get_chainping_fh($chaintype, 'out') or Echolot::Log::warn ("have no assigned filehandle for $chaintype out chainpings."), return undef; seek($fh, 0, SEEK_SET) or Echolot::Log::warn("Cannot seek to start of $chaintype out chainpings $!."), return undef; my @out = map { chomp; my @a = split; Echolot::Log::warn("'$_' has not 7 fields") if (scalar @a < 7); { sent => $a[0], addr1 => $a[1], type1 => $a[2], key1 => $a[3], addr2 => $a[4], type2 => $a[5], key2 => $a[6] } } <$fh>; my %sent = map { my $a = $_; my $key = join (' ', map ({ $a->{$_} } qw{sent addr1 type1 key1 addr2 type2 key2})); $key => 1 } @out; $fh = $self->get_chainping_fh($chaintype, 'done') or Echolot::Log::warn ("assigned filehandle for $chaintype done chainpings."), return undef; seek($fh, 0, SEEK_SET) or Echolot::Log::warn("Cannot seek to start of $chaintype done chainpings $!."), return undef; my @done = grep { # Only list things that actually got sent - and only once my $a = $_; my $key = join (' ', map ({ $a->{$_} } qw{sent addr1 type1 key1 addr2 type2 key2})); my $exists = exists $sent{$key}; delete $sent{$key}; $exists } map { chomp; my @a = split; { sent => $a[0], addr1 => $a[1], type1 => $a[2], key1 => $a[3], addr2 => $a[4], type2 => $a[5], key2 => $a[6], lat => $a[7] } } <$fh>; return { out => \@out, done => \@done }; }; =item $storage->B( I<$chaintype>, I<$addr1>, I<$type1>, I<$key1>, I<$addr2>, I<$type2>, I<$key2>, I<$sent_time> > Register a chain ping of type I<$chaintype> sent through I<$addr1> (I<$type1>, I<$key1>) and I<$addr2> (I<$type2>, I<$key2>) at I$. Returns 1 on success, undef on errors. =cut sub register_chainpingout($$$$$$$$$) { my ($self, $chaintype, $addr1, $type1, $key1, $addr2, $type2, $key2, $sent_time) = @_; my $fh = $self->get_chainping_fh($chaintype, 'out') or Echolot::Log::cluck ("chaintype $chaintype/out has no assigned filehandle."), return undef; seek($fh, 0, SEEK_END) or Echolot::Log::warn("Cannot seek to end of chaintype $chaintype out pings: $!."), return undef; print($fh join(' ', $sent_time, $addr1, $type1, $key1, $addr2, $type2, $key2)."\n") or Echolot::Log::warn("Error when writing to chaintype $chaintype out pings: $!."), return undef; $fh->flush(); Echolot::Log::debug("registering chainping $chaintype out through $addr1 ($type1; $key1) to $addr2 ($type2; $key2)."); return 1; }; =item $storage->B( I<$chaintype>, I<$addr1>, I<$type1>, I<$key1>, I<$addr2>, I<$type2>, I<$key2>, I<$sent_time>, I<$latency> ) Register that the chain ping of type I<$chaintype> sent through I<$addr1> (I<$type1>, I<$key1>) and I<$addr2> (I<$type2>, I<$key2>) at I$ has returned with latency I<$latency>. Returns 1 on success, undef on errors. =cut sub register_chainpingdone($$$$$$$$$$) { my ($self, $chaintype, $addr1, $type1, $key1, $addr2, $type2, $key2, $sent_time, $latency) = @_; # write ping to done my $fh = $self->get_chainping_fh($chaintype, 'done') or Echolot::Log::cluck ("chaintype $chaintype/done has no assigned filehandle."), return undef; seek($fh, 0, SEEK_END) or Echolot::Log::warn("Cannot seek to end of $chaintype/done pings: $!."), return undef; print($fh join(' ', $sent_time, $addr1, $type1, $key1, $addr2, $type2, $key2, $latency)."\n") or Echolot::Log::warn("Error when writing to $chaintype/done pings: $!."), return undef; $fh->flush(); Echolot::Log::debug("registering chainpingdone from ".(scalar localtime $sent_time)." with latency $latency chainping $chaintype out through $addr1 ($type1; $key1) to $addr2 ($type2; $key2)."); return 1; }; =item $storage->B( I<$addr>, I<$reason>, I<$additional> ) Add I<$addr> to the list of prospective remailers with I<$reason> and I<$additional> information. Returns 1. =cut sub add_prospective_address($$$$) { my ($self, $addr, $reason, $additional) = @_; return 1 if defined $self->{'METADATA'}->{'addresses'}->{$addr}; push @{ $self->{'METADATA'}->{'prospective_addresses'}{$addr} }, time().'; '. $reason. '; '. $additional; $self->commit(); return 1; }; =item $storage->B( ) Commit prospective remailers to the list of remailers we know. Returns 1. =cut sub commit_prospective_address($) { my ($self) = @_; $self->delay_commit(); for my $addr (keys %{$self->{'METADATA'}->{'prospective_addresses'}}) { if (defined $self->{'METADATA'}->{'addresses'}->{$addr}) { delete $self->{'METADATA'}->{'prospective_addresses'}->{$addr}; next; }; # expire old prospective addresses while (@{ $self->{'METADATA'}->{'prospective_addresses'}->{$addr} }) { my ($time, $reason, $additional) = split(/;\s*/, $self->{'METADATA'}->{'prospective_addresses'}->{$addr}->[0] ); if ($time < time() - Echolot::Config::get()->{'prospective_addresses_ttl'} ) { shift @{ $self->{'METADATA'}->{'prospective_addresses'}->{$addr} }; } else { last; }; }; unless (scalar @{ $self->{'METADATA'}->{'prospective_addresses'}->{$addr} }) { delete $self->{'METADATA'}->{'prospective_addresses'}->{$addr}; next; }; my %reasons; for my $line ( @{ $self->{'METADATA'}->{'prospective_addresses'}->{$addr} } ) { my ($time, $reason, $additional) = split(/;\s*/, $line); push @{ $reasons{$reason} }, $additional; }; # got prospective by reply to own remailer-conf or remailer-key request if ( defined $reasons{'self-capsstring-conf'} || defined $reasons{'self-capsstring-key'} ) { Echolot::Log::notice("$addr is used because of direct conf or key reply"); $self->add_address($addr); delete $self->{'METADATA'}->{'prospective_addresses'}->{$addr}; next; } # was listed in reliable's remailer-conf reply; @adds holds suggestors my @adds; push @adds, @{ $reasons{'reliable-caps-reply-type1'} } if defined $reasons{'reliable-caps-reply-type1'}; push @adds, @{ $reasons{'reliable-caps-reply-type2'} } if defined $reasons{'reliable-caps-reply-type2'}; if (scalar @adds) { my %unique; @adds = grep { ! $unique{$_}++; } @adds; if (scalar @adds >= Echolot::Config::get()->{'reliable_auto_add_min'} ) { Echolot::Log::notice("$addr is recommended by ". join(', ', @adds) . "."); $self->add_address($addr); delete $self->{'METADATA'}->{'prospective_addresses'}->{$addr}; next; }; }; }; $self->enable_commit(1); return 1; }; =item $storage->B( I<$addr> ) Get a reference to a hash of information of the remailers with address I<$addr>. The hash has the following keys: =over =item status =item id =item address =item fetch =item showit =item pingit =item ttl =item resurrection_ttl =back Returns undef on errors. =cut sub get_address($$) { my ($self, $addr) = @_; defined ($self->{'METADATA'}->{'addresses'}->{$addr}) or Echolot::Log::cluck ("$addr does not exist in Metadata."), return undef; my $result = { status => $self->{'METADATA'}->{'addresses'}->{$addr}->{'status'}, id => $self->{'METADATA'}->{'addresses'}->{$addr}->{'id'}, address => $_, fetch => $self->{'METADATA'}->{'addresses'}->{$addr}->{'fetch'}, showit => $self->{'METADATA'}->{'addresses'}->{$addr}->{'showit'}, pingit => $self->{'METADATA'}->{'addresses'}->{$addr}->{'pingit'}, ttl => $self->{'METADATA'}->{'addresses'}->{$addr}->{'ttl'}, resurrection_ttl => $self->{'METADATA'}->{'addresses'}->{$addr}->{'resurrection_ttl'}, }; return $result; }; =item $storage->B( ) Get an array of all remailers we know about. Each element in this array is a hash reference as returned by C. =cut sub get_addresses($) { my ($self) = @_; my @addresses = keys %{$self->{'METADATA'}->{'addresses'}}; my @return_data = map { $self->get_address($_); } @addresses; return @return_data; }; =item $storage->B( I<$addr> ) Adds a remailer with address I<$addr>. B, B, and B are set to the values configured for new remailers. Assign the remailer status B and a new unique ID. See L for more information on this. Returns 1. =cut sub add_address($$) { my ($self, $addr) = @_; my @all_addresses = $self->get_addresses(); my $maxid = $self->{'METADATA'}->{'addresses_maxid'}; unless (defined $maxid) { $maxid = 0; for my $addr (@all_addresses) { if ($addr->{'id'} > $maxid) { $maxid = $addr->{'id'}; }; }; }; Echolot::Log::notice("Adding address $addr."); my $remailer = { id => $maxid + 1, status => 'active', ttl => Echolot::Config::get()->{'addresses_default_ttl'}, fetch => Echolot::Config::get()->{'fetch_new'}, pingit => Echolot::Config::get()->{'ping_new'}, showit => Echolot::Config::get()->{'show_new'}, }; $self->{'METADATA'}->{'addresses'}->{$addr} = $remailer; $self->{'METADATA'}->{'addresses_maxid'} = $maxid+1; $self->commit(); return 1; }; =item $storage->B( I<@args> ) @args is supposed to have two elements: I<$address>, and I<$setting>. Set verious options for the remailer with address $I<$address>. I<$setting> has to be of the form C. Recognised keys are B, B, and B. Acceptable values are B and B. See L for the meaning of these settings. Returns 1, undef on error. =cut sub set_stuff($@) { my ($self, @args) = @_; my ($addr, $setting) = @args; my $args = join(', ', @args); defined ($addr) or Echolot::Log::cluck ("Could not get address for '$args'."), return undef; defined ($setting) or Echolot::Log::cluck ("Could not get setting for '$args'."), return undef; defined ($self->{'METADATA'}->{'addresses'}->{$addr}) or Echolot::Log::warn ("Address $addr does not exist."), return undef; if ($setting =~ /^(pingit|fetch|showit)=(on|off)$/) { my $option = $1; my $value = $2; Echolot::Log::info("Setting $option to $value for $addr"); $self->{'METADATA'}->{'addresses'}->{$addr}->{$option} = ($value eq 'on'); } else { Echolot::Log::warn ("Don't know what to do with '$setting' for $addr."), return undef; } $self->commit(); return 1; }; =item $storage->B( I<$id> ) Return the address for the remailer with id I<$id>. Return undef if there is no remailer with that id. =cut sub get_address_by_id($$) { my ($self, $id) = @_; my @addresses = grep {$self->{'METADATA'}->{'addresses'}->{$_}->{'id'} == $id} keys %{$self->{'METADATA'}->{'addresses'}}; return undef unless (scalar @addresses); if (scalar @addresses >= 2) { Echolot::Log::cluck("Searching for address by id '$id' gives more than one result."); }; my %return_data = %{$self->{'METADATA'}->{'addresses'}->{$addresses[0]}}; $return_data{'address'} = $addresses[0]; return \%return_data; }; =item $storage->B( I<$address> ) Decrease the TTL (Time To Live) for remailer with address I<$address> by one. If it hits zero the remailer's status is set to B. Returns 1, undef on error. =cut sub decrease_ttl($$) { my ($self, $address) = @_; defined ($self->{'METADATA'}->{'addresses'}->{$address}) or Echolot::Log::cluck ("$address does not exist in Metadata address list."), return undef; $self->{'METADATA'}->{'addresses'}->{$address}->{'ttl'} --; $self->{'METADATA'}->{'addresses'}->{$address}->{'status'} = 'ttl timeout', Echolot::Log::info("Remailer $address disabled: ttl expired."), $self->{'METADATA'}->{'addresses'}->{$address}->{'resurrection_ttl'} = Echolot::Config::get()->{'check_resurrection_ttl'} if ($self->{'METADATA'}->{'addresses'}->{$address}->{'ttl'} <= 0); $self->commit(); return 1; }; =item $storage->B( I<$address> ) Decrease the resurrection TTL (Time To Live) for remailer with address I<$address> by one. If it hits zero the remailer's status is set to B. Returns 1, undef on error. =cut sub decrease_resurrection_ttl($$) { my ($self, $address) = @_; defined ($self->{'METADATA'}->{'addresses'}->{$address}) or Echolot::Log::cluck ("$address does not exist in Metadata address list."), return 0; ($self->{'METADATA'}->{'addresses'}->{$address}->{'status'} eq 'ttl timeout') or Echolot::Log::cluck ("$address is not in ttl timeout status."), return 0; $self->{'METADATA'}->{'addresses'}->{$address}->{'resurrection_ttl'} --; $self->{'METADATA'}->{'addresses'}->{$address}->{'status'} = 'dead', Echolot::Log::info("Remailer $address is dead."), if ($self->{'METADATA'}->{'addresses'}->{$address}->{'resurrection_ttl'} <= 0); $self->commit(); return 1; }; =item $storage->B( I<$address> ) Restore the TTL (Time To Live) for remailer with address I<$address> to the value configured with I See L for more information on this settings. Returns 1, undef on error. =cut sub restore_ttl($$) { my ($self, $address) = @_; defined ($self->{'METADATA'}->{'addresses'}->{$address}) or Echolot::Log::cluck ("$address does not exist in Metadata address list."), return undef; defined ($self->{'METADATA'}->{'addresses'}->{$address}->{'status'}) or Echolot::Log::cluck ("$address does exist in Metadata address list but does not have status defined."), return undef; Echolot::Log::info("Remailer $address is alive and active again.") unless ($self->{'METADATA'}->{'addresses'}->{$address}->{'status'} eq 'active'); $self->{'METADATA'}->{'addresses'}->{$address}->{'ttl'} = Echolot::Config::get()->{'addresses_default_ttl'}; delete $self->{'METADATA'}->{'addresses'}->{$address}->{'resurrection_ttl'}; $self->{'METADATA'}->{'addresses'}->{$address}->{'status'} = 'active' if ($self->{'METADATA'}->{'addresses'}->{$address}->{'status'} eq 'ttl timeout' || $self->{'METADATA'}->{'addresses'}->{$address}->{'status'} eq 'dead'); $self->commit(); return 1; }; =item $storage->B( I<$id> ) Set the remailer whoise id is I<$id> to B. Returns 1, undef on error. =cut sub not_a_remailer($$) { my ($self, $id) = @_; my $remailer = $self->get_address_by_id($id); defined $remailer or Echolot::Log::cluck("No remailer found for id '$id'."), return undef; my $address = $remailer->{'address'}; defined ($self->{'METADATA'}->{'addresses'}->{$address}) or Echolot::Log::cluck ("$address does not exist in Metadata address list."), return undef; $self->{'METADATA'}->{'addresses'}->{$address}->{'status'} = 'disabled by user reply: is not a remailer'; Echolot::Log::info("Setting $id, $address to disabled by user reply."); $self->commit(); return 1; }; =item $storage->B( I<$type>, I<$caps>, I<$nick>, I<$address>, I<$timestamp> [, I<$dont_expire> ]) Sets the capabilities for remailer with address I<$address> to the given information (I<$nick>, I<$type>, I<$caps>, I<$timestamp>). Type here means the software used (Mixmaster, Reliable) as given by the remailer-conf reply or something like B. If there already is newer information about that key than I<$timestamp> the update is disregarded. If I<$dont_expire> is defined the setting is copied to the remailers metadata as well. Returns 1. =cut sub set_caps($$$$$$;$) { my ($self, $type, $caps, $nick, $address, $timestamp, $dont_expire) = @_; (defined $address) or Echolot::Log::cluck ("$address not defined in set_key."); if (! defined $self->{'metadata'}->{'remailers'}->{$address} ) { $self->{'metadata'}->{'remailers'}->{$address} = {}; }; if (! defined $self->{'METADATA'}->{'remailers'}->{$address}->{'conf'}) { $self->{'METADATA'}->{'remailers'}->{$address}->{'conf'} = { nick => $nick, type => $type, capabilities => $caps, last_update => $timestamp }; } else { my $conf = $self->{'METADATA'}->{'remailers'}->{$address}->{'conf'}; if ($conf->{'last_update'} >= $timestamp) { Echolot::Log::info("Stored data is already newer for remailer $nick."); return 1; }; $conf->{'last_update'} = $timestamp; if ($conf->{'nick'} ne $nick) { Echolot::Log::info($conf->{'nick'}." was renamed to $nick."); $conf->{'nick'} = $nick; }; if ($conf->{'capabilities'} ne $caps) { Echolot::Log::info("$nick has a new caps string '$caps' old: '".$conf->{'capabilities'}."'."); $conf->{'capabilities'} = $caps; }; if ($conf->{'type'} ne $type) { Echolot::Log::info("$nick has a new type string '$type'."); $conf->{'type'} = $type; }; }; if (defined $dont_expire) { $self->{'METADATA'}->{'remailers'}->{$address}->{'conf'}->{'dont_expire'} = $dont_expire; }; $self->commit(); return 1; }; =item $storage->B( I<$type>, I<$nick>, I<$address>, I<$key>, I<$keyid>, I<$version>, I<$caps>, I<$summary>, I<$timestamp>) Sets the I<$type> key I<$keyid> for remailer with address I<$address> to the given information (I<$nick>, I<$key>, I<$caps>, I<$summary>, I<$timestamp>). If there already is newer information about that key than I<$timestamp> the update is disregarded. Returns 1. =cut sub set_key($$$$$$$$$) { my ($self, $type, $nick, $address, $key, $keyid, $version, $caps, $summary, $timestamp) = @_; (defined $address) or Echolot::Log::cluck ("$address not defined in set_key."); if (! defined $self->{'metadata'}->{'remailers'}->{$address} ) { $self->{'metadata'}->{'remailers'}->{$address} = {}; }; if (! defined $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}) { $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'} = {}; }; if (! defined $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}->{$type}) { $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}->{$type} = {}; }; if (! defined $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}->{$type}->{$keyid}) { $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}->{$type}->{$keyid} = { key => $key, summary => $summary, nick => $nick, last_update => $timestamp }; } else { my $keyref = $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}->{$type}->{$keyid}; if ($keyref->{'last_update'} >= $timestamp) { Echolot::Log::info("Stored data is already newer for remailer $nick."); return 1; }; $keyref->{'last_update'} = $timestamp; if ($keyref->{'nick'} ne $nick) { Echolot::Log::info("$nick has a new key nick string '$nick' old: '".$keyref->{'nick'}."'."); $keyref->{'nick'} = $nick; }; if ($keyref->{'summary'} ne $summary) { Echolot::Log::info("$nick has a new key summary string '$summary' old: '".$keyref->{'summary'}."'."); $keyref->{'summary'} = $summary; }; if ($keyref->{'key'} ne $key) { #Echolot::Log::info("$nick has a new key string '$key' old: '".$keyref->{'key'}."' - This probably should not happen."); Echolot::Log::info("$nick has a new key string for same keyid $keyid."); $keyref->{'key'} = $key; }; }; $self->commit(); return 1; }; =item $storage->B( ) Return our secret (Used in Message Authentication Codes). =cut sub get_secret($) { my ($self) = @_; return $self->{'METADATA'}->{'secret'}; }; =item $storage->B( I<$remailer> ) Get an array of types supported by remailer with address I<$remailer>. Returns undef on errors. ¿ It may be possible that a type is returned but then has no keys. This may be a bug, I'm not sure. =cut sub get_types($$) { my ($self, $remailer) = @_; defined ($self->{'METADATA'}->{'addresses'}->{$remailer}) or Echolot::Log::cluck ("$remailer does not exist in Metadata remailer list."), return undef; return () unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}; return () unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}; my @types = keys %{$self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}}; return @types; }; =item $storage->B( I<$remailer>, I<$type> ) Checks if the remailer with address I<$remailer> has type I<$type> keys. Returns 1 if it has, 0 if not, undef on errors. =cut sub has_type($$$) { my ($self, $remailer, $type) = @_; defined ($self->{'METADATA'}->{'addresses'}->{$remailer}) or Echolot::Log::cluck ("$remailer does not exist in Metadata remailer list."), return undef; return 0 unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}; return 0 unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}; return 0 unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}; return 0 unless scalar keys %{$self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}}; return 1; }; =item $storage->B( I<$remailer>, I<$type> ) Returns an array listing all keyids of type I<$type> of remailer with address I<$remailer>. Returns undef on errors. =cut sub get_keys($$$) { my ($self, $remailer, $type) = @_; defined ($self->{'METADATA'}->{'addresses'}->{$remailer}) or Echolot::Log::cluck ("$remailer does not exist in Metadata address list."), return undef; defined ($self->{'METADATA'}->{'remailers'}->{$remailer}) or Echolot::Log::cluck ("$remailer does not exist in Metadata remailer list."), return undef; defined ($self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}) or Echolot::Log::cluck ("$remailer does not have keys in Metadata remailer list."), return undef; defined ($self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}) or Echolot::Log::cluck ("$remailer does not have type '$type' in Metadata remailer list."), return undef; my @keys = keys %{$self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}}; return @keys; }; =item $storage->B( I<$remailer>, I<$type>, I<$key> ) Returns a hash having they keys C, C, C, and C of the I<$type> key with id I<$key> of remailer with address I<$remailer>. Returns undef on errors. =cut sub get_key($$$$) { my ($self, $remailer, $type, $key) = @_; defined ($self->{'METADATA'}->{'addresses'}->{$remailer}) or Echolot::Log::cluck ("$remailer does not exist in Metadata address list."), return undef; defined ($self->{'METADATA'}->{'remailers'}->{$remailer}) or Echolot::Log::cluck ("$remailer does not exist in Metadata remailer list."), return undef; defined ($self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}) or Echolot::Log::cluck ("$remailer does not have keys in Metadata remailer list."), return undef; defined ($self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}) or Echolot::Log::cluck ("$remailer does not have type '$type' in Metadata remailer list."), return undef; defined ($self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}->{$key}) or Echolot::Log::cluck ("$remailer does not have key '$key' in type '$type' in Metadata remailer list."), return undef; my %result = ( summary => $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}->{$key}->{'summary'}, key => $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}->{$key}->{'key'}, nick => $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}->{$key}->{'nick'}, last_update => $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}->{$key}->{'last_update'} ); return %result; }; =item $storage->B( I<$remailer> ) Return the capabilities on file for remailer with address I<$remailer>. This is probably the one we got from remailer-conf or set manually. Returns undef on errors. =cut sub get_capabilities($$) { my ($self, $remailer) = @_; return undef unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}; return undef unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}->{'conf'}; return $self->{'METADATA'}->{'remailers'}->{$remailer}->{'conf'}->{'capabilities'}; }; =item $storage->B( I<$remailer> ) Return the capabilities on file for remailer with address I<$remailer>. This is probably the one we got from remailer-conf or set manually. Returns undef on errors. =cut sub get_nick($$) { my ($self, $remailer) = @_; defined $remailer or Echolot::Log::cluck ("Undefined remailer passed to get_nick()."), return undef; return undef unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}; return undef unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}->{'conf'}; return $self->{'METADATA'}->{'remailers'}->{$remailer}->{'conf'}->{'nick'}; }; =item $storage->B( ) Expires old keys, confs and pings from the Storage as configured by I, I, and I. See L for more information on these settings. Returns 1 on success, undef on errors. =cut sub expire($) { my ($self) = @_; my $now = time(); my $expire_keys = $now - Echolot::Config::get()->{'expire_keys'}; my $expire_conf = $now - Echolot::Config::get()->{'expire_confs'}; my $expire_pings = $now - Echolot::Config::get()->{'expire_pings'}; my $expire_chainpings = $now - Echolot::Config::get()->{'expire_chainpings'}; my $expire_fromlines = $now - Echolot::Config::get()->{'expire_fromlines'}; # Remailer Information and pings for my $remailer_addr ( keys %{$self->{'METADATA'}->{'remailers'}} ) { if (exists $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}) { for my $type ( keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}} ) { if (exists $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}) { for my $key ( keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}} ) { if ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}->{$key}->{'last_update'} < $expire_keys) { Echolot::Log::info("Expiring $remailer_addr, key, $type, $key."); $self->pingdata_close_one($remailer_addr, $type, $key, 'delete'); delete $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}->{$key}; }; }; delete $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type} unless (scalar keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}}); }; }; delete $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'} unless (scalar keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}}); } if (exists $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'conf'}) { delete $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'conf'} if (defined $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'conf'} && ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'conf'}->{'last_update'} < $expire_conf) && ! ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'conf'}->{'dont_expire'})); } delete $self->{'METADATA'}->{'remailers'}->{$remailer_addr}, next unless ( defined ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'conf'}) || defined ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'})); next unless exists $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}; for my $type ( keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}} ) { next unless exists $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}; for my $key ( keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}} ) { my @out = grep {$_ > $expire_pings} $self->get_pings($remailer_addr, $type, $key, 'out'); my @done = grep {$_->[0] > $expire_pings} $self->get_pings($remailer_addr, $type, $key, 'done'); # write ping to done my $fh = $self->get_ping_fh($remailer_addr, $type, $key, 'done') or Echolot::Log::cluck ("$remailer_addr; type=$type; key=$key has no assigned filehandle for done pings."), return undef; seek($fh, 0, SEEK_SET) or Echolot::Log::warn("Cannot seek to start of $remailer_addr out pings: $!."), return undef; truncate($fh, 0) or Echolot::Log::warn("Cannot truncate done pings file for remailer $remailer_addr; key=$key file to zero length: $!."), return undef; for my $done (@done) { print($fh $done->[0]." ".$done->[1]."\n") or Echolot::Log::warn("Error when writing to $remailer_addr out pings: $!."), return undef; }; $fh->flush(); # rewrite outstanding pings $fh = $self->get_ping_fh($remailer_addr, $type, $key, 'out') or Echolot::Log::cluck ("$remailer_addr; type=$type; key=$key has no assigned filehandle for out pings."), return undef; seek($fh, 0, SEEK_SET) or Echolot::Log::warn("Cannot seek to start of outgoing pings file for remailer $remailer_addr; key=$key: $!."), return undef; truncate($fh, 0) or Echolot::Log::warn("Cannot truncate outgoing pings file for remailer $remailer_addr; key=$key file to zero length: $!."), return undef; print($fh (join "\n", @out), (scalar @out ? "\n" : '') ) or Echolot::Log::warn("Error when writing to outgoing pings file for remailer $remailer_addr; key=$key file: $!."), return undef; $fh->flush(); }; }; }; # Chainpings for my $type ( keys %{$self->{'CHAINPING_FHS'}} ) { my $pings = $self->get_chainpings($type); @{ $pings->{'out'} } = map { my $a = $_; join (' ', map ({ $a->{$_} } qw{sent addr1 type1 key1 addr2 type2 key2})) } grep { $_->{'sent'} > $expire_chainpings } @{ $pings->{'out'} }; @{ $pings->{'done'} } = map { my $a = $_; join (' ', map ({ $a->{$_} } qw{sent addr1 type1 key1 addr2 type2 key2 lat})) } grep { $_->{'sent'} > $expire_chainpings } @{ $pings->{'done'} }; for my $dir (qw{out done}) { my $fh = $self->get_chainping_fh($type, $dir) or Echolot::Log::warn ("have no assigned filehandle for $type $dir chainpings."), return undef; seek($fh, 0, SEEK_SET) or Echolot::Log::warn("Cannot seek to start of $dir chainpings $type $!."), return undef; truncate($fh, 0) or Echolot::Log::warn("Cannot truncate $dir chainpings $type file to zero length: $!."), return undef; print($fh (join "\n", @{$pings->{$dir}}), (scalar @{$pings->{$dir}} ? "\n" : '') ) or Echolot::Log::warn("Error when writing to $dir chainpings $type file: $!."), return undef; $fh->flush(); }; }; # From Header lines for my $remailer_addr ( keys %{$self->{'METADATA'}->{'fromlines'}} ) { for my $type ( keys %{$self->{'METADATA'}->{'fromlines'}->{$remailer_addr}} ) { for my $user_supplied ( keys %{$self->{'METADATA'}->{'fromlines'}->{$remailer_addr}->{$type}} ) { delete $self->{'METADATA'}->{'fromlines'}->{$remailer_addr}->{$type}->{$user_supplied} if ($self->{'METADATA'}->{'fromlines'}->{$remailer_addr}->{$type}->{$user_supplied}->{'last_update'} < $expire_fromlines); }; delete $self->{'METADATA'}->{'fromlines'}->{$remailer_addr}->{$type} unless (scalar keys %{$self->{'METADATA'}->{'fromlines'}->{$remailer_addr}->{$type}}); }; delete $self->{'METADATA'}->{'fromlines'}->{$remailer_addr} unless (scalar keys %{$self->{'METADATA'}->{'fromlines'}->{$remailer_addr}}); }; $self->commit(); return 1; }; =item $storage->B( I<$address> ) Delete all data on the remailer with I<$address>. This includes stored conf and key information, pings and the remailer's settings like I et al. If this remailer is still referenced by other remailers' remailer-conf reply it is likely to get picked up again. Returns 1. =cut sub delete_remailer($$) { my ($self, $address) = @_; Echolot::Log::notice("Deleting remailer $address."); if (defined $self->{'METADATA'}->{'addresses'}->{$address}) { delete $self->{'METADATA'}->{'addresses'}->{$address} } else { Echolot::Log::cluck("Remailer $address does not exist in addresses.") }; if (defined $self->{'METADATA'}->{'remailers'}->{$address}) { for my $type ( keys %{$self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}} ) { for my $key ( keys %{$self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}->{$type}} ) { $self->pingdata_close_one($address, $type, $key, 'delete'); }; }; delete $self->{'METADATA'}->{'remailers'}->{$address} }; delete $self->{'METADATA'}->{'fromlines'}->{$address} if (defined $self->{'METADATA'}->{'fromlines'}->{$address}); $self->commit(); return 1; }; =item $storage->B( I<$address> ) Delete conf data of the remailer with I<$address>. Returns 1. =cut sub delete_remailercaps($$) { my ($self, $address) = @_; Echolot::Log::info("Deleting conf for remailer $address."); if (defined $self->{'METADATA'}->{'remailers'}->{$address}) { delete $self->{'METADATA'}->{'remailers'}->{$address}->{'conf'} if defined $self->{'METADATA'}->{'remailers'}->{$address}->{'conf'}; } else { Echolot::Log::cluck("Remailer $address does not exist in remailers.") }; $self->commit(); return 1; }; =item $storage->B( I<$address>, I<$with_from>, I<$from>, $I, $I ) Register that the remailer I<$address> returned the From header line I<$from>. If I<$with_from> is 1 we had tried to supply our own From, otherwise not. $I and $I are boolean variables indicating presence or absense of any disclaimer. Returns 1, undef on error. =cut sub register_fromline($$$$$$$) { my ($self, $address, $type, $with_from, $from, $top, $bot) = @_; defined ($self->{'METADATA'}->{'addresses'}->{$address}) or Echolot::Log::cluck ("$address does not exist in Metadata address list."), return undef; defined ($from) or Echolot::Log::cluck ("from is not defined in register_fromline."), return undef; defined ($with_from) or Echolot::Log::cluck ("from is not defined in register_fromline."), return undef; ($with_from == 0 || $with_from == 1) or Echolot::Log::cluck ("with_from has evil value $with_from in register_fromline."), return undef; Echolot::Log::debug("registering fromline $address, $type, $with_from, $from, $top, $bot."); $self->{'METADATA'}->{'fromlines'}->{$address}->{$type}->{$with_from} = { last_update => time(), from => $from, disclaim_top => $top, disclaim_bot => $bot, }; $self->commit(); return 1; }; =item $storage->B( I<$addr>, I<$type>, I<$user_supplied> ) Return a hash reference with header From line information. The hash has two keys, B and B, which holds the actual information. If there is no from line registered for the given combination, undef is returned. On Error, also undef is returned. =cut sub get_fromline($$$$) { my ($self, $addr, $type, $user_supplied) = @_; defined $self->{'METADATA'}->{'fromlines'}->{$addr} or return undef; defined $self->{'METADATA'}->{'fromlines'}->{$addr}->{$type} or return undef; defined $self->{'METADATA'}->{'fromlines'}->{$addr}->{$type}->{$user_supplied} or return undef; defined $self->{'METADATA'}->{'fromlines'}->{$addr}->{$type}->{$user_supplied}->{'last_update'} or Echolot::Log::cluck ("last_update is undefined with $addr $type $user_supplied."), return undef; defined $self->{'METADATA'}->{'fromlines'}->{$addr}->{$type}->{$user_supplied}->{'from'} or Echolot::Log::cluck ("from is undefined with $addr $type $user_supplied."), return undef; return { last_update => $self->{'METADATA'}->{'fromlines'}->{$addr}->{$type}->{$user_supplied}->{'last_update'}, from => $self->{'METADATA'}->{'fromlines'}->{$addr}->{$type}->{$user_supplied}->{'from'}, disclaim_top => $self->{'METADATA'}->{'fromlines'}->{$addr}->{$type}->{$user_supplied}->{'disclaim_top'}, disclaim_bot => $self->{'METADATA'}->{'fromlines'}->{$addr}->{$type}->{$user_supplied}->{'disclaim_bot'} }; } # sub convert($) { # my ($self) = @_; # # for my $remailer_addr ( keys %{$self->{'METADATA'}->{'remailers'}} ) { # for my $type ( keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}} ) { # for my $key ( keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}} ) { # if (defined $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'stats'}->{$type}->{$key}) { # $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}->{$key}->{'stats'} = # $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'stats'}->{$type}->{$key}; # }; # }; # }; # delete $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'stats'}; # }; # # $self->commit(); # }; # # sub convert($) { # my ($self) = @_; # # for my $remailer_addr ( keys %{$self->{'METADATA'}->{'addresses'}} ) { # $self->{'METADATA'}->{'addresses'}->{$remailer_addr}->{'fetch'} = 1; # $self->{'METADATA'}->{'addresses'}->{$remailer_addr}->{'pingit'} = 1; # $self->{'METADATA'}->{'addresses'}->{$remailer_addr}->{'showit'} = 0; # delete $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'pingit'}; # delete $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'showit'}; # }; # # $self->commit(); # }; =back =cut # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Commands.pm0000644000175000017500000001035012422221767016202 0ustar weaselweaselpackage Echolot::Commands; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Commands - manage commands like add key, set ttl etc. =head1 DESCRIPTION This package provides functions for sending out and receiving pings. =cut use strict; use Echolot::Log; use Fcntl ':flock'; # import LOCK_* constants #use Fcntl ':seek'; # import SEEK_* constants use POSIX; # import SEEK_* constants (older perls don't have SEEK_ in Fcntl) use English; sub addCommand($) { my ($command) = @_; my $filename = Echolot::Config::get()->{'commands_file'}; open(FH, ">>$filename" ) or Echolot::Log::warn("Cannot open $filename for appending $!."), return 0; flock(FH, LOCK_EX) or Echolot::Log::warn("Cannot get exclusive lock on $filename: $!."), return 0; print FH $command,"\n"; flock(FH, LOCK_UN) or Echolot::Log::warn("Cannot unlock $filename: $!."); close(FH) or Echolot::Log::warn("Cannot close $filename: $!."); }; sub processCommands($) { my $filename = Echolot::Config::get()->{'commands_file'}; (-e $filename) or return 1; my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks)= stat $filename; ($size > 0) or return 1; open(FH, "+<$filename" ) or Echolot::Log::warn("Cannot open $filename for reading: $!."), return 0; flock(FH, LOCK_EX) or Echolot::Log::warn("Cannot get exclusive lock on $filename: $!."), return 0; while () { chomp; my ($command, @args) = split; if ($command eq 'add') { Echolot::Globals::get()->{'storage'}->add_address(@args); } elsif ($command eq 'set') { Echolot::Globals::get()->{'storage'}->set_stuff(@args); } elsif ($command eq 'getkeyconf') { Echolot::Globals::get()->{'scheduler'}->schedule('getkeyconf', 0, time(), \@args ); } elsif ($command eq 'sendpings') { Echolot::Globals::get()->{'scheduler'}->schedule('ping', 0, time(), \@args ); } elsif ($command eq 'sendchainpings') { Echolot::Globals::get()->{'scheduler'}->schedule('chainping', 0, time(), \@args ); } elsif ($command eq 'buildstats') { Echolot::Globals::get()->{'scheduler'}->schedule('buildstats', 0, time() ); } elsif ($command eq 'buildkeys') { Echolot::Globals::get()->{'scheduler'}->schedule('buildkeys', 0, time() ); } elsif ($command eq 'buildthesaurus') { Echolot::Globals::get()->{'scheduler'}->schedule('buildthesaurus', 0, time() ); } elsif ($command eq 'buildfromlines') { Echolot::Globals::get()->{'scheduler'}->schedule('buildfromlines', 0, time() ); } elsif ($command eq 'summary') { @args = ('manual'); Echolot::Globals::get()->{'scheduler'}->schedule('summary', 0, time(), \@args ); } elsif ($command eq 'delete') { Echolot::Globals::get()->{'storage'}->delete_remailer(@args); } elsif ($command eq 'setremailercaps') { my $addr = shift @args; my $conf = join(' ', @args); Echolot::Conf::set_caps_manually($addr, $conf); } elsif ($command eq 'deleteremailercaps') { Echolot::Globals::get()->{'storage'}->delete_remailercaps(@args); } else { Echolot::Log::warn("Unkown command: '$_'."); }; }; seek(FH, 0, SEEK_SET) or Echolot::Log::warn("Cannot seek to start '$filename': $!."), return 0; truncate(FH, 0) or Echolot::Log::warn("Cannot truncate '$filename' to zero length: $!."), return 0; flock(FH, LOCK_UN) or Echolot::Log::warn("Cannot unlock '$filename': $!."); close(FH) or Echolot::Log::warn("Cannot close '$filename': $!."); }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Scheduler.pm0000644000175000017500000001204512422221767016362 0ustar weaselweaselpackage Echolot::Scheduler; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Scheduler - Task selector/scheduler for echolot =head1 DESCRIPTION This package provides several functions for scheduling tasks within the ping daemon. =over =cut use strict; use English; use Echolot::Log; my $ORDER = 1; =item B () Creates a new scheduler object. =cut sub new { my ($class, %params) = @_; my $self = {}; bless $self, $class; return $self; }; =item B (I, I, I, I, I) Adds a task with I to the list of tasks. Every I seconds I is called. If for example I is 3600 - meaning I should be executed hourly - setting I to 600 would mean that it get's called 10 minutes after the hour. I indicates that it is ok to miss one run of this job. This can happen if we run behind schedule for instance. =cut sub add($$$$$$) { my ($self, $name, $interval, $offset, $missok, $what) = @_; Echolot::Log::logdie("Must not add zero intervall for job $name.") unless $interval; if (defined $self->{'tasks'}->{$name}) { @{ $self->{'schedule'} } = grep { $_->{'name'} ne $name } @{ $self->{'schedule'} }; }; $self->{'tasks'}->{$name} = { interval => $interval, offset => $offset, what => $what, order => $ORDER++, missok => $missok, }; $self->schedule($name, 1); return 1; }; =item B (I, I, [ I, [I]] ) Schedule execution of I for I. If I is not given it is calculated from I and I passed to B. if I is set the task will be rescheduled when it's done (according to its interval). You may also give arguments to passed to the task. =cut sub schedule($$$;$$) { my ($self, $name, $reschedule, $for, $arguments) = @_; (defined $self->{'tasks'}->{$name}) or Echolot::Log::warn("Task $name is not defined."), return 0; my $interval = $self->{'tasks'}->{$name}->{'interval'}; my $offset = $self->{'tasks'}->{$name}->{'offset'}; unless (defined $for) { ($interval < 0) and return 1; my $now = time(); $for = $now - $now % $interval + $offset; ($for <= $now) and $for += $interval; my $cnt = 0; while ($self->{'tasks'}->{$name}->{'missok'} && ($for <= $now)) { $for += $interval; $cnt ++; }; Echolot::Log::debug("Skipping $cnt runs of $name.") if $cnt; }; $arguments = [] unless defined $arguments; push @{ $self->{'schedule'} }, { start => $for, order => $self->{'tasks'}->{$name}->{'order'}, name => $name, arguments => $arguments, reschedule => $reschedule }; @{ $self->{'schedule'} } = sort { $a->{'start'} <=> $b->{'start'} or $a->{'order'} <=> $b->{'order'} } @{ $self->{'schedule'} }; return 1; }; =item B () Start the scheduling run. It will run forever or until a task with I == 'exit' is executed. =cut sub run($) { my ($self) = @_; (defined $self->{'schedule'}->[0]) or Echolot::Log::warn("Scheduler is empty."), return 0; while(1) { my $now = time(); my $task = $self->{'schedule'}->[0]; if ($task->{'start'} < $now) { Echolot::Log::warn("Task $task->{'name'} could not be started on time.") unless ($task->{'start'} == 0); } else { Echolot::Log::debug("zZzZZzz."); $PROGRAM_NAME = "pingd [sleeping]"; sleep ($task->{'start'} - $now); }; (time() < $task->{'start'}) and next; $now = $task->{'start'}; do { $task = shift @{ $self->{'schedule'} }; my $name = $task->{'name'}; $PROGRAM_NAME = "pingd [executing $name]"; (defined $self->{'tasks'}->{$name}) or Echolot::Log::cluck("Task $task->{'name'} is not defined."); my $what = $self->{'tasks'}->{$name}->{'what'}; Echolot::Log::debug("Running $name (was scheduled for ".(time()-$now)." seconds ago)."); last if ($what eq 'exit'); &$what( $now, @{ $task->{'arguments'} } ); $self->schedule($name, 1, $now + $self->{'tasks'}->{$name}->{'interval'}) if ($task->{'reschedule'} && $self->{'tasks'}->{$name}->{'interval'} > 0); (defined $self->{'schedule'}->[0]) or Echolot::Log::warn("Scheduler is empty."), return 0; } while ($now >= $self->{'schedule'}->[0]->{'start'}); }; return 1; }; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Pinger.pm0000644000175000017500000001447412422221767015700 0ustar weaselweaselpackage Echolot::Pinger; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Pinger - actual sending and receiving of Pings. =head1 DESCRIPTION This package provides functions for sending out and receiving pings. =cut use strict; use English; use Echolot::Log; use Echolot::Pinger::Mix; use Echolot::Pinger::CPunk; sub do_mix_ping($$$$$$) { my ($address, $type, $keyid, $to, $with_from, $body) = @_; ($type eq 'mix') or Echolot::Log::warn("types should really be mix ($type)."), return 0; my %key = Echolot::Globals::get()->{'storage'}->get_key($address, $type, $keyid); Echolot::Pinger::Mix::ping( $body, $to, $with_from, [ $key{'nick'} ], { $keyid => \%key } ) or return 0; return 1; }; sub do_cpunk_ping($$$$$$) { my ($address, $type, $keyid, $to, $with_from, $body) = @_; my $keyhash = {}; if ($type ne 'cpunk-clear') { my %key = Echolot::Globals::get()->{'storage'}->get_key($address, $type, $keyid); $keyhash->{$keyid} = \%key; }; Echolot::Pinger::CPunk::ping( $body, $to, $with_from, [ { address => $address, keyid => $keyid, encrypt => ($type ne 'cpunk-clear'), pgp2compat => ($type eq 'cpunk-rsa') } ], $keyhash ) or return 0; return 1; }; sub do_ping($$$$) { my ($type, $address, $key, $with_from) = @_; my $now = time(); my $token = join(':', $address, $type, $key, $with_from, $now); my $mac = Echolot::Tools::make_mac($token); my $body = "remailer: $address\n". "type: $type\n". "key: $key\n". "with_from: $with_from\n". "sent: $now\n". "mac: $mac\n". Echolot::Tools::make_garbage(); $body = Echolot::Tools::crypt_symmetrically($body, 'encrypt'); my $to = Echolot::Tools::make_address('ping'); if ($type eq 'mix') { do_mix_ping($address, $type, $key, $to, $with_from, $body); } elsif ($type eq 'cpunk-rsa' || $type eq 'cpunk-dsa' || $type eq 'cpunk-clear') { do_cpunk_ping($address, $type, $key, $to, $with_from, $body); } else { Echolot::Log::warn("Don't know how to handle ping type $type."); return 0; }; Echolot::Globals::get()->{'storage'}->register_pingout($address, $type, $key, $now); return 1; }; sub send_pings($;$) { my ($scheduled_for, $which) = @_; $which = '' unless defined $which; my $call_intervall = Echolot::Config::get()->{'pinger_interval'}; my $send_every_n_calls = Echolot::Config::get()->{'ping_every_nth_time'}; my $timemod = int ($scheduled_for / $call_intervall); my $this_call_id = $timemod % $send_every_n_calls; my $session_id = int ($scheduled_for / ($call_intervall * $send_every_n_calls)); my @remailers = Echolot::Globals::get()->{'storage'}->get_addresses(); for my $remailer (@remailers) { next unless $remailer->{'pingit'}; my $address = $remailer->{'address'}; next unless ( $which eq 'all' || $which eq $address || $which eq ''); for my $type (Echolot::Globals::get()->{'storage'}->get_types($address)) { next unless Echolot::Config::get()->{'do_pings'}->{$type}; for my $key (Echolot::Globals::get()->{'storage'}->get_keys($address, $type)) { next unless ( $which eq $address || $which eq 'all' || (($which eq '') && ($this_call_id eq (Echolot::Tools::makeShortNumHash($address.$type.$key.$session_id) % $send_every_n_calls)))); my $with_from = (int($timemod / $send_every_n_calls)) % 2; Echolot::Log::debug("ping calling $type, $address, $key, $with_from."); do_ping($type, $address, $key, $with_from); } }; }; return 1; }; sub receive($$$$) { my ($header, $msg, $token, $timestamp) = @_; my $now = time(); my $body; my $bot = 0; my $top = 0; # < 2.0beta34 didn't encrypt pings. if ($msg =~ /^-----BEGIN PGP MESSAGE-----/m) { # work around borken middleman remailers that have a problem with some # sort of end of line characters and randhopping them through reliable # remailers.. # they add an empty line between each usefull line $msg =~ s/(\r?\n)\r?\n/$1/g if ($msg =~ /^-----BEGIN PGP MESSAGE-----\r?\n\r?\n/m); $top = ($msg =~ m/^\S.*-----BEGIN PGP MESSAGE-----/ms) ? 1 : 0; $bot = ($msg =~ m/^-----END PGP MESSAGE-----.*\S/ms) ? 1 : 0; $body = Echolot::Tools::crypt_symmetrically($msg, 'decrypt'); }; $body = $msg unless defined $body; my ($addr) = $body =~ /^remailer: (.*)$/m; my ($type) = $body =~ /^type: (.*)$/m; my ($key) = $body =~ /^key: (.*)$/m; my ($sent) = $body =~ /^sent: (.*)$/m; my ($with_from) = $body =~ /^with_from: (.*)$/m; my ($mac) = $body =~ /^mac: (.*)$/m; my @values = ($addr, $type, $key, defined $with_from ? $with_from : 'undef', $sent, $mac); # undef was added after 2.0.10 my $cleanstring = join ":", map { defined() ? $_ : "undef" } @values; my @values_obsolete = ($addr, $type, $key, $sent, $mac); # <= 2.0.10 (grep { ! defined() } @values_obsolete) and Echolot::Log::warn("Received ping at $timestamp has undefined values: $cleanstring."), return 0; pop @values; pop @values_obsolete; Echolot::Tools::verify_mac(join(':', @values), $mac) or Echolot::Tools::verify_mac(join(':', @values_obsolete), $mac) or # old style without with_from Echolot::Log::warn("Received ping at $timestamp has wrong mac; $cleanstring."), return 0; Echolot::Globals::get()->{'storage'}->register_pingdone($addr, $type, $key, $sent, $now - $sent) or return 0; if (defined $with_from) { # <= 2.0.10 didn't have with_from my ($from) = $header =~ /From: (.*)/i; $from = 'undefined' unless defined $from; Echolot::Globals::get()->{'storage'}->register_fromline($addr, $type, $with_from, $from, $top, $bot); }; return 1; }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Fromlines.pm0000644000175000017500000001027012422221767016400 0ustar weaselweaselpackage Echolot::Fromlines; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Thesaurus - build from header page =head1 DESCRIPTION This package builds the from header page with the information we received from pings. =cut use strict; use English; use Echolot::Log; sub build_fromlines() { return 1 unless Echolot::Config::get()->{'fromlines'}; my $data; my @remailers = Echolot::Globals::get()->{'storage'}->get_addresses(); for my $remailer (@remailers) { next unless $remailer->{'showit'}; my $addr = $remailer->{'address'}; my $nick = Echolot::Globals::get()->{'storage'}->get_nick($addr); next unless defined $nick; my $caps = Echolot::Globals::get()->{'storage'}->get_capabilities($addr); next unless defined $caps; next unless $caps !~ m/\btesting\b/i; my $middleman = $caps =~ m/\bmiddle\b/; next if $middleman; for my $user_supplied (0, 1) { $data->{$user_supplied}->{$addr}->{'nick'} = $nick; $data->{$user_supplied}->{$addr}->{'address'} = $addr; my @types = Echolot::Globals::get()->{'storage'}->get_types($addr); my $from_types; for my $type (@types) { my $from_info = Echolot::Globals::get()->{'storage'}->get_fromline($addr, $type, $user_supplied); my $from = $from_info->{'from'}; $from = 'Not Available' unless defined $from; $from = 'Middleman Remailer' if $middleman; my $disclaim_top = $from_info->{'disclaim_top'} && ! $middleman ? 1 : 0; my $disclaim_bot = $from_info->{'disclaim_bot'} && ! $middleman ? 1 : 0; #my $last_update = $from_info->{'last_update'}; #my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($last_update); my $frominfo = $disclaim_top.':'.$disclaim_bot.':'.$from; #my $date = sprintf("%04d-%02d-%02d", $year+1900, $mon+1, $mday); #my $value = $middleman ? $type : ($type." ($date)"); my $value = $type; push @{$from_types->{$frominfo}}, $value; }; my $types_from; for my $frominfo (sort keys %$from_types) { my $types = join ", ", sort { $a cmp $b } @{$from_types->{$frominfo}}; $types_from->{$types} = $frominfo; }; my @types_from = map { my ($disclaim_top, $disclaim_bot, $from) = split (/:/, $types_from->{$_}, 3); { nick => $nick, address => $addr, types => $_, disclaim_top => $disclaim_top, disclaim_bot => $disclaim_bot, from => Echolot::Tools::escape_HTML_entities($from) } } sort { $a cmp $b } keys %$types_from; $data->{$user_supplied}->{$addr}->{'data'} = \@types_from; }; # Remove user supplied if identical my $f0 = join ':', map { $_->{'disclaim_top'}.':'.$_->{'disclaim_bot'}.$_->{'types'}.':'.$_->{'from'} } @{$data->{0}->{$addr}->{'data'}}; my $f1 = join ':', map { $_->{'disclaim_top'}.':'.$_->{'disclaim_bot'}.$_->{'types'}.':'.$_->{'from'} } @{$data->{1}->{$addr}->{'data'}}; if ($f0 eq $f1) { delete $data->{1}->{$addr}; }; }; my @data0 = map {$data->{0}->{$_}} (sort { $data->{0}->{$a}->{'nick'} cmp $data->{0}->{$b}->{'nick'} } keys (%{$data->{0}})); my @data1 = map {$data->{1}->{$_}} (sort { $data->{1}->{$a}->{'nick'} cmp $data->{1}->{$b}->{'nick'} } keys (%{$data->{1}})); Echolot::Tools::write_HTML_file( Echolot::Config::get()->{'fromlinesindexfile'}, 'fromlinesindexfile', Echolot::Config::get()->{'buildfromlines'}, default => \@data0, usersupplied => \@data1); }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Report.pm0000644000175000017500000000413612422221767015721 0ustar weaselweaselpackage Echolot::Report; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Report - Summarize status of remailers =head1 DESCRIPTION This package prints the summary of remailers/addresses. =cut use strict; use English; use Echolot::Log; sub print_summary(;$) { my ($manual) = @_; my @addresses = sort { $a->{'address'} cmp $b->{'address'} } Echolot::Globals::get()->{'storage'}->get_addresses(); my $report = "*** Status summary ***\n"; for my $remailer (@addresses) { my $addr = $remailer->{'address'}; $report .= "$addr (ID: $remailer->{'id'}): ".uc($remailer->{'status'})."; ". "Fetch/Ping/Show: ". ($remailer->{'fetch'} ? '1' : '0') . ($remailer->{'pingit'} ? '1' : '0') . ($remailer->{'showit'} ? '1' : '0') . "; TTL: $remailer->{'ttl'}\n"; $report .= " Resurection TTL: $remailer->{'resurrection_ttl'}\n" if (defined $remailer->{'resurrection_ttl'} && ($remailer->{'status'} eq 'ttl timeout')); for my $type (Echolot::Globals::get()->{'storage'}->get_types($addr)) { $report .= " Type: $type: ".join(', ', Echolot::Globals::get()->{'storage'}->get_keys($addr, $type))."\n"; }; }; if (defined $manual) { Echolot::Log::notice($report); } else { Echolot::Log::info($report); } return 1; }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Stats.pm0000644000175000017500000010502712422221767015545 0ustar weaselweaselpackage Echolot::Stats; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Stats - produce Stats, keyrings et al =head1 DESCRIPTION This package provides functions for generating remailer stats, and keyrings. =cut use strict; use English; use Echolot::Log; my $STATS_DAYS; my $SECONDS_PER_DAY; my $WEIGHT; my %LAST_BROKENCHAIN_RUN; my %BROKEN_CHAINS; sub make_date() { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime(); sprintf("%s %02d %s %4d %02d:%02d:%02d GMT", Echolot::Tools::make_dayname($wday), $mday, Echolot::Tools::make_monthname($mon), $year + 1900, $hour, $min, $sec); }; sub make_min_hr($$) { my ($sec, $includesec) = @_; my ($s, $m, $h); if (defined $sec) { $s = $sec % 60; $m = $sec / 60 % 60; $h = int ($sec / 60 / 60); }; if ((! defined $sec) || ($sec < 0) || ($h > 99)) { $h = 99; $m = 59; $s = 59; }; if ($includesec) { if ($h) { return sprintf ("%2d:%02d:%02d", $h, $m, $s); } elsif ($m) { return sprintf ( " %2d:%02d", $m, $s); } else { return sprintf ( " %2d", $s); }; } else { if ($h) { return sprintf ("%2d:%02d", $h, $m); } else { return sprintf ( " :%02d", $m); }; }; }; sub build_list1_latencystr($) { my ($lat) = @_; my $str = '?' x $STATS_DAYS; for my $day (0 .. $STATS_DAYS - 1) { substr($str, $STATS_DAYS - 1 - $day, 1) = (defined $lat->[$day]) ? ($lat->[$day] < 300 ? '#' : ($lat->[$day] < 3600 ? '*' : ($lat->[$day] < 14400 ? '+' : ($lat->[$day] < 86400 ? '-' : ($lat->[$day] < 172800 ? '.' : '_' ))))) : ' '; }; return $str; } sub build_list2_latencystr($) { my ($lat) = @_; my $str = '?' x $STATS_DAYS; for my $day (0 .. $STATS_DAYS - 1) { substr($str, $STATS_DAYS - 1 - $day, 1) = (defined $lat->[$day]) ? ($lat->[$day] < 20*60 ? '0' : ($lat->[$day] < 1*3600 ? '1' : ($lat->[$day] < 2*3600 ? '2' : ($lat->[$day] < 3*3600 ? '3' : ($lat->[$day] < 4*3600 ? '4' : ($lat->[$day] < 5*3600 ? '5' : ($lat->[$day] < 6*3600 ? '6' : ($lat->[$day] < 7*3600 ? '7' : ($lat->[$day] < 8*3600 ? '8' : ($lat->[$day] < 9*3600 ? '9' : ($lat->[$day] < 12*3600 ? 'A' : ($lat->[$day] < 18*3600 ? 'B' : ($lat->[$day] < 24*3600 ? 'C' : ($lat->[$day] < 30*3600 ? 'D' : ($lat->[$day] < 36*3600 ? 'E' : ($lat->[$day] < 42*3600 ? 'F' : ($lat->[$day] < 48*3600 ? 'G' : 'H' ))))))))))))))))) : '?'; }; return $str; } sub build_list2_reliabilitystr($) { my ($rel) = @_; my $str = '?' x $STATS_DAYS; for my $day (0 .. $STATS_DAYS - 1) { substr($str, $STATS_DAYS - 1 - $day, 1) = (defined $rel->[$day]) ? (($rel->[$day] >= 0.9999) ? #(($rel->[$day] == 1) ? '+' : (int ($rel->[$day]*10))) : '?'; }; return $str; } sub build_list2_capsstr($) { my ($caps) = @_; my %caps; $caps{'middle'} = ($caps =~ m/\bmiddle\b/i); $caps{'post'} = ($caps =~ m/\bpost\b/i) || ($caps =~ m/\banon-post-to\b/i); $caps{'mix'} = ($caps =~ m/\bmix\b/i); $caps{'remix'} = ($caps =~ m/\bremix\b/i); $caps{'remix2'} = ($caps =~ m/\bremix2\b/i); $caps{'hybrid'} = ($caps =~ m/\bhybrid\b/i); $caps{'repgp2'} = ($caps =~ m/\brepgp2\b/i); $caps{'repgp'} = ($caps =~ m/\brepgp\b/i); $caps{'pgponly'} = ($caps =~ m/\bpgponly\b/i); $caps{'ext'} = ($caps =~ m/\bext\b/i); $caps{'max'} = ($caps =~ m/\bmax\b/i); $caps{'test'} = ($caps =~ m/\btest\b/i); $caps{'latent'} = ($caps =~ m/\blatent\b/i); $caps{'ek'} = ($caps =~ m/\bek\b/i); $caps{'ekx'} = ($caps =~ m/\bekx\b/i); $caps{'esub'} = ($caps =~ m/\besub\b/i); $caps{'inflt'} = ($caps =~ m/\binflt\d+\b/i); $caps{'rhop'} = ($caps =~ m/\brhop\d+\b/i); ($caps{'klen'}) = ($caps =~ m/\bklen(\d+)\b/i); my $str = ($caps{'middle'} ? 'D' : ' ') . ($caps{'post'} ? 'P' : ' ') . ($caps{'remix2'} ? '2' : ($caps{'remix'} ? 'R' : ($caps{'mix'} ? 'M' : ' ' ))) . ($caps{'hybrid'} ? 'H' : ' ') . ($caps{'repgp2'} ? '2' : ($caps{'repgp'} ? 'G' : ' ' )) . ($caps{'pgponly'} ? 'O' : ' ') . ($caps{'ext'} ? 'X' : ' ') . ($caps{'max'} ? 'A' : ' ') . ($caps{'test'} ? 'T' : ' ') . ($caps{'latent'} ? 'L' : ' ') . ($caps{'ekx'} ? 'E' : ($caps{'ek'} ? 'e' : ' ' )) . ($caps{'esub'} ? 'U' : ' ') . ($caps{'inflt'} ? 'I' : ' ') . ($caps{'rhop'} ? 'N' : ' ') . (defined $caps{'klen'} ? ($caps{'klen'} >= 900 ? '9' : ( $caps{'klen'} >= 800 ? '8' : ( $caps{'klen'} >= 700 ? '7' : ( $caps{'klen'} >= 600 ? '6' : ( $caps{'klen'} >= 500 ? '5' : ( $caps{'klen'} >= 400 ? '4' : ( $caps{'klen'} >= 300 ? '3' : ( $caps{'klen'} >= 200 ? '2' : ( $caps{'klen'} >= 100 ? '1' : '0' ))))))))) : ' '); return $str; } sub median($) { my ($arr) = @_; my $cnt = scalar @$arr; if ($cnt == 0) { return undef; } elsif ($cnt % 2 == 0) { return (($arr->[ int(($cnt - 1 ) / 2) ] + $arr->[ int($cnt / 2) ] ) / 2); } else { return $arr->[ int(($cnt - 1 ) / 2) ]; }; }; # how many % (0-1) values of @$lats are greater than $lat. # $@lats needs to be sorted sub percentile($$) { my ($lat, $lats) = @_; my $num = scalar @$lats; my $i; for ($i=0; $i < $num; $i++) { last if $lat < $lats->[$i]; } return ($num - $i) / $num; } sub calculate($$) { my ($addr, $types) = @_; my $now = time(); my $SKEW_ABS = 15*60; my $SKEW_PERCENT = 0.80; my @out; my @done; for my $type (@$types) { next unless Echolot::Globals::get()->{'storage'}->has_type($addr, $type); my @keys = Echolot::Globals::get()->{'storage'}->get_keys($addr, $type); for my $key (@keys) { push @out, grep {$_ > $now - $STATS_DAYS * $SECONDS_PER_DAY} Echolot::Globals::get()->{'storage'}->get_pings($addr, $type, $key, 'out'); push @done, grep {$_->[0] > $now - $STATS_DAYS * $SECONDS_PER_DAY} Echolot::Globals::get()->{'storage'}->get_pings($addr, $type, $key, 'done'); }; }; my @latency_total = map { $_->[1] } @done; my @latency_day; my $sent_total; my $received_total = 0; my @sent_day; my @received_day; for my $done (@done) { push @{ $latency_day [int(($now - $done->[0]) / $SECONDS_PER_DAY)] }, $done->[1]; my $day = int(($now - $done->[0]) / $SECONDS_PER_DAY); my $weight = $WEIGHT->[$day]; $sent_total += $weight; $sent_day [$day] ++; $received_total += $weight; $received_day[$day] ++; }; @latency_total = sort { $a <=> $b } @latency_total; my $latency_median = median (\@latency_total); my @latency_median_day; for ( 0 .. $STATS_DAYS - 1 ) { @{$latency_day[$_]} = defined $latency_day[$_] ? (sort { $a <=> $b } @{$latency_day[$_]}) : (); $latency_median_day[$_] = median ( $latency_day[$_] ); } if (scalar @out) { my @p = ( scalar @latency_total ) ? map { #printf(STDERR "($now - $_ - $SKEW_ABS)/$SKEW_PERCENT ". #"%s in (%s): %s\n", ($now - $_ - $SKEW_ABS)/$SKEW_PERCENT, join(',', @latency_total), #percentile( ($now - $_ - $SKEW_ABS)/$SKEW_PERCENT , \@latency_total )); percentile( ($now - $_ - $SKEW_ABS)/$SKEW_PERCENT , \@latency_total ) } @out : map { 0 } @out; for (my $i=0; $i < scalar @out; $i++) { my $day = int(($now - $out[$i]) / $SECONDS_PER_DAY); my $weight = $WEIGHT->[$day]; $sent_total += $weight; $sent_day [$day] ++; $received_total += $weight * $p[$i]; $received_day[$day] += $p[$i]; }; }; #printf STDERR "$received_total / %s\n", (defined $sent_total ? $sent_total : 'n/a'); $received_total /= $sent_total if ($sent_total); for ( 0 .. $STATS_DAYS - 1 ) { $received_day[$_] /= $sent_day[$_] if ($sent_day[$_]); }; return { avr_latency => $latency_median, avr_reliability => $received_total, latency_day => \@latency_median_day, reliability_day => \@received_day }; }; sub write_file($$$$) { my ($filebasename, $html_template, $expires, $output) = @_; my $filename = $filebasename.'.txt'; open(F, '>'.$filename) or Echolot::Log::warn("Cannot open $filename: $!."), return 0; print F $output; close (F); if (defined $expires) { Echolot::Tools::write_meta_information($filename, Expires => time + $expires) or Echolot::Log::debug ("Error while writing meta information for $filename."), return 0; }; return 1 unless defined $html_template; if (defined $output) { $output =~ s/&/&/g; $output =~ s/"/"/g; $output =~ s//>/g; }; Echolot::Tools::write_HTML_file($filebasename, $html_template, $expires, list => $output); return 1; }; sub build_mlist1($$$$$;$) { my ($rems, $broken1, $broken2, $sameop, $filebasename, $html_template) = @_; my $output = ''; $output .= sprintf "\nGroups of remailers sharing a machine or operator:\n$sameop\n" if (defined $sameop); $output .= sprintf "\nBroken type-I remailer chains:\n$broken1\n" if (defined $broken1); $output .= sprintf "\nBroken type-II remailer chains:\n$broken2\n" if (defined $broken2); $output .= sprintf "Last update: %s\n", make_date(); $output .= sprintf "mixmaster history latency uptime\n"; $output .= sprintf "--------------------------------------------\n"; for my $remailer (@$rems) { $output .= sprintf "%-14s %-12s %8s %6.2f%%\n", substr($remailer->{'nick'},0,14), build_list1_latencystr($remailer->{'stats'}->{'latency_day'}), make_min_hr($remailer->{'stats'}->{'avr_latency'}, 1), $remailer->{'stats'}->{'avr_reliability'} * 100; }; write_file($filebasename, $html_template, Echolot::Config::get()->{'buildstats'}, $output) or Echolot::Log::debug("writefile failed."), return 0; return 1; }; sub build_rlist1($$$$$;$) { my ($rems, $broken1, $broken2, $sameop, $filebasename, $html_template) = @_; my $output = ''; for my $remailer (sort {$a->{'caps'} cmp $b->{'caps'}} @$rems) { $output .= $remailer->{'caps'}."\n" } $output .= sprintf "\nGroups of remailers sharing a machine or operator:\n$sameop\n" if (defined $sameop); $output .= sprintf "\nBroken type-I remailer chains:\n$broken1\n" if (defined $broken1); $output .= sprintf "\nBroken type-II remailer chains:\n$broken2\n" if (defined $broken2); $output .= sprintf "\n"; $output .= sprintf "Last update: %s\n", make_date(); $output .= sprintf "remailer email address history latency uptime\n"; $output .= sprintf "-----------------------------------------------------------------------\n"; for my $remailer (@$rems) { $output .= sprintf "%-8s %-32s %-12s %8s %6.2f%%\n", substr($remailer->{'nick'},0,8), substr($remailer->{'address'},0,32), build_list1_latencystr($remailer->{'stats'}->{'latency_day'}), make_min_hr($remailer->{'stats'}->{'avr_latency'}, 1), $remailer->{'stats'}->{'avr_reliability'} * 100; }; write_file($filebasename, $html_template, Echolot::Config::get()->{'buildstats'}, $output) or Echolot::Log::debug("writefile failed."), return 0; return 1; }; sub build_list2($$$$$$;$) { my ($rems, $type, $broken1, $broken2, $sameop, $filebasename, $html_template) = @_; my $output = ''; $output .= sprintf "Stats-Version: 2.0\n"; $output .= sprintf "Generated: %s\n", make_date(); $output .= sprintf "%-12s Latent-Hist Latent Uptime-Hist Uptime Options\n", ($type == 1 ? 'Cypherpunk' : $type == 2 ? 'Mixmaster' : "Type $type"); $output .= sprintf "------------------------------------------------------------------------\n"; for my $remailer (@$rems) { $output .= sprintf "%-12s %-12s %6s %-12s %5.1f%% %s\n", substr($remailer->{'nick'},0,12), build_list2_latencystr($remailer->{'stats'}->{'latency_day'}), make_min_hr($remailer->{'stats'}->{'avr_latency'}, 0), build_list2_reliabilitystr($remailer->{'stats'}->{'reliability_day'}), $remailer->{'stats'}->{'avr_reliability'} * 100, build_list2_capsstr($remailer->{'caps'}); }; $output .= sprintf "\nGroups of remailers sharing a machine or operator:\n$sameop\n" if (defined $sameop); $output .= sprintf "\nBroken type-I remailer chains:\n$broken1\n" if (defined $broken1); $output .= sprintf "\nBroken type-II remailer chains:\n$broken2\n" if (defined $broken2); $output .= sprintf "\n\n\nRemailer-Capabilities:\n\n"; for my $remailer (sort {$a->{'caps'} cmp $b->{'caps'}} @$rems) { $output .= $remailer->{'caps'}."\n" if defined $remailer->{'caps'}; } write_file($filebasename, $html_template, Echolot::Config::get()->{'buildstats'}, $output) or Echolot::Log::debug("writefile failed."), return 0; return 1; }; sub build_clist($$$$$;$) { my ($remhash, $broken1, $broken2, $sameop, $filebasename, $html_template) = @_; my $output = ''; $output .= sprintf "Stats-Version: 2.0.1\n"; $output .= sprintf "Generated: %s\n", make_date(); $output .= sprintf "Mixmaster Latent-Hist Latent Uptime-Hist Uptime Options Type\n"; $output .= sprintf "------------------------------------------------------------------------------------\n"; my $all; for my $type (keys %$remhash) { for my $remailer (@{$remhash->{$type}}) { $all->{ $remailer->{'nick'} }->{$type} = $remailer }; }; for my $nick (sort {$a cmp $b} keys %$all) { for my $type (sort {$a cmp $b} keys %{$all->{$nick}}) { $output .= sprintf "%-12s %-12s %6s %-12s %5.1f%% %s %s\n", $nick, build_list2_latencystr($all->{$nick}->{$type}->{'stats'}->{'latency_day'}), make_min_hr($all->{$nick}->{$type}->{'stats'}->{'avr_latency'}, 0), build_list2_reliabilitystr($all->{$nick}->{$type}->{'stats'}->{'reliability_day'}), $all->{$nick}->{$type}->{'stats'}->{'avr_reliability'} * 100, build_list2_capsstr($all->{$nick}->{$type}->{'caps'}), $type; }; }; $output .= sprintf "\nGroups of remailers sharing a machine or operator:\n$sameop\n" if (defined $sameop); $output .= sprintf "\nBroken type-I remailer chains:\n$broken1\n" if (defined $broken1); $output .= sprintf "\nBroken type-II remailer chains:\n$broken2\n" if (defined $broken2); $output .= sprintf "\n\n\nRemailer-Capabilities:\n\n"; for my $nick (sort {$a cmp $b} keys %$all) { for my $type (keys %{$all->{$nick}}) { $output .= $all->{$nick}->{$type}->{'caps'}."\n", last if defined $all->{$nick}->{$type}->{'caps'}; }; } write_file($filebasename, $html_template, Echolot::Config::get()->{'buildstats'}, $output) or Echolot::Log::debug("writefile failed."), return 0; return 1; }; sub build_rems($) { my ($types) = @_; my %rems; for my $remailer (Echolot::Globals::get()->{'storage'}->get_addresses()) { my $addr = $remailer->{'address'}; my $has_type = 0; for my $type (@$types) { $has_type = 1, last if (Echolot::Globals::get()->{'storage'}->has_type($addr, $type)); }; next unless $has_type; my $rem = { 'stats' => calculate($addr,$types), 'nick' => Echolot::Globals::get()->{'storage'}->get_nick($addr), 'caps' => Echolot::Globals::get()->{'storage'}->get_capabilities($addr), 'address' => $addr, }; $rem->{'list-it'} = $remailer->{'showit'} && defined $rem->{'caps'} && ($rem->{'caps'} !~ m/\btesting\b/i); $rem->{'latency'} = $rem->{'stats'}->{'avr_latency'}; # for sorting purposes only $rem->{'latency'} = 9999 unless defined $rem->{'latency'}; $rems{$addr} = $rem if (defined $rem->{'stats'} && defined $rem->{'nick'} && defined $rem->{'address'} && defined $rem->{'caps'} ); }; my $sort_by_latency = Echolot::Config::get()->{'stats_sort_by_latency'}; my @rems = sort { - ($a->{'stats'}->{'avr_reliability'} <=> $b->{'stats'}->{'avr_reliability'}) || (($a->{'latency'} <=> $b->{'latency'}) * $sort_by_latency) || ($a->{'nick'} cmp $b->{'nick'}) } map { $rems{$_} } keys %rems; return \@rems; }; sub compress_broken_chain($@) { my ($num, @list) = @_; my %unique = (); @list = sort { $a cmp $b} grep { ! $unique{$_}++; } @list; my %bad_left; my %bad_right; for my $chain (@list) { chomp $chain; my ($left, $right) = $chain =~ m/\((\S+) \s (\S+)\)/x or Echolot::Log::warn("Could not parse bad chain '$chain'."), next; $bad_right{$right}++; $bad_right{$right} += $num if ($left eq '*'); $bad_left {$left }++; $bad_left {$left } += $num if ($right eq '*'); }; my $threshold = $num * Echolot::Config::get()->{'chainping_allbad_factor'}; my @result = (); for my $key (keys %bad_right) { delete $bad_right{$key}, next if $bad_right{$key} < $threshold; push @result, "(* $key)"; }; for my $key (keys %bad_left) { delete $bad_left{$key}, next if $bad_left{$key} < $threshold; push @result, "($key *)"; }; for my $chain (@list) { chomp $chain; my ($left, $right) = $chain =~ m/\((\S+) \s (\S+)\)/x or # Echolot::Log::warn("Could not parse bad chain '$chain'."), -- don't warn again push(@result, $chain), next; next if defined $bad_right{$right}; next if defined $bad_left {$left }; push(@result, $chain), }; %unique = (); @result = sort { $a cmp $b} grep { ! $unique{$_}++; } @result; return @result; }; sub find_broken_chains($$$) { my ($chaintype, $rems, $hard) = @_; if (!defined $LAST_BROKENCHAIN_RUN{$chaintype} || $LAST_BROKENCHAIN_RUN{$chaintype} < time() - Echolot::Config::get()->{'chainping_update'} || ! defined $BROKEN_CHAINS{$chaintype} ) { Echolot::Log::debug ("Broken Chains $chaintype need generating."), $LAST_BROKENCHAIN_RUN{$chaintype} = time(); my $pings = Echolot::Globals::get()->{'storage'}->get_chainpings($chaintype); my @intensive_care = (); my %remailers = map { $_->{'address'} => $_ } @$rems; my $stats; my %received; my @broken_chains; for my $status (qw{done out}) { my $status_done = $status eq 'done'; my $status_out = $status eq 'out'; for my $ping (@{$pings->{$status}}) { my $addr1 = $ping->{'addr1'}; my $addr2 = $ping->{'addr2'}; my $sent = $ping->{'sent'}; next if $sent < (time() - Echolot::Config::get()->{'chainping_period'}); next unless defined $remailers{$addr1}; next unless defined $remailers{$addr2}; if ($status_done) { $received{$addr1.':'.$addr2.':'.$sent} = 1; }; if ($status_out && !defined $received{$addr1.':'.$addr2.':'.$sent}) { my $lat1 = $remailers{$addr1}->{'stats'}->{'avr_latency'}; my $lat2 = $remailers{$addr2}->{'stats'}->{'avr_latency'}; $lat1 = 0 unless defined $lat1; $lat2 = 0 unless defined $lat2; my $theoretical_lat = $lat1 + $lat2; $theoretical_lat = 0 unless defined $theoretical_lat; my $latency = time() - $ping->{'sent'}; # print ("lat helps $latency < ".int($theoretical_lat * Echolot::Config::get()->{'chainping_grace'})." $addr1 $addr2\n"), next if ($latency < $theoretical_lat * Echolot::Config::get()->{'chainping_grace'}); }; # print "Having $addr1 $addr2 $status at $sent\n"; $stats->{$addr1}->{$addr2}->{$status}++; }; }; # require Data::Dumper; # print Data::Dumper->Dump([$stats]); for my $addr1 (keys %$stats) { for my $addr2 (keys %{$stats->{$addr1}}) { my $theoretical_rel = $remailers{$addr1}->{'stats'}->{'avr_reliability'} * $remailers{$addr2}->{'stats'}->{'avr_reliability'}; my $out = $stats->{$addr1}->{$addr2}->{'out'}; my $done = $stats->{$addr1}->{$addr2}->{'done'}; $done = 0 unless defined $done; ($out < Echolot::Config::get()->{'chainping_minsample'} && $done == 0) and push (@intensive_care, { addr1 => $addr1, addr2 => $addr2, reason => "only $out sample".($out>1?'s':'').", none returned so far" }), next; ($out > 0) or Echolot::Log::debug("Should not devide through zero ($done/$out) for $addr1, $addr2."), next; my $real_rel = $done / $out; # print "$addr1 $addr2 $done / $out == $real_rel ($theoretical_rel)\n"; next if ($real_rel > $theoretical_rel * Echolot::Config::get()->{'chainping_fudge'}); my $nick1 = $remailers{$addr1}->{'nick'}; my $nick2 = $remailers{$addr2}->{'nick'}; push @broken_chains, { public => $remailers{$addr1}->{'list-it'} && $remailers{$addr2}->{'list-it'}, chain => "($nick1 $nick2)" }; push @intensive_care, { addr1 => $addr1, addr2 => $addr2, reason => "bad: $done/$out" }; }; }; $BROKEN_CHAINS{$chaintype} = \@broken_chains; Echolot::Chain::set_intensive_care($chaintype, \@intensive_care); } else { Echolot::Log::debug ("Broken Chains $chaintype are up to date."), }; my @hard = defined $hard ? (split /\n/, $hard) : (); my @pub = @hard; my @priv = @hard; push @pub, map { $_->{'chain'} } grep { $_->{'public'} } @{ $BROKEN_CHAINS{$chaintype} }; push @priv, map { $_->{'chain'} } @{ $BROKEN_CHAINS{$chaintype} }; my $pub = join "\n", compress_broken_chain(scalar @$rems, @pub); my $priv = join "\n", compress_broken_chain(scalar @$rems, @priv); return ($pub, $priv); }; sub build_lists() { my $clist; my $pubclist; my $rems; my $pubrems; my %stats; my %addresses; my $hardbroken1 = Echolot::Tools::read_file( Echolot::Config::get()->{'broken1'}, 1); my $hardbroken2 = Echolot::Tools::read_file( Echolot::Config::get()->{'broken2'}, 1); my $sameop = Echolot::Tools::read_file( Echolot::Config::get()->{'sameop'}, 1); my $pubbroken1; my $pubbroken2; my $privbroken1; my $privbroken2; my $mixrems = build_rems(['mix']); my $cpunkrems = build_rems(['cpunk-rsa', 'cpunk-dsa', 'cpunk-clear']); if (Echolot::Config::get()->{'do_chainpings'}) { ($pubbroken1, $privbroken1) = find_broken_chains('cpunk', $cpunkrems, $hardbroken1); ($pubbroken2, $privbroken2) = find_broken_chains('mix' , $mixrems , $hardbroken2); } else { $pubbroken1 = $privbroken1 = $hardbroken1; $pubbroken2 = $privbroken2 = $hardbroken2; }; unless (Echolot::Config::get()->{'show_chainpings'}) { $pubbroken1 = $hardbroken1; $pubbroken2 = $hardbroken2; }; $rems = $mixrems; $mixrems = undef; @$pubrems = grep { $_->{'list-it'} } @$rems; build_mlist1( $rems, $privbroken1, $privbroken2, $sameop, Echolot::Config::get()->{'private_resultdir'}.'/'.'mlist', 'mlist'); build_list2( $rems, 2, $privbroken1, $privbroken2, $sameop, Echolot::Config::get()->{'private_resultdir'}.'/'.'mlist2', 'mlist2'); build_mlist1( $pubrems, $pubbroken1, $pubbroken2, $sameop, Echolot::Config::get()->{'resultdir'}.'/'.'mlist', 'mlist'); build_list2( $pubrems, 2, $pubbroken1, $pubbroken2, $sameop, Echolot::Config::get()->{'resultdir'}.'/'.'mlist2', 'mlist2'); $stats{'mix_total'} = scalar @$pubrems; $stats{'mix_98'} = scalar grep { $_->{'stats'}->{'avr_reliability'} >= 0.98 } @$pubrems; $addresses{$_->{'address'}}=1 for @$pubrems; if (Echolot::Config::get()->{'combined_list'}) { $clist->{'mix'} = $rems; $pubclist->{'mix'} = $pubrems; $pubrems = undef; }; $rems = $cpunkrems; $cpunkrems = undef; @$pubrems = grep { $_->{'list-it'} } @$rems; build_rlist1( $rems, $privbroken1, $privbroken2, $sameop, Echolot::Config::get()->{'private_resultdir'}.'/'.'rlist', 'rlist'); build_list2( $rems, 1, $privbroken1, $privbroken2, $sameop, Echolot::Config::get()->{'private_resultdir'}.'/'.'rlist2', 'rlist2'); build_rlist1( $pubrems, $pubbroken1, $pubbroken2, $sameop, Echolot::Config::get()->{'resultdir'}.'/'.'rlist', 'rlist'); build_list2( $pubrems, 1, $pubbroken1, $pubbroken2, $sameop, Echolot::Config::get()->{'resultdir'}.'/'.'rlist2', 'rlist2'); $stats{'cpunk_total'} = scalar @$pubrems; $stats{'cpunk_98'} = scalar grep { $_->{'stats'}->{'avr_reliability'} >= 0.98 } @$pubrems; $addresses{$_->{'address'}}=1 for @$pubrems; if (Echolot::Config::get()->{'combined_list'} && ! Echolot::Config::get()->{'separate_rlists'}) { $clist->{'cpunk'} = $rems; $pubclist->{'cpunk'} = $pubrems; $pubrems = undef; }; if (Echolot::Config::get()->{'separate_rlists'}) { $rems = build_rems(['cpunk-rsa']); @$pubrems = grep { $_->{'list-it'} } @$rems; build_rlist1( $rems, $privbroken1, $privbroken2, $sameop, Echolot::Config::get()->{'private_resultdir'}.'/'.'rlist-rsa', 'rlist-rsa'); build_list2( $rems, 1, $privbroken1, $privbroken2, $sameop, Echolot::Config::get()->{'private_resultdir'}.'/'.'rlist2-rsa', 'rlist2-rsa'); build_rlist1( $pubrems, $pubbroken1, $pubbroken2, $sameop, Echolot::Config::get()->{'resultdir'}.'/'.'rlist-rsa', 'rlist-rsa'); build_list2( $pubrems, 1, $pubbroken1, $pubbroken2, $sameop, Echolot::Config::get()->{'resultdir'}.'/'.'rlist2-rsa', 'rlist2-rsa'); if (Echolot::Config::get()->{'combined_list'}) { $clist->{'cpunk-rsa'} = $rems; $pubclist->{'cpunk-rsa'} = $pubrems; $pubrems = undef; }; $rems = build_rems(['cpunk-dsa']); @$pubrems = grep { $_->{'list-it'} } @$rems; build_rlist1( $rems, $privbroken1, $privbroken2, $sameop, Echolot::Config::get()->{'private_resultdir'}.'/'.'rlist-dsa', 'rlist-dsa'); build_list2( $rems, 1, $privbroken1, $privbroken2, $sameop, Echolot::Config::get()->{'private_resultdir'}.'/'.'rlist2-dsa', 'rlist2-dsa'); build_rlist1( $pubrems, $pubbroken1, $pubbroken2, $sameop, Echolot::Config::get()->{'resultdir'}.'/'.'rlist-dsa', 'rlist-dsa'); build_list2( $pubrems, 1, $pubbroken1, $pubbroken2, $sameop, Echolot::Config::get()->{'resultdir'}.'/'.'rlist2-dsa', 'rlist2-dsa'); if (Echolot::Config::get()->{'combined_list'}) { $clist->{'cpunk-dsa'} = $rems; $pubclist->{'cpunk-dsa'} = $pubrems; $pubrems = undef; }; $rems = build_rems(['cpunk-clear']); @$pubrems = grep { $_->{'list-it'} } @$rems; build_rlist1( $rems, $privbroken1, $privbroken2, $sameop, Echolot::Config::get()->{'private_resultdir'}.'/'.'rlist-clear', 'rlist-clear'); build_list2( $rems, 1, $privbroken1, $privbroken2, $sameop, Echolot::Config::get()->{'private_resultdir'}.'/'.'rlist2-clear', 'rlist2-clear'); build_rlist1( $pubrems, $pubbroken1, $pubbroken2, $sameop, Echolot::Config::get()->{'resultdir'}.'/'.'rlist-clear', 'rlist-clear'); build_list2( $pubrems, 1, $pubbroken1, $pubbroken2, $sameop, Echolot::Config::get()->{'resultdir'}.'/'.'rlist2-clear', 'rlist2-clear'); if (Echolot::Config::get()->{'combined_list'}) { $clist->{'cpunk-clear'} = $rems; $pubclist->{'cpunk-clear'} = $pubrems; $pubrems = undef; }; }; if (Echolot::Config::get()->{'combined_list'}) { build_clist( $clist, $privbroken1, $privbroken2, $sameop, Echolot::Config::get()->{'private_resultdir'}.'/'.'clist', 'clist'); build_clist( $pubclist, $pubbroken1, $pubbroken2, $sameop, Echolot::Config::get()->{'resultdir'}.'/'.'clist', 'clist'); }; $stats{'unique_addresses'} = scalar keys %addresses; Echolot::Tools::write_HTML_file( Echolot::Config::get()->{'resultdir'}.'/'.Echolot::Config::get()->{'indexfilebasename'}, 'indexfile', Echolot::Config::get()->{'buildstats'}, %stats ); my $file = Echolot::Config::get()->{'echolot_css'}, my $css; { local $/ = undef; open(F, $file) or Echolot::Log::warn("Could not open $file: $!."), return 0; $css = ; close (F) or Echolot::Log::warn("Cannot close $file: $!."), return 0; } $file = Echolot::Config::get()->{'resultdir'}.'/echolot.css'; open(F, '>'.$file) or Echolot::Log::warn("Cannot open $file: $!."), return 0; print F $css or Echolot::Log::warn("Cannot print to $file: $!."), return 0; close (F) or Echolot::Log::warn("Cannot close $file: $!."), return 0; }; sub build_mixring() { my @filenames; my $filename = Echolot::Config::get()->{'resultdir'}.'/pubring.mix'; push @filenames, $filename; open(F, '>'.$filename) or Echolot::Log::warn("Cannot open $filename: $!."), return 0; $filename = Echolot::Config::get()->{'resultdir'}.'/type2.list'; push @filenames, $filename; open(T2L, '>'.$filename) or Echolot::Log::warn("Cannot open $filename: $!."), return 0; $filename = Echolot::Config::get()->{'private_resultdir'}.'/pubring.mix'; push @filenames, $filename; open(F_PRIV, '>'.$filename) or Echolot::Log::warn("Cannot open $filename: $!."), return 0; $filename = Echolot::Config::get()->{'private_resultdir'}.'/type2.list'; push @filenames, $filename; open(T2L_PRIV, '>'.$filename) or Echolot::Log::warn("Cannot open $filename: $!."), return 0; my $data; for my $remailer (Echolot::Globals::get()->{'storage'}->get_addresses()) { my $addr = $remailer->{'address'}; next unless Echolot::Globals::get()->{'storage'}->has_type($addr, 'mix'); my %key; for my $keyid (Echolot::Globals::get()->{'storage'}->get_keys($addr, 'mix')) { my %new_key = Echolot::Globals::get()->{'storage'}->get_key($addr, 'mix', $keyid); if (!defined $key{'last_update'} || $key{'last_update'} < $new_key{'last_update'} ) { %key = %new_key; }; }; my $caps = Echolot::Globals::get()->{'storage'}->get_capabilities($addr); $key{'list-it'} = $remailer->{'showit'} && defined $caps && ($caps !~ m/\btesting\b/i); if ( defined Echolot::Globals::get()->{'storage'}->get_nick($addr) ) { $data->{$key{'summary'}} = \%key; $data->{$key{'summary'}} = \%key; }; }; for my $indx (sort {$a cmp $b} keys %$data) { my $key = $data->{$indx}; if ($key->{'list-it'}) { print F $key->{'summary'}."\n\n"; print F $key->{'key'},"\n\n"; print T2L $key->{'summary'},"\n"; }; print F_PRIV $key->{'summary'}."\n\n"; print F_PRIV $key->{'key'},"\n\n"; print T2L_PRIV $key->{'summary'},"\n"; }; close(F); close(T2L); close(F_PRIV); close(T2L_PRIV); for my $filename (@filenames) { Echolot::Tools::write_meta_information($filename, Expires => time + Echolot::Config::get()->{'buildkeys'}) or Echolot::Log::debug ("Error while writing meta information for $filename."), return 0; }; }; sub build_pgpring_type($$$$) { my ($type, $GnuPG, $keyring, $keyids) = @_; for my $remailer (Echolot::Globals::get()->{'storage'}->get_addresses()) { my $addr = $remailer->{'address'}; next unless Echolot::Globals::get()->{'storage'}->has_type($addr, $type); my %key; my $final_keyid; for my $keyid (Echolot::Globals::get()->{'storage'}->get_keys($addr, $type)) { my %new_key = Echolot::Globals::get()->{'storage'}->get_key($addr, $type, $keyid); if (!defined $key{'last_update'} || $key{'last_update'} < $new_key{'last_update'} ) { %key = %new_key; $final_keyid = $keyid; }; }; # only if we have a conf if ( defined Echolot::Globals::get()->{'storage'}->get_nick($addr) ) { my ( $stdin_fh, $stdout_fh, $stderr_fh, $status_fh, $handles ) = Echolot::Tools::make_gpg_fds(); my $pid = $GnuPG->wrap_call( commands => [qw{--no-options --no-secmem-warning --no-default-keyring --fast-list-mode --keyring}, $keyring, '--import'], command_args => ['--', '-'], handles => $handles ); my ($stdout, $stderr, $status) = Echolot::Tools::readwrite_gpg($key{'key'}, $stdin_fh, $stdout_fh, $stderr_fh, $status_fh); waitpid $pid, 0; ($stdout eq '') or Echolot::Log::info("GnuPG returned something in stdout '$stdout' while adding key for '$addr': So what?"); # See DETAIL.gz in GnuPG's doc directory for syntax of GnuPG status my ($count, $count_imported) = $status =~ /^\[GNUPG:\] IMPORT_RES (\d+) \d+ (\d+)/m; if ($count_imported > 1) { Echolot::Log::info("GnuPG status '$status' indicates more than one key for '$addr' imported. Ignoring."); } elsif ($count_imported < 1) { Echolot::Log::info("GnuPG status '$status' didn't indicate key for '$addr' was imported correctly. Ignoring."); }; my $caps = Echolot::Globals::get()->{'storage'}->get_capabilities($addr); $keyids->{$final_keyid} = $remailer->{'showit'} && defined $caps && ($caps !~ m/\btesting\b/i); }; }; return 1; }; sub build_pgpring_export($$$$) { my ($GnuPG, $keyring, $file, $keyids) = @_; my ( $stdin_fh, $stdout_fh, $stderr_fh, $status_fh, $handles ) = Echolot::Tools::make_gpg_fds(); my $pid = $GnuPG->wrap_call( commands => [qw{--no-options --no-secmem-warning --no-default-keyring --keyring}, $keyring, '--export'], command_args => ['--', @$keyids ], handles => $handles ); my ($stdout, $stderr, $status) = Echolot::Tools::readwrite_gpg('', $stdin_fh, $stdout_fh, $stderr_fh, $status_fh); waitpid $pid, 0; open (F, ">$file") or Echolot::Log::warn ("Cannot open '$file': $!."), return 0; print F $stdout; close F; Echolot::Tools::write_meta_information($file, Expires => time + Echolot::Config::get()->{'buildkeys'}) or Echolot::Log::debug ("Error while writing meta information for $file."), return 0; return 1; }; sub build_pgpring() { my $GnuPG = new GnuPG::Interface; $GnuPG->call( Echolot::Config::get()->{'gnupg'} ) if (Echolot::Config::get()->{'gnupg'}); $GnuPG->options->hash_init( armor => 1, homedir => Echolot::Config::get()->{'gnupghome'} ); $GnuPG->options->meta_interactive( 0 ); my $keyring = Echolot::Config::get()->{'tmpdir'}.'/'. Echolot::Globals::get()->{'hostname'}.".".time.'.'.$PROCESS_ID.'_'.Echolot::Globals::get()->{'internalcounter'}++.'.keyring'; my $keyids = {}; build_pgpring_type('cpunk-rsa', $GnuPG, $keyring, $keyids) or Echolot::Log::debug("build_pgpring_type failed."), return undef; build_pgpring_export($GnuPG, $keyring, Echolot::Config::get()->{'resultdir'}.'/pgp-rsa.asc', [ grep {$keyids->{$_}} keys %$keyids ]) or Echolot::Log::debug("build_pgpring_export failed."), return undef; build_pgpring_export($GnuPG, $keyring, Echolot::Config::get()->{'private_resultdir'}.'/pgp-rsa.asc', [ keys %$keyids ]) or Echolot::Log::debug("build_pgpring_export failed."), return undef; build_pgpring_type('cpunk-dsa', $GnuPG, $keyring, $keyids) or Echolot::Log::debug("build_pgpring_type failed."), return undef; build_pgpring_export($GnuPG, $keyring, Echolot::Config::get()->{'resultdir'}.'/pgp-all.asc', [ grep {$keyids->{$_}} keys %$keyids ]) or Echolot::Log::debug("build_pgpring_export failed."), return undef; build_pgpring_export($GnuPG, $keyring, Echolot::Config::get()->{'private_resultdir'}.'/pgp-all.asc', [ keys %$keyids ]) or Echolot::Log::debug("build_pgpring_export failed."), return undef; unlink ($keyring) or Echolot::Log::warn("Cannot unlink tmp keyring '$keyring'."), return undef; unlink ($keyring.'~'); # gnupg does those evil backups }; sub build_stats() { $STATS_DAYS = Echolot::Config::get()->{'stats_days'}; $SECONDS_PER_DAY = Echolot::Config::get()->{'seconds_per_day'}; $WEIGHT = Echolot::Config::get()->{'pings_weight'}; build_lists(); }; sub build_keys() { build_mixring(); build_pgpring(); }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Log.pm0000644000175000017500000000673412422221767015175 0ustar weaselweaselpackage Echolot::Log; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Globals - echolot global variables =head1 DESCRIPTION =cut use strict; use Carp qw{}; #use Time::HiRes qw( gettimeofday ); my %LOGLEVELS = qw{ trace 8 debug 7 info 6 notice 5 warn 4 warning 4 error 3 critical 2 alert 1 emergency 0 }; my $LOGLEVEL; my $LOGFILE; my $LOGFH; my @monnames = qw{Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec}; sub header_log($$) { my ($level, $msg) = @_; #my ($secs, $msecs) = gettimeofday(); #my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime( $secs ); #my $time = sprintf("%s %02d %02d:%02d:%02d.%06d", # $monnames[$mon], # $mday, # $hour, $min, $sec, $msecs); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); my $time = sprintf("%s %02d %02d:%02d:%02d", $monnames[$mon], $mday, $hour, $min, $sec); my $prefix = $time.' ['.uc($level).'] '; my $logstring = $msg."\n"; my $first = 0; $logstring =~ s/^/ $prefix . ($first++ ? ' ' : '' ) /emg; return $logstring; }; sub reopen() { $LOGFH->close() if ($LOGFH->opened()); open($LOGFH, ">>".$LOGFILE) or warn("Cannot open logfile $LOGFILE: $!"); }; sub init() { $LOGFILE = Echolot::Config::get()->{'logfile'}; $LOGLEVEL = Echolot::Config::get()->{'loglevel'}; $LOGFH = new IO::Handle; die ("Logfile not defined") unless defined ($LOGFILE); die ("Loglevel not defined") unless defined ($LOGLEVEL); die ("Loglevel $LOGLEVEL unkown") unless defined ($LOGLEVELS{$LOGLEVEL}); $LOGLEVEL = $LOGLEVELS{$LOGLEVEL}; reopen(); }; sub log_message($$) { my ($level, $msg) = @_; die("Loglevel $level unkown.") unless defined $LOGLEVELS{$level}; return if $LOGLEVELS{$level} > $LOGLEVEL; $msg = header_log($level, $msg); print $LOGFH $msg; $LOGFH->flush(); }; sub trace($) { log_message('trace', $_[0]); }; sub debug($) { log_message('debug', $_[0]); }; sub info($) { log_message('info', $_[0]); }; sub notice($) { log_message('notice', $_[0]); }; sub warn($) { log_message('warn', $_[0]); }; sub warning($) { log_message('warning', $_[0]); }; sub error($) { log_message('error', $_[0]); }; sub critical($) { log_message('critical', $_[0]); }; sub alert($) { log_message('alert', $_[0]); }; sub emergency($) { log_message('emergency', $_[0]); }; sub logdie($) { my ($msg) = @_; critical($msg); die($msg); }; sub cluck($) { my ($msg) = @_; my $longmess = Carp::longmess(); $longmess =~ s/^/ /mg; $msg .= "\n".$longmess; warning($msg); }; sub confess($) { my ($msg) = @_; my $longmess = Carp::longmess(); $longmess =~ s/^/ /mg; $msg .= "\n".$longmess; error($msg); die($msg); }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Pinger/0002755000175000017500000000000012422221767015332 5ustar weaselweaselecholot-2.1.9/Echolot/Pinger/CPunk.pm0000644000175000017500000001500612422221767016710 0ustar weaselweaselpackage Echolot::Pinger::CPunk; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Pinger::CPunk - send cypherpunk pings =head1 DESCRIPTION This package provides functions for sending cypherpunk (type I) pings. =cut use strict; use English; use GnuPG::Interface; use Echolot::Log; sub encrypt_to($$$$) { my ($msg, $recipient, $keys, $pgp2compat) = @_; (defined $keys->{$recipient}) or Echolot::Log::warn("Key for recipient $recipient is not defined."), return undef; (defined $keys->{$recipient}->{'key'}) or Echolot::Log::warn("Key->key for recipient $recipient is not defined."), return undef; my $keyring = Echolot::Config::get()->{'tmpdir'}.'/'. Echolot::Globals::get()->{'hostname'}.".".time.'.'.$PROCESS_ID.'_'.Echolot::Globals::get()->{'internalcounter'}++.'.keyring'; my $GnuPG = new GnuPG::Interface; $GnuPG->call( Echolot::Config::get()->{'gnupg'} ) if (Echolot::Config::get()->{'gnupg'}); $GnuPG->options->hash_init( homedir => Echolot::Config::get()->{'gnupghome'} ); $GnuPG->options->meta_interactive( 0 ); my ( $stdin_fh, $stdout_fh, $stderr_fh, $status_fh, $handles ) = Echolot::Tools::make_gpg_fds(); my $pid = $GnuPG->wrap_call( commands => [qw{--no-options --no-secmem-warning --no-default-keyring --fast-list-mode --keyring}, $keyring, '--import'], command_args => ['--', '-' ], handles => $handles ); my ($stdout, $stderr, $status) = Echolot::Tools::readwrite_gpg($keys->{$recipient}->{'key'}, $stdin_fh, $stdout_fh, $stderr_fh, $status_fh); waitpid $pid, 0; ($stdout eq '') or Echolot::Log::info("GnuPG returned something in stdout '$stdout' while adding key for '$recipient': So what?"); #($stderr eq '') or #Echolot::Log::warn("GnuPG returned something in stderr: '$stderr' while adding key for '$recipient'; returning."), #return undef; ($status =~ /^^\[GNUPG:\] IMPORTED $recipient /m) or Echolot::Log::info("GnuPG status '$status' didn't indicate key for '$recipient' was imported correctly."), return undef; #$msg =~ s/\r?\n/\r\n/g; $GnuPG->options->hash_init( armor => 1 ); ( $stdin_fh, $stdout_fh, $stderr_fh, $status_fh, $handles ) = Echolot::Tools::make_gpg_fds(); my $plaintextfile; # Files are required for compaitibility with PGP 2.* # we also use files in all other cases since there is a bug in either GnuPG or GnuPG::Interface # that let Echolot die if in certain cases: # If a key is unuseable because it expired and we want to encrypt something to it # pingd dies if there is only enough time between calling encrypt() and printing the message # to GnuPG. (a sleep 1 triggered that reproduceably) $plaintextfile = Echolot::Config::get()->{'tmpdir'}.'/'. Echolot::Globals::get()->{'hostname'}.".".time.'.'.$PROCESS_ID.'_'.Echolot::Globals::get()->{'internalcounter'}++.'.plaintext'; open (F, '>'.$plaintextfile) or Echolot::Log::warn("Cannot open $plaintextfile for writing: $!."), return 0; print (F $msg); close (F) or Echolot::Log::warn("Cannot close $plaintextfile."), return 0; my $commands = [qw{--no-options --no-secmem-warning --always-trust --no-default-keyring --textmode --cipher-algo 3DES --keyring}, $keyring, '--recipient', $recipient, '--encrypt']; my $command_args = ['--', $plaintextfile]; $pid = $GnuPG->wrap_call( commands => $commands, command_args => $command_args, handles => $handles ); ($stdout, $stderr, $status) = Echolot::Tools::readwrite_gpg('', $stdin_fh, $stdout_fh, $stderr_fh, $status_fh); waitpid $pid, 0; #($stderr eq '') or #Echolot::Log::warn("GnuPG returned something in stderr: '$stderr' while encrypting to '$recipient'."), #return undef; ($status =~ /^\[GNUPG:\] KEYEXPIRED (\d+)/m) and Echolot::Log::info("Key $recipient expired at ".scalar gmtime($1)." UTC"), return undef; (($status =~ /^\[GNUPG:\] BEGIN_ENCRYPTION\s/m) && ($status =~ /^\[GNUPG:\] END_ENCRYPTION\s/m)) or Echolot::Log::info("GnuPG status '$status' didn't indicate message to '$recipient' was encrypted correctly (stderr: $stderr; args: ".join(' ', @$commands, @$command_args).")."), return undef; unlink ($keyring) or Echolot::Log::warn("Cannot unlink tmp keyring '$keyring'."), return undef; unlink ($keyring.'~'); # gnupg does those evil backups (defined $plaintextfile) and (unlink ($plaintextfile) or Echolot::Log::warn("Cannot unlink tmp plaintextfile '$plaintextfile'."), return undef); my $result; $plaintextfile .= '.asc'; open (F, '<'.$plaintextfile) or Echolot::Log::warn("Cannot open $plaintextfile for reading: $!."), return 0; $result = join '', ; close (F) or Echolot::Log::warn("Cannot close $plaintextfile."), return 0; (defined $plaintextfile) and (unlink ($plaintextfile) or Echolot::Log::warn("Cannot unlink tmp plaintextfile '$plaintextfile'."), return undef); $result =~ s,^Version: .*$,Version: N/A,m; #$result =~ s/\r?\n/\r\n/g; return $result; }; sub ping($$$$$) { my ($body, $to, $with_from, $chain, $keys) = @_; my $msg = $body; for my $hop (reverse @$chain) { my $header = ''; if ($with_from) { my $address = Echolot::Config::get()->{'my_localpart'} . '@' . Echolot::Config::get()->{'my_domain'}; $header = "##\nFrom: Echolot Pinger <$address>\n\n"; $with_from = 0; }; # "Latent-Time: +0\n". $msg = "::\n". "Anon-To: $to\n". "\n". $header. $msg; if ($hop->{'encrypt'}) { my $encrypted = encrypt_to($msg, $hop->{'keyid'}, $keys, $hop->{'pgp2compat'}); (defined $encrypted) or Echolot::Log::debug("Encrypted is undefined."), return undef; $msg = "::\n". "Encrypted: PGP\n". "\n". $encrypted; }; $to = $hop->{'address'}; } Echolot::Tools::send_message( To => $to, Body => $msg ); return 1; }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Pinger/Mix.pm0000644000175000017500000001101712422221767016423 0ustar weaselweaselpackage Echolot::Pinger::Mix; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Pinger::Mix - send mix pings =head1 DESCRIPTION This package provides functions for sending mixmaster (type II) pings. =cut use strict; use English; use IO::Handle; use Echolot::Log; sub ping($$$$$) { my ($body, $to, $with_from, $chain, $keys) = @_; my $chaincomma = join (',', @$chain); my $keyring = Echolot::Config::get()->{'mixhome'}.'/pubring.mix'; open (F, '>'.$keyring) or Echolot::Log::warn("Cannot open $keyring for writing: $!."), return 0; for my $keyid (keys %$keys) { print (F $keys->{$keyid}->{'summary'}, "\n\n"); print (F $keys->{$keyid}->{'key'},"\n\n"); }; close (F) or Echolot::Log::warn("Cannot close $keyring: $!."), return 0; my $type2list = Echolot::Config::get()->{'mixhome'}.'/type2.list'; open (F, '>'.$type2list) or Echolot::Log::warn("Cannot open $type2list for writing: $!."), return 0; for my $keyid (keys %$keys) { print (F $keys->{$keyid}->{'summary'}, "\n"); }; close (F) or Echolot::Log::warn("Cannot close $type2list: $!."), return 0; my $mixcfg = Echolot::Config::get()->{'mixhome'}.'/mix.cfg'; my $address = Echolot::Config::get()->{'my_localpart'} . '@' . Echolot::Config::get()->{'my_domain'}; my $sendmail = Echolot::Config::get()->{'sendmail'}; open (F, ">$mixcfg") or Echolot::Log::warn("Cannot open $mixcfg for writing: $!."), return 0; print (F "REMAIL n\n"); print (F "NAME Echolot Pinger\n"); print (F "ADDRESS $address\n"); print (F "PUBRING pubring.mix\n"); print (F "TYPE2LIST type2.list\n"); print (F "SENDMAIL $sendmail -f $address -t\n"); print (F "VERBOSE 0\n"); print (F "INDUMMYP 0\n"); print (F "OUTDUMMYP 0\n"); close (F) or Echolot::Log::warn("Cannot close $mixcfg: $!."), return 0; my ($stdinR, $stdinW) = (IO::Handle->new(), IO::Handle->new()); my ($stdoutR, $stdoutW) = (IO::Handle->new(), IO::Handle->new()); my ($stderrR, $stderrW) = (IO::Handle->new(), IO::Handle->new()); pipe $stdinR, $stdinW; pipe $stdoutR, $stdoutW; pipe $stderrR, $stderrW; my $pid = fork(); defined $pid or Echolot::Log::warn("Cannot fork for calling mixmaster: $!."), return 0; unless ($pid) { # child $stdinW->close; $stdoutR->close; $stderrR->close; close STDIN; close STDOUT; close STDERR; open (STDIN, "<&".$stdinR->fileno) or Echolot::Log::warn ("Cannot dup stdinR (fd ".$stdinR->fileno.") as STDIN: $!"); open (STDOUT, ">&".$stdoutW->fileno) or Echolot::Log::warn ("Cannot dup stdoutW (fd ".$stdoutW->fileno.") as STDOUT: $!"); open (STDERR, ">&".$stderrW->fileno) or Echolot::Log::warn ("Cannot dup stderrW (fd ".$stderrW->fileno.") as STDERE: $!"); $ENV{'MIXPATH'} = Echolot::Config::get()->{'mixhome'}; { exec(Echolot::Config::get()->{'mixmaster'}, qw{-m -S -l}, $chaincomma); }; Echolot::Log::warn("Cannot exec mixpinger: $!."); exit(1); }; $stdinR->close; $stdoutW->close; $stderrW->close; my $msg; $msg .= "From: Echolot Pinger <$address>\n" if $with_from; $msg .= "To: $to\n\n$body\n"; Echolot::Log::debug("mixping: fds: stdinW $stdinW; stdoutR $stdoutR; stderrR $stderrR."), my ($stdout, $stderr, undef) = Echolot::Tools::readwrite_gpg($msg, $stdinW, $stdoutR, $stderrR, undef); waitpid $pid, 0; $stderr =~ s/\n+$//; Echolot::Log::debug("Mixmaster said on unfiltered stderr: $stderr") if ($stderr ne ''); $stderr =~ s/^Chain: .*//mg; $stderr =~ s/^Warning: The message has a From: line.*//mg; $stderr =~ s/\n+$//; Echolot::Log::info("Mixmaster said on stdout: $stdout") if ($stdout ne ''); Echolot::Log::warn("Mixmaster said on stderr: $stderr") if ($stderr ne ''); return 1; }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Tools.pm0000644000175000017500000003167212422221767015553 0ustar weaselweaselpackage Echolot::Tools; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Tools - Tools for echolot =head1 DESCRIPTION =cut use strict; use HTML::Template; use Digest::MD5 qw{}; use IO::Select; use IO::Handle; use GnuPG::Interface; use Echolot::Log; use English; sub hash($) { my ($data) = @_; ($data) = $data =~ m/(.*)/s; # untaint my $hash = Digest::MD5::md5_hex($data); return $hash; }; sub make_random($;%) { my ($length, %args) = @_; my $random; open (FH, Echolot::Config::get()->{'dev_random'}) or Echolot::Log::warn("Cannot open ".Echolot::Config::get()->{'dev_random'}." for reading: $!."), return 0; read(FH, $random, $length) or Echolot::Log::warn("Cannot read from ".Echolot::Config::get()->{'dev_random'}.": $!."), return 0; close (FH) or Echolot::Log::warn("Cannot close ".Echolot::Config::get()->{'dev_random'}.": $!."), return 0; $random = unpack('H*', $random) if ($args{'armor'} == 1); return $random; }; sub make_mac($) { my ($token) = @_; my $mac = hash($token . Echolot::Globals::get()->{'storage'}->get_secret() ); return $mac; }; sub makeShortNumHash($) { my ($text) = @_; my $hash = Echolot::Tools::make_mac($text); $hash = substr($hash, 0, 4); my $sum = hex($hash); return $sum; }; sub verify_mac($$) { my ($token, $mac) = @_; return (hash($token . Echolot::Globals::get()->{'storage'}->get_secret() ) eq $mac); }; sub make_address($) { my ($subsystem) = @_; my $token = $subsystem.'='.time(); my $hash = hash($token . Echolot::Globals::get()->{'storage'}->get_secret() ); my $cut_hash = substr($hash, 0, Echolot::Config::get()->{'hash_len'}); my $complete_token = $token.'='.$cut_hash; my $address = Echolot::Config::get()->{'recipient_delimiter'} ne ''? Echolot::Config::get()->{'my_localpart'}. Echolot::Config::get()->{'recipient_delimiter'}. $complete_token. '@'. Echolot::Config::get()->{'my_domain'} : Echolot::Config::get()->{'my_localpart'}. '@'. Echolot::Config::get()->{'my_domain'}. '('. $complete_token. ')'; return $address; }; sub verify_address_tokens($) { my ($address) = @_; my ($type, $timestamp, $received_hash); if (Echolot::Config::get()->{'recipient_delimiter'} ne '') { my $delimiter = quotemeta( Echolot::Config::get()->{'recipient_delimiter'}); ($type, $timestamp, $received_hash) = $address =~ /$delimiter (.*) = (\d+) = ([0-9a-f]+) @/x or ($type, $timestamp, $received_hash) = $address =~ /\( (.*) = (\d+) = ([0-9a-f]+) \)/x or Echolot::Log::debug("Could not parse to header '$address'."), return undef; } else { ($type, $timestamp, $received_hash) = $address =~ /\( (.*) = (\d+) = ([0-9a-f]+) \)/x or Echolot::Log::debug("Could not parse to header '$address'."), return undef; }; my $token = $type.'='.$timestamp; my $hash = Echolot::Tools::hash($token . Echolot::Globals::get()->{'storage'}->get_secret() ); my $cut_hash = substr($hash, 0, Echolot::Config::get()->{'hash_len'}); ($cut_hash eq $received_hash) or Echolot::Log::info("Hash mismatch in '$address'."), return undef; return { timestamp => $timestamp, token => $type }; }; sub send_message(%) { my (%args) = @_; defined($args{'To'}) or Echolot::Log::cluck ('No recipient address given.'), return 0; $args{'Subject'} = '(no subject)' unless (defined $args{'Subject'}); $args{'Body'} = '' unless (defined $args{'Body'}); $args{'From_'} = Echolot::Config::get()->{'my_localpart'}. '@'. Echolot::Config::get()->{'my_domain'}; if (defined $args{'Token'}) { $args{'From'} = make_address( $args{'Token'} ); } else { $args{'From'} = $args{'From_'}; }; $args{'Subject'} = 'none' unless (defined $args{'Subject'}); my @lines = map { $_."\n" } split (/\r?\n/, $args{'Body'}); open(SENDMAIL, '|'.Echolot::Config::get()->{'sendmail'}.' -f '.$args{'From_'}.' -t') or Echolot::Log::warn("Cannot run sendmail: $!."), return 0; printf SENDMAIL "From: %s\n", $args{'From'}; printf SENDMAIL "To: %s\n", $args{'To'}; printf SENDMAIL "Subject: %s\n", $args{'Subject'}; printf SENDMAIL "\n"; for my $line (@lines) { print SENDMAIL $line; }; close SENDMAIL; return 1; }; sub make_monthname($) { my ($month) = @_; my @MON = qw{Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec}; return $MON[$month]; }; sub make_dayname($) { my ($day) = @_; my @WDAY = qw{Sun Mon Tue Wed Thu Fri Sat}; return $WDAY[$day]; }; sub date822($) { my ($date) = @_; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($date); # 14 Aug 2002 17:11:12 +0100 return sprintf("%s, %02d %s %d %02d:%02d:%02d +0000", make_dayname($wday), $mday, make_monthname($mon), $year + 1900, $hour, $min, $sec); }; sub write_meta_information($%) { my ($file, %data) = @_; return 1 unless Echolot::Config::get()->{'write_meta_files'}; $file .= Echolot::Config::get()->{'meta_extension'}; open (F, ">$file") or Echolot::Log::warn ("Cannot open $file: $!."), return 0; if (defined $data{'Expires'}) { my $date = date822($data{'Expires'}); print F "Expires: $date\n"; }; close(F); return 1; }; sub escape_HTML_entities($) { my ($in) = @_; $in =~ s/&/&/; $in =~ s/"/"/; $in =~ s//>/; return $in; }; sub write_HTML_file($$;$%) { my ($origfile, $template_file, $expire, %templateparams) = @_; my $operator = Echolot::Config::get()->{'operator_address'}; $operator =~ s/@/./; for my $lang ( keys %{Echolot::Config::get()->{'templates'}} ) { my $template = HTML::Template->new( filename => Echolot::Config::get()->{'templates'}->{$lang}->{$template_file}, strict => 0, die_on_bad_params => 0, global_vars => 1 ); $template->param ( %templateparams ); $template->param ( CURRENT_TIMESTAMP => scalar gmtime() ); $template->param ( SITE_NAME => Echolot::Config::get()->{'sitename'} ); $template->param ( separate_rlist => Echolot::Config::get()->{'separate_rlists'} ); $template->param ( combined_list => Echolot::Config::get()->{'combined_list'} ); $template->param ( thesaurus => Echolot::Config::get()->{'thesaurus'} ); $template->param ( fromlines => Echolot::Config::get()->{'fromlines'} ); $template->param ( version => Echolot::Globals::get()->{'version'} ); $template->param ( operator => $operator ); $template->param ( expires => date822( time + $expire )); my $file = $origfile; $file .= '.'.$lang unless ($lang eq 'default'); $file .= '.html'; open(F, '>'.$file) or Echolot::Log::warn("Cannot open $file: $!."), return 0; print F $template->output() or Echolot::Log::warn("Cannot print to $file: $!."), return 0; close (F) or Echolot::Log::warn("Cannot close $file: $!."), return 0; if (defined $expire) { write_meta_information($file, Expires => time + $expire) or Echolot::Log::debug ("Error while writing meta information for $file."), return 0; }; }; return 1; }; sub make_gpg_fds() { my %fds = ( stdin => IO::Handle->new(), stdout => IO::Handle->new(), stderr => IO::Handle->new(), status => IO::Handle->new() ); my $handles = GnuPG::Handles->new( %fds ); return ($fds{'stdin'}, $fds{'stdout'}, $fds{'stderr'}, $fds{'status'}, $handles); }; sub readwrite_gpg($$$$$) { my ($in, $inputfd, $stdoutfd, $stderrfd, $statusfd) = @_; Echolot::Log::trace("Entering readwrite_gpg."); local $INPUT_RECORD_SEPARATOR = undef; my $sout = IO::Select->new(); my $sin = IO::Select->new(); my $offset = 0; Echolot::Log::trace("input is $inputfd; output is $stdoutfd; err is $stderrfd; status is ".(defined $statusfd ? $statusfd : 'undef')."."); $inputfd->blocking(0); $stdoutfd->blocking(0); $statusfd->blocking(0) if defined $statusfd; $stderrfd->blocking(0); $sout->add($stdoutfd); $sout->add($stderrfd); $sout->add($statusfd) if defined $statusfd; $sin->add($inputfd); my ($stdout, $stderr, $status) = ("", "", ""); my ($readyr, $readyw); while ($sout->count() > 0 || (defined($sin) && ($sin->count() > 0))) { Echolot::Log::trace("select waiting for ".($sout->count())." fds."); ($readyr, $readyw, undef) = IO::Select::select($sout, $sin, undef, 42); Echolot::Log::trace("ready: write: ".(defined $readyw ? scalar @$readyw : 'none')."; read: ".(defined $readyr ? scalar @$readyr : 'none')); for my $wfd (@$readyw) { Echolot::Log::trace("writing to $wfd."); my $written = 0; if ($offset != length($in)) { $written = $wfd->syswrite($in, length($in) - $offset, $offset); } unless (defined ($written)) { Echolot::Log::warn("Error while writing to GnuPG: $!"); close $wfd; $sin->remove($wfd); $sin = undef; } else { $offset += $written; if ($offset == length($in)) { Echolot::Log::trace("writing to $wfd done."); close $wfd; $sin->remove($wfd); $sin = undef; } } } next unless (defined($readyr)); # Wait some more. for my $rfd (@$readyr) { if ($rfd->eof) { Echolot::Log::trace("reading from $rfd done."); $sout->remove($rfd); close($rfd); next; } Echolot::Log::trace("reading from $rfd."); if ($rfd == $stdoutfd) { $stdout .= <$rfd>; next; } if (defined $statusfd && $rfd == $statusfd) { $status .= <$rfd>; next; } if ($rfd == $stderrfd) { $stderr .= <$rfd>; next; } } } Echolot::Log::trace("readwrite_gpg done."); return ($stdout, $stderr, $status); }; sub crypt_symmetrically($$) { my ($msg, $direction) = @_; ($direction eq 'encrypt' || $direction eq 'decrypt') or Echolot::Log::cluck("Wrong argument direction '$direction' passed to crypt_symmetrically."), return undef; my $GnuPG = new GnuPG::Interface; $GnuPG->call( Echolot::Config::get()->{'gnupg'} ) if (Echolot::Config::get()->{'gnupg'}); $GnuPG->options->hash_init( armor => 1, homedir => Echolot::Config::get()->{'gnupghome'} ); $GnuPG->options->meta_interactive( 0 ); $GnuPG->passphrase( Echolot::Globals::get()->{'storage'}->get_secret() ); my ( $stdin_fh, $stdout_fh, $stderr_fh, $status_fh, $handles ) = make_gpg_fds(); my $pid = $direction eq 'encrypt' ? $GnuPG->encrypt_symmetrically( handles => $handles ) : $GnuPG->decrypt( handles => $handles ); my ($stdout, $stderr, $status) = readwrite_gpg($msg, $stdin_fh, $stdout_fh, $stderr_fh, $status_fh); waitpid $pid, 0; if ($direction eq 'encrypt') { (($status =~ /^\[GNUPG:\] BEGIN_ENCRYPTION\s/m) && ($status =~ /^\[GNUPG:\] END_ENCRYPTION\s/m)) or Echolot::Log::info("GnuPG status '$status' didn't indicate message was encrypted correctly (stderr: $stderr). Returning."), return undef; } elsif ($direction eq 'decrypt') { (($status =~ /^\[GNUPG:\] BEGIN_DECRYPTION\s/m) && ($status =~ /^\[GNUPG:\] DECRYPTION_OKAY\s/m) && ($status =~ /^\[GNUPG:\] END_DECRYPTION\s/m)) or Echolot::Log::info("GnuPG status '$status' didn't indicate message was decrypted correctly (stderr: $stderr). Returning."), return undef; }; my $result = $stdout; $result =~ s,^Version: .*$,Version: N/A,m; return $result; }; sub make_garbage() { my $file = Echolot::Config::get()->{'dev_urandom'}; open(FH, $file) or Echolot::Log::warn("Cannot open $file: $!."), return ""; my $random = ''; my $want = int(rand(int(Echolot::Config::get()->{'random_garbage'} / 2))); my $i = 0; while ($want > 0) { my $buf; $want -= read(FH, $buf, $want); $random .= $buf; ($i++ > 15 && $want > 0) and Echolot::Log::warn("Could not get enough garbage (still missing $want."), last; }; close (FH) or Echolot::Log::warn("Cannot close $file: $!."); $random = unpack("H*", $random); $random = join "\n", grep { $_ ne '' } (split /(.{64})/, $random); $random = "-----BEGIN GARBAGE-----\n". $random."\n". "-----END GARBAGE-----\n"; return $random; }; sub read_file($;$) { my ($name, $fail_ok) = @_; unless (open (F, $name)) { Echolot::Log::warn("Could not open '$name': $!.") unless ($fail_ok); return undef; }; local $/ = undef; my $result = ; close (F); return $result; }; sub cleanup_tmp() { my $tmpdir = Echolot::Config::get()->{'tmpdir'}; opendir(DIR, $tmpdir) or Echolot::Log::warn("Could not open '$tmpdir': $!."), return undef; my @files = grep { ! /^[.]/ } readdir(DIR); closedir(DIR); for my $file (@files) { unlink($tmpdir.'/'.$file) or Echolot::Log::warn("Could not unlink '$tmpdir/$file': $!."); }; }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/Echolot/Config.pm0000644000175000017500000002705312422221767015656 0ustar weaselweaselpackage Echolot::Config; # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 Name Echolot::Config - echolot configuration =head1 DESCRIPTION Sets default configuration options and reads configuration from the config file. =head1 FILES The configuration file is searched in those places in that order: =over =item the file pointed to by the B environment variable =item /pingd.conf =item $HOME/echolot/pingd.conf =item $HOME/pingd.conf =item $HOME/.pingd.conf =item /etc/echolot/pingd.conf =item /etc/pingd.conf =back =cut use strict; use Carp; use English; my $CONFIG; sub init($) { my ($params) = @_; die ("Basedir is not defined\n") unless defined $params->{'basedir'}; my @CONFIG_FILES = (); push(@CONFIG_FILES, $ENV{'ECHOLOT_CONF'}) if defined $ENV{'ECHOLOT_CONF'}; push(@CONFIG_FILES, $params->{'basedir'}.'/pingd.conf') if defined $params->{'basedir'}; push(@CONFIG_FILES, $ENV{'HOME'}.'/echolot/pingd.conf') if defined $ENV{'HOME'}; push(@CONFIG_FILES, $ENV{'HOME'}.'/pingd.conf') if defined $ENV{'HOME'}; push(@CONFIG_FILES, $ENV{'HOME'}.'/.pingd.conf') if defined $ENV{'HOME'}; push(@CONFIG_FILES, '/etc/echolot/pingd.conf'); push(@CONFIG_FILES, '/etc/pingd.conf'); my $DEFAULT; $DEFAULT = { # System Specific Options recipient_delimiter => '+', dev_random => '/dev/random', dev_urandom => '/dev/urandom', sendmail => '/usr/sbin/sendmail', # Magic Numbers hash_len => 8, stats_days => 12, seconds_per_day => 24 * 60 * 60, # New Remailers fetch_new => 1, ping_new => 1, show_new => 1, # Statistics Generation separate_rlists => 0, combined_list => 0, thesaurus => 1, fromlines => 1, stats_sort_by_latency => 0, # Timers and Counters processmail => 60, # process incomng mail every minute buildstats => 5*60, # build statistics every 5 minutes buildkeys => 8*60*60, # build keyring every 8 hours buildthesaurus => 60*60, # hourly buildfromlines => 60*60, # hourly commitprospectives => 8*60*60, # commit prospective addresses every 8 hours expire => 24*60*60, # daily getkeyconf_interval => 5*60, # send out requests every 5 minutes getkeyconf_every_nth_time => 24*60/5, # send out the same request to the same remailer once a day check_resurrection => 7*24*60*60, # weekly summary => 24*60*60, # daily metadata_backup => 8*60*60, # make backups of metadata and rotate them every 8 hours metadata_backup_count => 32, # keep 32 rotations of metadata pinger_interval => 5*60, # send out pings every 5 minutes ping_every_nth_time => 24, # send out pings to the same remailer every 24 calls, i.e. every 2 hours chainpinger_interval => 5*60, # send out pings every 5 minutes chainping_every_nth_time => 2016, # send out pings to the same chain every 2016 calls, i.e. week chainping_ic_every_nth_time => 288, # send out pings to broken or unknown chains every 288 calls, i.e. every day chainping_period => 10*24*60*60, # 12 days chainping_fudge => 0.3, # if less than 0.3 * rel1 * rel2 make it, the chain is really broken chainping_grace => 1.5, # don't count pings sent no longer than 1.5 * (lat1 + lat2) ago chainping_update => 4*60*60, # chain stats should never be older than 4 hours chainping_minsample => 3, # have at least sent 3 pings before judging any chain chainping_allbad_factor => 0.5, # at least 50% of possible chains (A x) need to fail for (A *) to be listed in broken chains addresses_default_ttl => 5, # getkeyconf seconds (days) check_resurrection_ttl => 8, # check_resurrection seconds (weeks) prospective_addresses_ttl => 5*24*60*60, # 5 days reliable_auto_add_min => 6, # 6 remailes need to list new address expire_keys => 5*24*60*60, # 5 days expire_confs => 5*24*60*60, # 5 days expire_pings => 12*24*60*60, # 12 days expire_thesaurus => 21*24*60*60, # 21 days expire_chainpings => 12*24*60*60, # 12 days expire_fromlines => 5*24*60*60, # 5 days cleanup_tmpdir => 24*60*60, # daily random_garbage => 8192, # Directories and files mailin => 'mail', mailerrordir => 'mail-errors', resultdir => 'results', thesaurusdir => 'results/thesaurus', thesaurusindexfile => 'results/thesaurus/index', fromlinesindexfile => 'results/from', private_resultdir => 'results.private', indexfilebasename => 'echolot', gnupghome => 'gnupghome', gnupg => '', mixhome => 'mixhome', mixmaster => 'mix', tmpdir => 'tmp', broken1 => 'broken1.txt', broken2 => 'broken2.txt', sameop => 'sameop.txt', gzip => 'gzip', commands_file => 'commands.txt', pidfile => 'pingd.pid', save_errormails => 0, write_meta_files => 1, meta_extension => '.meta', storage => { backend => 'File', File => { basedir => 'data' } }, # logging logfile => 'pingd.log', loglevel => 'info', # ping types do_pings => { 'cpunk-dsa' => 1, 'cpunk-rsa' => 1, 'cpunk-clear' => 1, 'mix' => 1 }, do_chainpings => 1, show_chainpings => 1, which_chainpings => { 'cpunk' => [ qw{cpunk-dsa cpunk-rsa cpunk-clear} ], 'mix' => [ qw{mix} ] }, pings_weight => [ qw{0.5 1.0 1.0 1.0 1.0 0.9 0.8 0.5 0.3 0.2 0.2 0.1 } ], # templates templates => { default => { 'indexfile' => 'templates/echolot.html', 'thesaurusindexfile' => 'templates/thesaurusindex.html', 'fromlinesindexfile' => 'templates/fromlinesindex.html', 'mlist' => 'templates/mlist.html', 'mlist2' => 'templates/mlist2.html', 'rlist' => 'templates/rlist.html', 'rlist-rsa' => 'templates/rlist-rsa.html', 'rlist-dsa' => 'templates/rlist-dsa.html', 'rlist-clear' => 'templates/rlist-clear.html', 'rlist2' => 'templates/rlist2.html', 'rlist2-rsa' => 'templates/rlist2-rsa.html', 'rlist2-dsa' => 'templates/rlist2-dsa.html', 'rlist2-clear' => 'templates/rlist2-clear.html', 'clist' => 'templates/clist.html', }, }, 'echolot_css' => 'templates/echolot.css', remailerxxxtext => "Hello,\n". "\n". "This message requests remailer configuration data. The pinging software thinks\n". " is a remailer. Either it has been told so by the\n". "maintainer of the pinger or it found the address in a remailer-conf or\n". "remailer-key reply of some other remailer.\n". "\n". "If this is _not_ a remailer, you can tell this pinger that and it will stop\n". "sending you those requests immediately (otherwise it will try a few more times).\n". "Just reply and make sure the following is the first line of your message:\n". " not a remailer\n". "\n". "If you want to talk to a human please mail .\n", homedir => undef, my_localpart => undef, my_domain => undef, operator_address => undef, sitename => undef, verbose => 0 }; my $configfile = undef; for my $filename ( @CONFIG_FILES ) { if ( defined $filename && -e $filename ) { $configfile = $filename; print "Using config file $configfile\n" if ($params->{'verbose'}); last; }; }; die ("no Configuration file found\n") unless defined $configfile; { local $/ = undef; open(CONFIGCODE, $configfile) or confess("Could not open configfile '$configfile': $!"); my $config_code = ; close (CONFIGCODE); ($config_code) = $config_code =~ /^(.*)$/s; eval ($config_code); ($EVAL_ERROR) and confess("Evaling config code from '$configfile' returned error: $EVAL_ERROR"); } for my $key (keys %$CONFIG) { warn("Unkown option: $key\n") unless (exists $DEFAULT->{$key}); }; # Work around spelling bug until 2.0rc3 if (exists $CONFIG->{'seperate_rlists'}) { if (exists $CONFIG->{'separate_rlists'}) { warn ("seperate_rlists has been superseded by separate_rlists."); } else { warn ("seperate_rlists has been superseded by separate_rlists, please change it in your config file.\n"); $CONFIG->{'separate_rlists'} = $CONFIG->{'seperate_rlists'}; }; delete $CONFIG->{'seperate_rlists'}; } # In 2.0.6: thesaurusindexfile and indexfilebasename config values # should not longer have the extension (.html) in them # Handle this gracefully for now: if (exists $CONFIG->{'thesaurusindexfile'}) { $CONFIG->{'thesaurusindexfile'} =~ s/\.html?$// and warn ("thesaurusindexfile no longer should have the .html extension.\n"); } if (exists $CONFIG->{'indexfilebasename'}) { $CONFIG->{'indexfilebasename'} =~ s/\.html?$// and warn ("indexfilebasename no longer should have the .html extension.\n"); } for my $key (keys %$DEFAULT) { $CONFIG->{$key} = $DEFAULT->{$key} unless exists $CONFIG->{$key}; }; $CONFIG->{'homedir'} = $params->{'basedir'} unless (defined $CONFIG->{'homedir'}); $CONFIG->{'verbose'} = $params->{'verbose'} if ($params->{'verbose'}); for my $key (keys %$CONFIG) { warn ("Config option $key is not defined\n") unless defined $CONFIG->{$key}; }; }; sub check_binaries() { for my $bin (qw{mixmaster}) { my $path = get()->{$bin}; if ($path =~ m#/#) { Echolot::Log::warn ("$bin binary $path does not exist or is not executeable") unless -x $path; } else { my $found = 0; if (defined $ENV{'PATH'}) { for my $pathelem (split /:/, $ENV{'PATH'}) { $found = $pathelem, last if -e $pathelem.'/'.$path; }; }; if ($found) { Echolot::Log::warn ("$bin binary $found/$path is not executeable") unless -x $found.'/'.$path; } else { Echolot::Log::warn ("$bin binary $path not found"); }; }; }; }; sub get() { return $CONFIG; }; sub dump() { print Data::Dumper->Dump( [ $CONFIG ], [ 'CONFIG' ] ); }; 1; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/UPGRADE0000644000175000017500000000130312422221767013516 0ustar weaselweaselUpgrading checklist for Echolot - Download the latest .tar.gz from . - Verify the signature. - Stop the running pingd. - Make sure it no longer runs. - Make a backup of your entire echolot directory. really. - Untar the new Echolot release to a new directory and copy your data directory and pingd.conf there. - Replace your old Echolot directory with the new tree. - Start the new pingd and verify it's working properly. - Congratulations. Notes for upgrades from a version earlier than 2.0.6 - If you have specified thesaurusindexfile and/or indexfilebasename in your Echolot configuration file, please remove the .html extension from that setting. EOF echolot-2.1.9/TODO0000644000175000017500000000222412422221767013177 0ustar weaselweasel Legend: - Not done * Top priority . Partially done o Done D Deferred X Abandoned - updated information with from lines - don't send that many messages with From Headers - optionally log messages wich raise problems - close and open files instead of keeping all the files open - make 'not a remailer' check not that strict can be done later: - allow capsstring overrides/additions - check gnupg version number on startup - chain preload and postload D have a way to always have certain keys in the keyring (manually add nym's keyrings) This is not necessary if we add real nymserv pinging D rotate secret D parse bounces and mark pings as dead immediatly D separate keys in remailer-key reply if they share the same armor (cmeclex is the only one doing this. AARG) D query other stat pages for new remailers D ping nymservers D ping mail2news gates D ping remailer -> usenet, summarize from lines X have different stats philosophies (pessimist, optimist..) X get rid of dependency on mix X get rid of dependency on gnupg X have a timeout on GPG calls (nonblocking IO should solve the problem) echolot-2.1.9/NEWS0000644000175000017500000004203512422221767013212 0ustar weaselweaselChanges in version 2.1.9 - 2014-10-23 * Several debian updates. * Update FSF address in all files. * Fix the name of an option in an example in pingd.conf(5). closes: DebianBug#459938. * Fix a bashishm in tools/create-distribution. * Fix a markup typo in the pingd manpage. * Fix "defined(@array) is deprecated" in Echolot/Tools.pm * Newer GnuPG::Interface versions are more picky when it comes to command and command_args. Previously, they would accept things in any order and would not care which of the two arrays had which. Now, command_args is really limited to have arguments for the command listed in command. Any options go with, and in front of, the command. Changes in version 2.1.8 - 2005-04-25 * debian: Redirect init script output to /dev/null in logrotate Changes in version 2.1.7 - 2004-11-15 * debian: Add disable and enable to allowed commands in init script * debian: Create echolot user group in proper gid space. * debian: remove debconf stuff. * handle empty environment better - previously we would whine when HOME or PATH were not set. Changes in version 2.1.6 - 2004-08-07 * Catch a possible use of undefined values in a log trace() call. Changes in version 2.1.5 - 2004-06-22 * Fix use of an illegal filedescriptor when we know of a remailer, but do not have keys for it yet/anymore. * Check if syswrite's return value is defined (also is: do not write to gpg forever, once it has died or closed its stdin). * Tell Mixmaster to create no dummies. It has no remailer keys and reliability information anyway. * Increase the number of remailers that need to list an address before it is added to 6. It was 3 previously. * Make set fetch= work again. * Make disable and enable actually work. Changes in version 2.1.4 - 2004-06-10 * Have new disable and enable commands which are shortcut for set pingit=off showit=off fetch=off and =on respectively. * Accept show=, ping=, and fetchit= in addition to their real names in set. * Do not accept expired or future mixmaster keys. * Print summary on notice level, not info, if it was requested manually. * Remove get_remailers since get_addresses in Echolot::Storage::File does almost the same. * Make sure we do not create empty key hashes in metadata. * gpg interaction cleanup, move a lot of duplicated code to Echolot::Tools. * Filter out mixmaster outputs to stderr that are no problem. * There's a TRACE loglevel now, which is even more noisy than DEBUG. * Changed all pointers to savannah to alioth. * Removed obsolete tools to convert echolot setups during 2.0beta* releases. * Handle situations better in which a ping receives when the last key already is expired, and the remailer no longer exists in the remailers part of the metadata (only in the addresses part). * Add a file that describes how Echolot works. Changes in version 2.1.3 - 2004-04-20 * Improvements to pingd.conf(5) by Colin. * Use 'Apr' instead of 'Arp' for April. closes: DebianBug#243504. Changes in version 2.1.2 - 2003-11-04 * Also ignore testing remailers in the thesaurus. * Also ignore testing remailers in fromlines. Changes in version 2.1.1 - 2003-11-02 * Fix 0/false in Fetch/Ping/Show in summary report. * fork() for calling mixmaster, so we can exec() it rather can using open("| ...") which ivokes a shell. * Catch SIGPIPE and log it as error. * Document thesaurusindexfile. * Minor documentation fixes. * When asked to shut down, exit immediatly after the current running task, even if we have a backlog. * If we run late we should now drop actions where it does not hurt. * Remove random command invocation via pingctr/init script. People should just call pingd directly. Also wait for pingd to really shut down in stop and restart. * Regularily clean up the temp directory. * Send out pings every two hours by default. * Modified statistics generation: We no longer assume a normal distribution of latencies but instead use percentiles when calculating a life probability of an outstanding ping. Also we do not show the mean of latency but the median as this seems to be 'more correct'. Also have different weights for pings based on their age. * Optimized chain pinging: takes less CPU. * Do not list remailers with the 'testing' capability in public stats. Testing indicates a node is not ready for real users' traffic yet. * Publish operator's address on index page. * Refuse to run with euid == 0. Changes in version 2.1 - 2003-03-03 * Minor documentation fixes suggested by Ryan Lackey. * Adjusted some loglevels (several info got downgraded to debug) * Removed --pgp2 in CPunk RSA pings. * Append random garbage to pings so they have different lengths. new options: - random_gabage (default: 8192) Garbage length is chosen uniformly from 0 to random_gabage bytes. - dev_urandom (default: /dev/urandom) where to read garbage from (more generally: non-blocking (low-quality) randomless source) * Chain pinging: Echolot now finds broken chains. new options: - do_chainpings (default: 1) Whether to do chain pinging. - show_chainpings (default: 1) Whether to show the result of our chain pinging in the public stats. Several other settings control the details of chain pinging. You probably don't want or need to tweak them. See the pingd.conf(5) manual page for details. - Also adds the new "sendchainpings" command. * Create an index.txt file in the thesaurus directory which holds id, nick, and address for each remailer in the thesaurus. This helps additional tools to utilize Echolot's data. * Compile a list of default From: lines as well as a list of remailers which allow user supplied From headers. * Print status summary to log daily. - Also adds the new "summary" command. * Reliable and CRLF - a neverending (sad) story. * Handle situations better in which a ping is received when the key already has been expired. Changes in version 2.0.10 - 2003-02-03 * Return undef rather than 0 if we cannot open a Maildir. * Add missing use of Carp to Echolot::Storage::File.pm. Changes in version 2.0.9 - 2003-01-14 * Logging is finally cleaned up. There are two new config options: logfile and loglevel. * Automatically remove stale .pid files. * Trash messages with errors immediately rather than storing them in mailerrordir forever. Changes in version 2.0.8 - 2003-01-13 * Work around a bug that let pingd die in certain random cases when dealing with unuseable PGP keys (like expired keys). Talk with Frank Tobin from GnuPG::Interface fame led to the diagnosis that pingd gets a SIGPIPE since the GnuPG process already exited. * Removed a duplicate line from v2legend. * Allow periods in hostname. * Don't use GnuPG::Interface's recipients when encrypting any more as it is broken with at least GnuPG 1.2.1. * Always use --no-secmem-warning with GnuPG calls. * Also use supported remailers from type2 only remailers. * Reset metadata if status is not defined. Changes in version 2.0.7 - 2002-12-18 * Added upgrade HOWTO. Changes in version 2.0.6 - 2002-12-18 * Have support for translated templates * The templates now make use of CSS. * Minor documentation fixes by Colin Tuckley * thesaurusindexfile and indexfilebasename config values should no longer have the extension (.html) in them * Fix v2 stats for cypherpunk remailers by using right column title. * Use '(no subject)' instead of '' as a subject if none is otherwise chosen. Changes in version 2.0.5 - 2002-10-25 * Only take default parameters if they are not set in pingd.conf (as opposed to set to undef). * Also support setups without user defined mailboxes (recipient_delimiter set to ''). Changes in version 2.0.4 - 2002-10-16 * Fix pingd.conf(5) manpage a bit (indention levels). Changes in version 2.0.3 - 2002-10-12 * Set program name according to current action. * Fix a typo that showed up in perl 5.0005_02 (had a , instead of a . in an open() when reading mboxen as input). Changes in version 2.0.2 - 2002-09-21 * Die immeditatly if there is no Version information in metadata. * Make regular backups of metadata and rotate them properly. Changes in version 2.0.1 - 2002-09-21 * Store unkown fields from mix summary lines and put them in keyrings. Changes in version 2.0 - 2002-09-17 * New version number, this is 2.0, really! * Minor spelling fixes. Changes in version 2.0rc3 - 2002-09-12 * Write SENDMAIL to mix.cfg * Add sendpings command. * Only decrease a remailer's ttl during requesting -conf if it was requested by the usualy timer run and not by the user. * Template cleanup: fix a link and add links to .txt versions to front page. * Have install-perl-modules tool. * Spelling fixes. * seperate_rlists was renamed to separate_rlists. * README was improved. Changes in version 2.0rc2 - 2002-09-08 * Reopen stdin to /dev/null instead of closing it to avoid perl 5.8 warnings. Changes in version 2.0rc1 - 2002-09-05 * new version number. * Random spelling fixes. * Do not show hidden remailers in thesaurus. Changes in version 2.0beta34 - 2002-09-04 * Make ping/request time more random. * Encrypt pings (symmetrically), so that dup detection of some remailers (austria) fails and pings get processed. * Write total number of unique remailers to echolot.html. Changes in version 2.0beta33 - 2002-08-23 * Scheduler fixes (inserted jobs for one time processing got requeued over and over again according to their interval). * Give a short summary about current stats on index page. Changes in version 2.0beta32 - 2002-08-23 * Fix a major bug introduced in 2.0beta31 that resulted in no remailer-xxx queries beeing sent out. Changes in version 2.0beta31 - 2002-08-21 * Have a consistent name for the ~/echolot directory in README. * If you request keyconf from only a few remailers, more requests could have been sent. Fixed that. * Write REMAIL n to mix.cfg - apparently it defaults to yes, which means Mixmaster will want to create keys. * Fix random typos. * Fix pingctl script. Changes in version 2.0beta30 - 2002-08-15 * Write NAME and ADDRESS to mix.cfg - mix cannot figure it out if detached. Changes in version 2.0beta29 - 2002-08-14 * Create .meta files with the expiry date of pages. * Also put the expiry date in HTML meta headers. * Make indexfilebasename a configure option. * Code cleanup: renamed some functions. Changes in version 2.0beta28 - 2002-08-13 * Write a standard mix.cfg configuration file for mixmaster. It only sets PUBRING and TYPE2LIST. Needed for systems where those values are overriden by systemwide defaults. Changes in version 2.0beta27 - 2002-08-13 * Some small fixes to the Debian Package Changes in version 2.0beta26 - 2002-08-12 * Fix a few typos in the echolot.html template. * Added /etc/echolot/pingd.conf to the list of configfiles. * Have a debian/ directory to build a Debian package. * Config option mailindir was renamed to mailin. You now can also point it to a mbox format mailbox. * Have --quiet. * Stricter permissions for most newly created directories (go-rwx). Changes in version 2.0beta25 - 2002-08-10 * Produce echolot.html, an index file for echolot results. Changes in version 2.0beta24 - 2002-08-10 * Remove »x« from end of pubring.mix summary lines. Changes in version 2.0beta23 - 2002-08-07 * Unlinking Thesaurus files works now. * Detach correctly now. Changes in version 2.0beta22 - 2002-08-05 * Actually use the sane basedir. *sigh* Changes in version 2.0beta21 - 2002-08-05 * Using a sane basedir by default. You no longer need to specify it in pingd.conf. Changes in version 2.0beta20 - 2002-08-02 * Added pingctl wrapper to tools. * echolot --help now gives a brief list of commands. * Added acknowledgements. Changes in version 2.0beta19 - 2002-07-29 * Build keyrings in results.private too Changes in version 2.0beta18 - 2002-07-23 * Allow for inclusion of broken chain reports and same operator/machine lists in stats pages. Broken chain reports are not generated automatically; this is planned for a later release. For now you need to supply this information in the files broken1.txt, broken2.txt, and sameop.txt. Changes in version 2.0beta17 - 2002-07-22 * You no longer need an extra Mixmaster installation for your pinger. Echolot can make use of any Mixmaster binary you alread have installed. * The config hash »Pinger::Mix« is obsolete now. Please set »mixmaster« to the path of your mix executeable. Echolot now uses its own mix directory below echolothome by default (config option mixhome). * The default gnupghome has changed from »gnupg« to »gnupghome«. * New config option »gnupg«. * Not the current time but the scheduled start time is used when deciding which remailers to ping. This is nice if we get delayed for whatever reason. * Changed URL in templates to point to www.palfrader.org/echolot instead of the savannah page. How to upgrade (assuming you have a default configuration): ./pingd stop mv gnupg gnupghome vi pingd.conf (remove Pinger::Mix, set mixmaster; cf. pingd.conf.sample ) ./pingd [...] start Changes in version 2.0beta16 - 2002-07-17 * Do not send all pings for the same remailer at the same time * The cheap hashing function that was used for determining when to ping was replaced by md5. The local secret also is an input to the function so not all echolot pingers ping the same remailer at the same time. * In the distribution the pingd.conf file has been renamed to pingd.conf. So it should be possible to just untar the new tar.gz over the old installation (you have a backup anyway, don't you?). * The getkeyconf command takes optional addresses to request config data from. * The getkeyconf config option was replaced by getkeyconf_interval and getkeyconf_every_nth_time. Not all requests are sent at the same time any more. * Stats can be sorted by latency rather than nick. Set stats_sort_by_latency if you want that. Changes in version 2.0beta15 - 2002-07-16 * Have echolot version in stats HTML pages * Random documentation fixes * Make it runs with older perls (5.005_03 is tested) - always pass two arguments to mkdir() - import SEEK_ constants from POSIX rather than Fcntl - do not use warnings Changes in version 2.0beta14 - 2002-07-16 * Added commands buildstats buildkeys and buildthesaurus * Added legend to templates (Orange) * Thesaurus building failed when an id did not return a valid remailer. A check was there bug it was wrong * Have pingd.conf.5 manpage documenting all options Changes in version 2.0beta13 - 2002-07-13 * Have correct title tags and some layout changes in the HTML templates * Encode HTML Entities in HTML Stats * Only show valid remailers in Thesaurus * Support »not a remailer« reply to remailer-xxx * Make sure only the right keys are exported to the pgp keyrings * Do not skip key if GnuPG returns something in stderr with remailer-key replies Changes in version 2.0beta12 - 2002-07-11 * Cut strings when they do not fit in the table formats * Sort type2.list/pubring.mix Changes in version 2.0beta11 - 2002-07-11 * Don't run in Taint mode anymore per default. If you still want it, trimm your PATH to something acceptable for perl and add »-T« to the first line of pingd. Changes in version 2.0beta10 - 2002-07-11 * Don't reset PATH to /usr/bin:/bin any more (Yes, again) * Documentation updates Changes in version 2.0beta9 - 2002-07-11 * Don't reset PATH to /usr/bin:/bin any more * Use confess or cluck instead of croak in some places. Changes in version 2.0beta8 - 2002-07-10 * Fix public clist. Changes in version 2.0beta7 - 2002-07-10 * fixed a stupid syntax bug. Changes in version 2.0beta6 - 2002-07-10 * Did away with Mail::Internet. Using local sendmail binary only. This also means the smarthost config option is obsolete and there is a sendmail config option now. Changes in version 2.0beta5 - 2002-07-10 * Thesaurus filenames changed from nn-foo to nn.foo. Use the change-thesaurus-filenames script in the tools directory to convert your current thesaurus if you wish to keep the data. * Seperate rlists for rsa, dsa and plaintext pings are supported now. Enable seperate_rlists in pingd.conf. * Have a combined list. Enable with combined_list in pingd.conf. Changes in version 2.0beta4 - 2002-07-10 * Minor documentation fixes * Added --process switch * Die if metadata or conf data cannot be parsed * Build manpage into extra file Changes in version 2.0beta3 - 2002-07-10 * Fixed a small bug with writing the metadata so that it could not be parsed again Changes in version 2.0beta2 - 2002-07-10 * Moved from XML to Data::Dumper Use convert-xml-to-datadumper in the tools directory to convert your setup: pingd stop mv pingd.conf pingd.conf.old mv data/metadata data/metadata.old convert-xml-to-datadumper CONFIG < pingd.conf.old > pingd.conf convert-xml-to-datadumper METADATA < data/metadata.old > data/metadata pingd start Changes in version 2.0beta1 - 2002-07-07 * Initial public beta test echolot-2.1.9/pingd0000755000175000017500000006151212422221767013543 0ustar weaselweasel#!/usr/bin/perl -w # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA $| = 1; =pod =head1 NAME pingd - echolot ping daemon =head1 SYNOPSIS =over =item B B =item B B =item B B =item B B I
[I
...] =item B B I
[I
...] =item B B I
[I
...] =item B B I
[I
...] =item B B option=value [option=value..] I
[I
...] =item B B I =item B B I
=item B B [I
[I
...]] =item B B [I
[I
...]] =item B B I
B<:>I
[I
B<:>I
...] =item B B =item B B =item B B =item B B =item B B =item B B =back =head1 DESCRIPTION pingd is the heart of echolot. Echolot is a pinger for anonymous remailers. A Pinger in the context of anonymous remailers is a program that regularily sends messages through remailers to check their reliability. It then calculates reliability statistics which are used by remailer clients to choose the chain of remailers to use. Additionally it collects configuration parameters and keys of all remailers and offers them in a format readable by remailer clients. When called without parameters pingd schedules tasks like sending pings, processing incoming mail and requesting remailer-xxx data and runs them at configurable intervalls. =head1 COMMANDS =over =item B Start the ping daemon. =item B Send the running pingd process a SIGTERM. =item B Sends a HUP signal to the daemon which instructs it to process the commands. For other effects of sending the HUP Signal see the SIGNALS section below. =item B I
[I
...] Add I
to the list of remailers to query for keys and confs. =item B I
[I
...] Delete I
from the list of remailers to query for keys and confs. Delete all statistics and keys for that remailer. Note that echolot will add back this remailer if it learns of it from other remailers again. If you do not want that, use the disable command. =item B I
[I
...] Shorthand for B B B. This makes echolot completely ignore that remailer, until you enable it again. =item B I
[I
...] Shorthand for B B B. =item B option=value [option=value..] I
[I
...] Possible options and values: =over =item B{B,B} Set B (show remailer in mlist, rlist etc.) for remailer I
to either B or B. =item B{B,B} Set B (send out pings to that remailer) for remailer I
to either B or B. =item B{B,B} Set B (fetch remailer-key and remailer-conf) for remailer I
to either B or B. =back =item B I Some remailers (Mixmaster V2 - currently lcs and passthru2) don't return a useable remailer-conf message. For such remailers you need to set it manually. For instance: ./pingd setremailercaps '$remailer{"passthru2"} = " mix middle";' ./pingd setremailercaps '$remailer{"lcs"} = " mix klen1000";' =item B I
Delete remailer-conf data for I
. The config data will be reset from the next valid remailer-conf reply by the remailer. =item B [I
[I
...]] Send a command to immediatly request keys and configuration from remailers. If no addresses are given requests will be sent to all remailers. =item B [I
[I
...]] Send a command to immediatly send pings to the given remailers. If no addresses are given requests will be sent to all remailers. =item B I
B<:>I
[I
B<:>I
...] Send a command to immediatly send chain pings to the given chains. A chain is two remailer addresses seperated by a colon. =item B Send a command to immediatly rebuild stats. =item B Send a command to immediatly rebuild the keyrings. =item B Send a command to immediatly rebuild the Thesaurus. =item B Send a command to immediatly rebuild the From Header lines page. =item B Print a status summary of all known addresses to the log (level notice). =item B Dumps the current configuration to standard output. =back =head1 OPTIONS =over =item B<--basedir> The home directory to which everything else is relative. See the BASE DIRECTORY section below. =item B<--verbose> Verbose mode. Causes B to print debugging messages about its progress. =item B<--quiet> Quiet mode. Be even quieter than normal. =item B<--help> Print a short help message and exit sucessfully. =item B<--version> Print version number and exit sucessfully. =item B<--nohup> Usefull only with the B, B, B, B, B, B, B, B, B, B, B, or B command. Don't send a HUP signal to the daemon which instructs it to process the commands after adding the command to the task list. By default such a signal is sent. =item B<--process> Usefull only with the B command. Read and process the commands file on startup. =item B<--detach> Usefull only with the B command. Tell B to detach. =back =head1 BASE DIRECTORY The home directory to which everything else is relative. Basedir defaults to whatever directory the B binary is located. It can be overridden by the B environment variable which in turn is weaker than the B<--basedir> setting. This directory is then used to locate the configuration file B (see FILES below). The B setting in B finally sets the base directory. =head1 FILES The configuration file is searched in these places in this order: =over =item the file pointed to by the B environment variable =item /pingd.conf =item $HOME/echolot/pingd.conf =item $HOME/pingd.conf =item $HOME/.pingd.conf =item /etc/echolot/pingd.conf =item /etc/pingd.conf =back =head1 ENVIRONMENT =over =item ECHOLOT_CONF echolot config file (see section FILES) =item ECHOLOT_HOME echolot base directory (see section BASE DIRECTORY) =back =head1 SIGNALS On B, B, and B B will schedule a shutdown for as soon as the current actions are finished or immediatly if no actions are currently being processed. It will then write all metadata and pingdata to disk and close all files cleanly before exiting. On B will execute any pending commands from the commands file (B by default). It also closes and reopens the file 'output' which is used for stdout and stderr when the daemon is running detached. This can be used if you want to rotate that file. =head1 AUTHOR Peter Palfrader Epeter@palfrader.orgE =head1 BUGS Please report them at EURL:http://alioth.debian.org/projects/echolot/E =cut use strict; use FindBin qw{ $Bin }; use lib ( $Bin, "$Bin/lib" ); use Getopt::Long; use English; use Carp; use Echolot::Config; use Echolot::Globals; use Echolot::Storage::File; use Echolot::Scheduler; use Echolot::Conf; use Echolot::Mailin; use Echolot::Pinger; use Echolot::Chain; use Echolot::Stats; use Echolot::Commands; use Echolot::Thesaurus; use Echolot::Fromlines; use Echolot::Report; use Echolot::Log; use POSIX qw(setsid); delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; my $VERSION = '2.1.9'; my $redirected_stdio = 0; sub setSigHandlers() { $SIG{'HUP'} = sub { Echolot::Log::info("Got SIGHUP. scheduling readcommands."); Echolot::Globals::get()->{'scheduler'}->schedule('readcommands', 0, time() ); if ($redirected_stdio) { close STDOUT; close STDERR; my $logfile = Echolot::Config::get()->{'logfile'}; open (STDOUT, ">>$logfile") or die ("Cannot open '$logfile' as STDOUT\n"); open (STDERR, ">&STDOUT") or die ("Cannot dup STDOUT as STDERR\n"); }; Echolot::Log::reopen(); }; $SIG{'INT'} = sub { Echolot::Log::info("Got SIGINT. scheduling exit."); Echolot::Globals::get()->{'scheduler'}->schedule('exit', 0, 0 ); }; $SIG{'QUIT'} = sub { Echolot::Log::info("Got SIGQUIT. scheduling exit."); Echolot::Globals::get()->{'scheduler'}->schedule('exit', 0, 0 ); }; $SIG{'TERM'} = sub { Echolot::Log::info("Got SIGTERM. scheduling exit."); Echolot::Globals::get()->{'scheduler'}->schedule('exit', 0, 0 ); }; $SIG{'PIPE'} = sub { Echolot::Log::error("Got SIGPIPE"); }; }; sub commit_prospective_address() { Echolot::Globals::get()->{'storage'}->commit_prospective_address(); }; sub expire() { Echolot::Globals::get()->{'storage'}->expire(); }; sub metadata_backup() { Echolot::Globals::get()->{'storage'}->metadata_backup(); }; sub command_adddelete(@) { my $command = shift @_; my @argv = @_; die ("command_adddelete requires command\n") unless defined $command; die ("add requires argument
\n") unless scalar @argv; my @addresses; for my $address (@argv) { die ("argument
is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ ); push @addresses, $address; }; for my $address (@addresses) { Echolot::Commands::addCommand("$command $address"); }; }; sub command_set(@) { my @argv = @_; my @settings; while (scalar @argv && $argv[0] =~ /^(show(?:it)?|ping(?:it)?|fetch(?:it)?)=(on|off)$/) { my $name = $1; my $value = $2; $name = 'showit' if ($name eq 'show'); $name = 'pingit' if ($name eq 'ping'); $name = 'fetch' if ($name eq 'fetchit'); push @settings, "$name=$value"; shift @argv; }; my @addresses; for my $address (@argv) { die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ ); push @addresses, $address; }; for my $address (@argv) { for my $setting (@settings) { Echolot::Commands::addCommand("set $address $setting"); }; }; }; sub command_enabledisable (@) { my @argv = @_; my $command = shift @argv; my $value; if ($command eq 'enable') { $value='on'; } elsif ($command eq 'disable') { $value='off'; } else { die ("argument $command is not enable or disable. Shouldn't even be here.\n"); }; my @cmd = ("showit=$value", "pingit=$value", "fetch=$value"); push @cmd, @argv; command_set(@cmd); } sub command_setremailercaps(@) { my @argv = @_; my @caps; for my $caps (@argv) { my ($remailer_nick, $remailer_address) = ($caps =~ /^\s* \$remailer{"(.*)"} \s*=\s* "<(.*@.*)>.*"; \s*$/ix); die ("caps '$caps' is not a valid remailer caps line\n") unless (defined $remailer_nick && defined $remailer_address); push @caps, { address => $remailer_address, caps => $caps }; }; for my $caps (@caps) { Echolot::Commands::addCommand("setremailercaps ".$caps->{'address'}." ".$caps->{'caps'}); }; }; sub command_deleteremailercaps(@) { my @argv = @_; my @addresses; for my $address (@argv) { die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ ); push @addresses, $address; }; for my $address (@addresses) { Echolot::Commands::addCommand("deleteremailercaps $address"); }; }; sub command_getkeyconf(@) { my @argv = @_; my @addresses; for my $address (@argv) { die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ ); push @addresses, $address; }; push @addresses, 'all' unless (scalar @addresses); for my $address (@addresses) { Echolot::Commands::addCommand("getkeyconf $address"); }; }; sub command_sendpings(@) { my @argv = @_; my @addresses; for my $address (@argv) { die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ ); push @addresses, $address; }; push @addresses, 'all' unless (scalar @addresses); for my $address (@addresses) { Echolot::Commands::addCommand("sendpings $address"); }; }; sub command_sendchainpings(@) { my @argv = @_; my @args; for my $arg (@argv) { my ($addr1, $addr2) = split /:/, $arg; die ("Argument $arg is not in the form address1:address2") unless defined $addr1 && defined $addr2; die ("Address 1 in $arg ('$addr1') is not a valid email address\n") unless ($addr1 =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ ); die ("Address 2 in $arg ('$addr2') is not a valid email address\n") unless ($addr2 =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ ); push @args, [ $addr1, $addr2 ]; }; for my $arg (@args) { Echolot::Commands::addCommand("sendchainpings $arg->[0] $arg->[1]"); }; }; sub pid_exists($) { my ($remove_stale) = @_; my $pidfile = Echolot::Config::get()->{'pidfile'}; if (! $remove_stale) { return (-e $pidfile); } else { if (!-e $pidfile) { return 0; }; open (PIDFILE, $pidfile) or die ("Cannot open pidfile '$pidfile': $!.\n"); my $line = ; close PIDFILE; my ($pid, $host, $time) = $line =~ /^(\d+) \s+ (\S+) \s+ (\d+) \s* $/x or die ("Cannot parse pidfile '$pidfile' line '$line'.\n"); (Echolot::Globals::get()->{'hostname'} eq $host) or die ("Pidfile exists and is from another host.\n"); ($host ne 'unknown') or die ("Pidfile exists and hostname is unknown.\n"); ($time < time()) or die ("Pidfile exists and timestamp is in the future.\n"); my $sent = kill 0, $pid; ($sent == 0) or die ("Pidfile exists and process $pid running.\n"); warn ("Removing stale pidfile.\n"); unlink ($pidfile) or die ("Removing stale pidfile $pidfile failed.\n"); return 0; } }; sub daemon_run($) { my ($process) = @_; Echolot::Log::logdie("Pidfile '".Echolot::Config::get()->{'pidfile'}."' exists\n") if pid_exists(0); open (PIDFILE, '>'.Echolot::Config::get()->{'pidfile'}) or Echolot::Log::logdie("Cannot open pidfile '".Echolot::Config::get()->{'pidfile'}."': $!"); print PIDFILE "$PROCESS_ID ".Echolot::Globals::get()->{'hostname'}." ".time()."\n"; close PIDFILE; Echolot::Globals::initStorage(); setSigHandlers(); Echolot::Globals::get()->{'scheduler'} = new Echolot::Scheduler; my $scheduler = Echolot::Globals::get()->{'scheduler'}; $scheduler->add('exit' , -1 , 0, 0, 'exit' ); $scheduler->add('readcommands' , -1 , 0, 1, \&Echolot::Commands::processCommands ); $scheduler->add('processmail' , Echolot::Config::get()->{'processmail'} , 0, 1, \&Echolot::Mailin::process ); $scheduler->add('ping' , Echolot::Config::get()->{'pinger_interval'} , 0, 0, \&Echolot::Pinger::send_pings ); $scheduler->add('chainping' , Echolot::Config::get()->{'chainpinger_interval'} , 0, 0, \&Echolot::Chain::send_pings ); $scheduler->add('buildstats' , Echolot::Config::get()->{'buildstats'} , 0, 1, \&Echolot::Stats::build_stats ); $scheduler->add('buildkeys' , Echolot::Config::get()->{'buildkeys'} , 0, 1, \&Echolot::Stats::build_keys ); $scheduler->add('buildthesaurus' , Echolot::Config::get()->{'buildthesaurus'} , 0, 1, \&Echolot::Thesaurus::build_thesaurus ); $scheduler->add('buildfromlines' , Echolot::Config::get()->{'buildfromlines'} , 0, 1, \&Echolot::Fromlines::build_fromlines ); $scheduler->add('metadata_backup' , Echolot::Config::get()->{'metadata_backup'} , 0, 1, \&metadata_backup ); $scheduler->add('commitprospectives' , Echolot::Config::get()->{'commitprospectives'} , 0, 1, \&commit_prospective_address ); $scheduler->add('expire' , Echolot::Config::get()->{'expire'} , 0, 1, \&expire ); $scheduler->add('getkeyconf' , Echolot::Config::get()->{'getkeyconf_interval'} , 0, 0, \&Echolot::Conf::send_requests ); $scheduler->add('check_resurrection' , Echolot::Config::get()->{'check_resurrection'} , 0, 0, \&Echolot::Conf::check_resurrection ); $scheduler->add('cleanup_tmp' , Echolot::Config::get()->{'cleanup_tmpdir'} , 0, 1, \&Echolot::Tools::cleanup_tmp ); $scheduler->add('summary' , Echolot::Config::get()->{'summary'} , 0, 1, \&Echolot::Report::print_summary ); Echolot::Globals::get()->{'scheduler'}->schedule('readcommands', 0, time() ) if ($process); $scheduler->run(); Echolot::Globals::get()->{'storage'}->commit(); Echolot::Globals::get()->{'storage'}->finish(); unlink (Echolot::Config::get()->{'pidfile'}) or Echolot::Log::warn ("Cannot unlink pidfile ".Echolot::Config::get()->{'pidfile'}."."); }; sub send_sig($) { my ($sig) = @_; die ("Pidfile '".Echolot::Config::get()->{'pidfile'}."' does not exist\n") unless pid_exists(0); open (PIDFILE, '<'.Echolot::Config::get()->{'pidfile'}) or confess ("Cannot open pidfile '".Echolot::Config::get()->{'pidfile'}."': $!\n"); my $line = ; close PIDFILE; my ($pid, $host, $time) = $line =~ /^(\d+) \s+ (\S+) \s+ (\d+) \s* $/x or confess ("Cannot parse pidfile '$line'\n"); my $sent = kill $sig, $pid; ($sent == 1) or confess ("Did not send signal $sig to exactly one process but $sent. (pidfile reads $line)\n"); }; sub daemon_hup() { send_sig(1); }; sub daemon_stop() { send_sig(15); }; sub make_dirs() { for my $dir ( Echolot::Config::get()->{'resultdir'}, Echolot::Config::get()->{'thesaurusdir'}, ) { if ( ! -d $dir ) { mkdir ($dir, 0755) or confess ("Cannot create directory $dir: $!\n"); }; }; my @dirs = ( Echolot::Config::get()->{'private_resultdir'}, Echolot::Config::get()->{'gnupghome'}, Echolot::Config::get()->{'mixhome'}, Echolot::Config::get()->{'tmpdir'}, Echolot::Config::get()->{'storage'}->{'File'}->{'basedir'}, Echolot::Config::get()->{'mailerrordir'}, Echolot::Config::get()->{'mailerrordir'}.'/cur', Echolot::Config::get()->{'mailerrordir'}.'/tmp', Echolot::Config::get()->{'mailerrordir'}.'/new'); push @dirs, ( Echolot::Config::get()->{'mailin'}.'/cur', Echolot::Config::get()->{'mailin'}.'/tmp', Echolot::Config::get()->{'mailin'}.'/new' ) if (-d Echolot::Config::get()->{'mailin'}); for my $dir (@dirs) { if ( ! -d $dir ) { mkdir ($dir, 0700) or confess ("Cannot create directory $dir: $!\n"); }; }; }; sub hup_if_wanted($) { my ($nohup) = @_; if (!$nohup && pid_exists(0)) { daemon_hup() } else { print "Don't forget to run $PROGRAM_NAME process.\n"; }; }; ($EUID == 0) and die("$PROGRAM_NAME should not be run as root.\n"); my $params = { basedir => $Bin }; $params->{'basedir'} = $ENV{'ECHOLOT_HOME'} if (defined $ENV{'ECHOLOT_HOME'}); Getopt::Long::config('bundling'); if (!GetOptions ( 'help' => \$params->{'help'}, 'version' => \$params->{'version'}, 'verbose+' => \$params->{'verbose'}, 'nohup' => \$params->{'nohup'}, 'detach' => \$params->{'detach'}, 'process' => \$params->{'process'}, 'basedir' => \$params->{'basedir'}, 'quiet' => \$params->{'quiet'}, )) { die ("$PROGRAM_NAME: Usage: $PROGRAM_NAME [-fwhv]\n"); }; if ($params->{'help'}) { print ("Usage: $PROGRAM_NAME [options] command\n"); print ("See man pingd or perldoc pingd for more info.\n"); print ("echolot $VERSION - (c) 2002, 2003, 2004 Peter Palfrader \n"); print ("http://alioth.debian.org/projects/echolot/\n"); print ("\n"); print ("Commands:\n"); print (" start starts echolot pingd\n"); print (" signals pingd to ... \n"); print (" stop ... shutdown\n"); print (" process ... reopen outfile and process commands\n"); print (" add ... add a remailer address\n"); print (" delete ... delete a remailer address\n"); print (" disable ... disable a remailer\n"); print (" enable ... enable a remailer\n"); print (" set ... set remailer options\n"); print (" setremailercaps ... set remailer capabilities manually\n"); print (" deleteremailercaps ... delete remailer capabilities manually\n"); print (" getkeyconf ... request remailer-xxx data immediatly\n"); print (" sendpings ... request immediate sending of pings\n"); print (" sendchainpings ... request immediate sending of chainpings\n"); print (" buildstats ... build remailer stats immediatly\n"); print (" buildkeys ... buid keyrings immediatly\n"); print (" buildthesaurus ... build thesaurus immediatly\n"); print (" buildfromlines ... build fromlines immediatly\n"); print (" summary ... print status summary to log\n"); print (" dumpconf ... dump configuration\n"); exit 0; }; if ($params->{'version'}) { print ("echolot $VERSION\n"); print ("(c) 2002, 2003, 2004 Peter Palfrader \n"); print ("http://alioth.debian.org/projects/echolot/\n"); exit 0; }; $params->{'quiet'} = undef if ($params->{'verbose'}); my $COMMAND = shift @ARGV; die ("command required\n") unless defined $COMMAND; Echolot::Config::init( $params ); chdir( Echolot::Config::get()->{'homedir'} ); Echolot::Globals::init( version => $VERSION); if ($COMMAND eq 'add' || $COMMAND eq 'delete') { command_adddelete($COMMAND, @ARGV); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'set') { command_set(@ARGV); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'enable' || $COMMAND eq 'disable') { command_enabledisable($COMMAND, @ARGV); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'deleteremailercaps') { command_deleteremailercaps(@ARGV); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'setremailercaps') { command_setremailercaps(@ARGV); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'getkeyconf') { command_getkeyconf(@ARGV); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'sendpings') { command_sendpings(@ARGV); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'sendchainpings') { command_sendchainpings(@ARGV); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'buildstats') { Echolot::Commands::addCommand("buildstats"); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'buildkeys') { Echolot::Commands::addCommand("buildkeys"); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'buildthesaurus') { Echolot::Commands::addCommand("buildthesaurus"); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'buildfromlines') { Echolot::Commands::addCommand("buildfromlines"); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'summary') { Echolot::Commands::addCommand("summary"); hup_if_wanted($params->{'nohup'}); } elsif ($COMMAND eq 'process') { daemon_hup(); } elsif ($COMMAND eq 'stop') { daemon_stop(); } elsif ($COMMAND eq 'start') { # FIXME: remove stale pid files die ("Pidfile '".Echolot::Config::get()->{'pidfile'}."' exists\n") if pid_exists(1); Echolot::Log::init(); make_dirs(); Echolot::Config::check_binaries(); if ($params->{'detach'}) { print "Detaching.\n" unless ($params->{'quiet'}); Echolot::Log::debug("Detaching."); exit(0) if (fork()); POSIX::setsid(); exit(0) if (fork()); my $logfile = Echolot::Config::get()->{'logfile'}; open (STDOUT, ">>$logfile") or die ("Cannot open '$logfile' as STDOUT\n"); open (STDERR, ">&STDOUT") or die ("Cannot dup STDOUT as STDERR\n"); open (STDIN , "{'process'} ); Echolot::Log::info "Shutdown complete."; } else { daemon_run( $params->{'process'} ); }; } elsif ($COMMAND eq 'dumpconf') { Echolot::Config::dump(); } elsif ($COMMAND eq 'convert') { Echolot::Globals::initStorage(); setSigHandlers(); Echolot::Globals::get()->{'storage'}->convert(); Echolot::Globals::get()->{'storage'}->commit(); Echolot::Globals::get()->{'storage'}->finish(); } else { die ("Command $COMMAND unknown"); }; exit 0; # vim: set ts=4 shiftwidth=4: echolot-2.1.9/LICENSE0000644000175000017500000000146612422221767013523 0ustar weaselweaselEcholot - a Pinger for anonymous remailers. Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader Echolot 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA echolot-2.1.9/pingd.conf.sample0000644000175000017500000000523412422221767015743 0ustar weaselweasel# # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # see man pingd.conf(5) for a list of all available configuration options. # There are a lot more than those listed in this sample. $CONFIG = { # A short name for your site/pinger. Is used in the statistics produced. 'sitename' => 'unconfigured', # The local part of the pinger's email address # In "pinger@remailer.example.com" the localpart is "pinger". 'my_localpart' => 'pinger', # The domain part (FQDN) of the pinger's email address. # In "pinger@remailer.example.com" the domain part is "remailer.example.com". 'my_domain' => 'remailer.example.com', # The email address of the human operator that runs this pinger. 'operator_address' => 'remop@example.org', # Name of the mixmaster executable. If it is not in your PATH make # sure to include path information. #'mixmaster' => '/home/pinger/Mix/mix', # The recipient_delimiter parameter specifies the separator between user names # and address extensions (user+foo). # # If it is an empty string Echolot does not make use of user defined mailboxes # but rather encodes the message type et al in a Comment/Realname part of an # address. # # The use of recipient_delimiter is strongly recommended if your MTA setup # supports it. 'recipient_delimiter' => '+', #'recipient_delimiter' => '', # Also build separate rlists with data from only DSA pings, only RSA # pings and only unencrypted pings. 'separate_rlists' => 0, # Build a combined list of all different stats too. While this is no # standard format it is nice to read for a human eye. 'combined_list' => 0, # Log file and level (valid levels are debug, info, notice, warning, error, # critical, alert, emergency) #'logfile' => 'pingd.log', #'loglevel' => 'info', }; 1; # vim:set syntax=perl: echolot-2.1.9/tools/0002755000175000017500000000000012422221767013651 5ustar weaselweaselecholot-2.1.9/tools/install-perl-modules0000755000175000017500000000220312422221767017646 0ustar weaselweasel#! /usr/bin/perl -w # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # This script installs all perl libraries Echolot needs # using your CPAN module use strict; use CPAN; for my $mod (qw{Data::Dumper Digest::MD5 HTML::Template GnuPG::Interface}) { my $obj = CPAN::Shell->expand('Module',$mod); $obj->install(); }; echolot-2.1.9/tools/pingctl0000755000175000017500000000660512422221767015244 0ustar weaselweasel#!/bin/sh # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002 admin@arancio.net # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # pingcntl: echolot control + wrapper set -e ################################################################ # You perhaps want to change those items USER=pinger VERBOSE=0 BASEDIR="/home/pinger/echolot" PINGD="$BASEDIR/pingd" PIDFILE="$BASEDIR/pingd.pid" # You probably don't want to mess with stuff below this line ################################################################ CHECKULIMIT=1 CHECKUID=1 wait_for_deaddaemon () { PID=$1 sleep 3 if test -n "$PID" then if kill -0 $PID 2>/dev/null then echo -n "Waiting for pid $PID ." cnt=0 while kill -0 $PID 2>/dev/null do cnt=`expr $cnt + 1` if [ $cnt -gt 30 ] then echo " Failed.. " exit 1; fi sleep 2 echo -n "." done rm -f $PIDFILE else rm -f $PIDFILE fi fi } # Check for evil ulimits if [ "$CHECKULIMIT" -gt "0" ]; then FDs=`ulimit -n` HFDs=`ulimit -H -n` if [ "$FDs" -lt "512" ]; then if [ "$HFDs" -lt "512" ]; then echo "Hardlimit for open File Descriptors is less than 512." >&2 echo "Please consider raising it." >&2 if [ "$FDs" -lt "$HFDs" ]; then echo "Raising it to $HFDs" >&2 ulimit -n $HFDs fi else if [ "$HFDs" -lt "1024" ]; then FDs=$HFDs else FDs=1024 fi echo "Softlimit for open File Descriptors is less than 512." >&2 echo "Raising it to $FDs" >&2 ulimit -n $FDs fi fi fi # Check for right User SU="" if [ "$CHECKUID" -gt "0" ]; then CUID=`id -u` CUIDNAME=`id -nu` if [ "$CUIDNAME" = "$USER" ]; then SU="" elif [ "$CUID" = "0" ]; then SU="su $USER -c" else echo "You are neither $USER nor root. Aborting." >&2 exit 1; fi fi # set VERBOSE if [ $VERBOSE -gt 0 ]; then VERBOSE="--verbose" else VERBOSE="" fi case $1 in start) if [ -f $PIDFILE ] ; then PID=`cat $PIDFILE 2>/dev/null` || true if kill -0 $PID 2>/dev/null then echo "pingd already running." exit 0 else echo -n "Removing stale pid file: " rm -f $PIDFILE echo "$PIDFILE." fi fi echo -n "Starting Echolot: pingd" if [ ! -z "$SU" ]; then $SU "$PINGD --detach $VERBOSE start" else $PINGD --detach $VERBOSE start fi echo "." ;; stop) echo -n "Stopping Echolot: pingd" if [ ! -z "$SU" ]; then $SU "$PINGD stop" else $PINGD stop fi echo "." ;; reload|force-reload|restart) PID=`cat $PIDFILE 2>/dev/null` || true $0 stop wait_for_deaddaemon $PID $0 start ;; *) echo "Usage: $0 (start|stop|reload|force-reload|restart)" >&2 exit 1 ;; esac exit 0 # vim:set ts=2: # vim:set shiftwidth=2: echolot-2.1.9/tools/create-distribution0000755000175000017500000000306212422221767017556 0ustar weaselweasel#!/bin/sh # # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # This script is used after exporting the CVS to build a new # release tarball set -e version=`grep 'VERSION =' pingd | sed -e "s/.* '//" -e "s/'.*//"` dirname="echolot-$version" tagname="echolot-$version" if ! git show "$tagname" -- > /dev/null; then echo >&2 "Seems this release is not tagged yet." exit 1 fi mkdir "$dirname" git archive "$tagname" | (cd "$dirname" && tar xvf -) (cd "$dirname" && pod2man --section=1 --release="$version" --center=Echolot pingd doc/pingd.1 ) (cd "$dirname" && pod2man --section=5 --release="$version" --center=Echolot doc/pingd.conf.pod doc/pingd.conf.5 ) tar czf "$dirname.tar.gz" "$dirname" echo "Maybe run gpg --detach-sign $dirname.tar.gz" echolot-2.1.9/COPYING0000644000175000017500000004311012422221767013541 0ustar weaselweasel GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. echolot-2.1.9/README0000644000175000017500000002550712422221767013400 0ustar weaselweasel##################################################################### ## R E A D M E F O R E C H O L O T ########################### ##################################################################### | Echolot, das: (German) sonic depth finder PURPOSE ------- Echolot is a Pinger for anonymous remailers such as Mixmaster A Pinger in the context of anonymous remailers is a program that regularly sends messages through remailers to determine their status. Based on the responses, the Pinger calculates reliability statistics which may be used by remailer clients to choose a chain of remailers to use. Furthermore, Echolot collects configuration parameters and keys of remailers and offers the collected information in a format readable by remailer clients. This helps reduce the administration effort required to use or host remailers. This is Echolot2. Besides the name, author, and purpose, this software has nothing to do with Echolot1. Echolot2 has been written from scratch. LICENSE ------- Please see the file named "LICENSE". REQUIREMENTS ------------ o GnuPG (1.0.7 or higher required) o Mixmaster http://mixmaster.sourceforge.net/ o Perl (5.8 or higher suggested) o a local Mail Transfer Agent (MTA) o Procmail (recommended) The following perl modules: o HTML::Template o GnuPG::Interface (0.33 or higher required) o Data::Dumper (should be part of perl-base) o Digest::MD5 (included in perl 5.8 or higher) INITIAL SETUP ------------- o Verify that gpg 1.0.7 or later is installed: # gpg -version o If the required perl modules above are not yet installed on your system, or if you are not sure if these perl libraries are installed, please install or upgrade the libraries as follows. Even if you already have these libraries installed, there is no harm in following this installation procedure anyway. Execute the install-perl-modules script from the tools directory. It makes use of the CPAN module to check whether you have the required modules installed and if not downloads and installs them for you. # tools/install-perl-modules If this command reports errors, please verify that you are using perl 5.8 or higher and are connected to the Internet. [Note: if you operating system already has packages with the required libraries than those generally are preferred since they integrate better with your system. On Debian for instance the following command can be used to get everything that is required: # apt-get install libgnupg-interface-perl libhtml-template-perl ] o Create a new user named »pinger« (You can use a different user name if you so desire. The remainder of this document assumes that Echolot has been installed as user »pinger«). o Copy all Echolot files and directories to the directory ~pinger/echolot. o Copy the pingd.conf.sample file to pingd.conf. o Check the homedir setting and set sitename in pingd.conf to match your host. o If the Mixmaster executable »mix« is not in your PATH, set the »mixmaster« config option in pingd.conf to point to your local installation of mixmaster. Echolot can use any accessible mixmaster binary, such as the mix binary of a remailer that may be installed on the same machine. (Frequently found in /home/remailer/Mix/mix.) Echolot will not share the Mixmaster pool or key rings with the existing Mixmaster installation. Instead, Echolot uses pools and keyrings as specified by the mixhome configuration option. If you prefer, you can build a second Mixmaster binary for the exclusive use of Echolot and place that binary in /home/pinger/Mix. There is no need to put configuration information or key rings into that directory - they will not get used. o If the GnuPG executable »gpg« is not in your PATH, set the »gnupg« configuration option in pingd.conf. o Set my_localpart and my_domain in pingd.conf to the appropriate values for your pinger. Mail to my_localpart@my_domain needs to reach Echolot. o Make sure your MTA supports user defined mailboxes to ensure that email addressed to my_localpart+anything@my_domain will reach Echolot. If your MTA uses a character other than »+« to indicate a user defined extension, set recipient_delimiter accordingly in pingd.conf. If you are using postfix as your MTA, adding the following line to postfix.s main.cf file will enable user defined mailboxes: recipient_delimiter = + If you are using an MTA other than postfix, consult your MTA's documentation to determine how to enable user defined mailboxes. If it is not possible for you to have user defined mailboxes set recipient_delimiter to the empty string "" in pingd.conf. Echolot will then work around it (This is _not_ recommended). o Echolot can read its incoming mail either from an mbox format mailbox or from a Maildir. The latter is preferred for technical reasons since a Maildir does not require file locking. Echolot's »mailin« configuration variable defines from which location mail is being read. The variable defaults to »mail«. If this is a directory, Maildir is assumed, otherwise mbox format is assumed. If you can only use mbox format for incoming email: Change the »mailin« config option to »/var/spool/pinger« (or wherever incoming email for user pinger is being spooled on your system). If you are able to use Maildir (recommended): Mail will be delivered to /home/pinger/echolot/mail, a Maildir mailbox. Create Echolot's Maildir: # mkdir /home/pinger/echolot/mail Make sure the directory owned by pinger: # chown pinger. /home/pinger/echolot/mail If you are using postfix as your MTA, add one of the following lines to postfix's main.cf file to enable the use of procmail depending where on your system procmail is located. mailbox_command = /usr/bin/procmail mailbox_command = /usr/local/bin/procmail Reload postfix for the changes to main.cf to take effect. # postfix reload With procmail now active in your MTA, save the following two lines as /home/pinger/.procmailrc to ensure that mail for Echolot will be stored in Echolot.s Maildir: :0 $HOME/echolot/mail/ (CAVEAT: the trailing slash is significant and may not be omitted!) If you are using qmail as your MTA, do the following: # echo "./echolot/mail/" > .qmail # touch .qmail-default o Finally, double-check to make sure that all of Echolot.s files and directories are owed by user pinger. RUNNING ECHOLOT FOR THE FIRST TIME ---------------------------------- o Obtain the email addresses of 4 reliable remailers. Once connected to the remailer network, Echolot will over time learn about other remailers in operation. You can find a list of email addresses of reliable remailers to seed Echolot.s auto-discovery feature at http://www.noreply.org/echolot/rlist2.txt This list was created by the Echolot program. o As user »pinger«, open two terminal windows. o Change into the directory where echolot is kept. $ cd echolot o In the first terminal window, type: [ you may want to set the log level to 'debug' in pingd.conf to get an idea what exactly Echolot is doing ] $ ./pingd --detach start $ tail -f pingd.log o In the second terminal window, type: $ ./pingd add ... You can also use the following shell magic to add all addresses from an existing rlist.txt or mlist.txt: $ grep \$remailer rlist.txt | cut -f 2 -d \< | cut -f 1 -d \> | xargs ./pingd add Monitor the first terminal in which you started pingd. You should see mention of email addresses being added. o In the second terminal window, execute $ ./pingd getkeyconf This will request remailer key and configuration files from the remailers that you added in the previous step. o pingd can be stopped with the command $ ./pingd stop VERIFYING ECHOLOT's OPERATION ----------------------------- o Wait a few minutes for Echolot to receive results back from the remailers that have been pinged o Look at one of Echolot.s result pages with the web browser of your choice. For example: $ cd /home/pinger/echolot/results $ lynx mlist2.html The file should list several remailers. NOTE: Results for Type I remailers can be expected within minutes. Results for Type II remailers may take up to an hour to appear. DAY-TO-DAY OPERATION -------------------- o To run Echolot in the background, run $ ./pingd --detach start o You can monitor the log file to obtain debugging output: $ tail -f pingd.conf Do not forget to set the appropriate log level in pingd.conf. o The tools directory contains the »pingctl« wrapper for Echolot. The wrapper takes care of checking ulimits, userid, and cd'ing to the correct directory. To start Echolot at system startup, install this wrapper as an init script in /etc/init.d or /usr/local/etc/rc.d, or wherever your operating system stores System V-style initialization scripts. You can link this wrapper from the runlevel directories if your init is SysV style. o Echolot puts its stats in the result directory. Echolot also produces an index file named echolot.html. If you want to use echolot.html as your webpage.s index page, create a symbolic link. $ ln -sf echolot.html index.html Alternatively, you can set the indexfilebasename option in pingd.conf to »index« (no .html extension). o Echolot additionally produces .meta files by default. These files include extra headers that your http server should send to clients. If you are using Apache as your web sever, you can load the mod_cern_meta Apache module and set MetaFiles to "on". Please ensure that Apache's MetaSuffix matches your meta_extension setting (".meta" by default) and that MetaDir is set to ".". See your web server's documentation for more information on meta files. CONFIGURATION ------------- Consult the pingd.conf.5 manpage for documentation on the available configuration options. To obtain all available configuration options and their current value run: $ ./pingd dumpconf You will need to restart pingd after making changes to pingd.conf for the changes to take effect. CAVEATS ------- Echolot will keep open all ping and metadata files. This means it needs quite a few file descriptors (about 2 * total keys or 6 to 8 * remailers plus some for perl). If you have a very strict ulimit for open files you need to increase it. A ulimit of 512 should suffice. Obscure errors experienced might be caused by a ulimit that has been set too low. Please report bugs and feature requests at http://alioth.debian.org/projects/echolot/ The Echolot homepage can be found at http://www.palfrader.org/echolot/ ACKNOWLEDGEMENTS ---------------- Orange Admin for contributing ideas and templates Lucky Green for (re)writing docs BiKiKii Admin for valuable feedback All testers of Echolot. echolot-2.1.9/.gitignore0000644000175000017500000000003512422221767014475 0ustar weaselweaseldoc/pingd.1 doc/pingd.conf.5 echolot-2.1.9/templates/0002755000175000017500000000000012422221767014507 5ustar weaselweaselecholot-2.1.9/templates/rlist2-dsa.html0000644000175000017500000000341512422221767017362 0ustar weaselweasel Cypherpunk Remailers (v2, DSA only) [<TMPL_VAR NAME="SITE_NAME">] ">

Remailer Reliability Stats []

Up Plaintext version

Available Stats:
Cypherpunk (Type I) Cypherpunk (Type I) Mixmaster (Type II) Combined
v1 (rsa) (dsa) (cleartext) v1 V2
v2 (rsa) (dsa) (cleartext) v2

Cypherpunk Remailers (v2, DSA only)

This is an automatically generated list of remailer reliability statistics. Please see the Legend below for interpretative data.



Created by Echolot .
Last update: . echolot-2.1.9/templates/v1legend.html0000644000175000017500000000150412422221767017100 0ustar weaselweasel

Legend

History: The result of test messages sent in the last 12 days.

?No test message sent
(space)Response not received
#Response in less than 5 minutes
*Response in less than 1 hour
+Response in less than 4 hours
-Response in less than 24 hours
.Response in less than 2 days
_Response in more than 2 days

Latency: The average response time of the remailer.

Uptime: The fraction of responses received from tests sent in the last 12 days. echolot-2.1.9/templates/echolot.html0000644000175000017500000000760712422221767017042 0ustar weaselweasel Remailer Reliability Stats [<TMPL_VAR NAME="SITE_NAME">] ">

Remailer Reliability Stats []

This pinger automatically collects and generates remailer reliability statistics for both Type I (Cypherpunk) and Type II (Mixmaster) remailers. It also offers current keyrings and each remailer's reply to remailer-xxx queries for your convenience.

Available Stats

Out of the Cypherpunk and Mixmaster remailers ( unique addresses) on these stats there are CPunks and Mixes over 98.0% in terms of overall reliability.

HTML

Cypherpunk (Type I) Mixmaster (Type II) Combined
rlist.html (rsa, dsa, unencrypted) mlist.html clist2.html
rlist2.html (rsa, dsa, unencrypted) mlist2.html

Plain Text

Cypherpunk (Type I) Mixmaster (Type II) Combined
rlist.txt (rsa, dsa, unencrypted) mlist.txt clist2.txt
rlist2.txt (rsa, dsa, unencrypted) mlist2.txt

Available Keyrings

Cypherpunk (Type I) Mixmaster (Type II)
RSA only pubring.mix
RSA and DSA type2.list

Thesaurus

Find the Thesaurus index in thesaurus/.

From Header Lines

A summary of From Headers each remailer uses has also been compiled. It also lists which remailers allow setting the From: Header.

Created by Echolot at (individual pages may be newer).
This pinger is operated by .
All times in Coordinated Universal Time (UTC). echolot-2.1.9/templates/clist.html0000644000175000017500000000337212422221767016516 0ustar weaselweasel Remailers (combined list) [<TMPL_VAR NAME="SITE_NAME">] ">

Remailer Reliability Stats []

Up Plaintext version

Available Stats:
Cypherpunk (Type I) Cypherpunk (Type I) Mixmaster (Type II) Combined
v1 (rsa) (dsa) (cleartext) v1 V2
v2 (rsa) (dsa) (cleartext) v2

Remailers (combined list)

This is an automatically generated list of remailer reliability statistics. Please see the Legend below for interpretative data.



Created by Echolot .
Last update: . echolot-2.1.9/templates/echolot.css0000644000175000017500000000346212422221767016661 0ustar weaselweaselbody { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 15px; font-style: normal; line-height: normal; font-weight: normal; font-variant: normal; text-transform: none} h1 { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 21px; font-style: normal; line-height: normal; font-weight: normal; font-variant: normal; text-transform: none } h2 { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 19px; font-style: normal; line-height: normal; font-weight: normal; font-variant: normal; text-transform: none } h3 { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 17px; font-style: normal; line-height: normal; font-weight: normal; font-variant: normal; text-transform: none } .tablehead { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 15px; font-style: normal; line-height: normal; font-weight: bolder; font-variant: normal; text-transform: none ; text-decoration: none} td { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 15px; font-style: normal; line-height: normal; font-weight: normal; font-variant: normal; text-transform: none } ul { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 15px; font-style: normal; line-height: normal; font-weight: normal; font-variant: normal; text-transform: none } li { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 15px; font-style: normal; line-height: normal; font-weight: normal; font-variant: normal; text-transform: none } tr { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 15px; font-style: normal; line-height: normal; font-weight: normal; font-variant: normal; text-transform: none } a { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 15px; font-style: normal; line-height: normal; font-weight: normal; font-variant: normal; text-transform: none } echolot-2.1.9/templates/rlist-clear.html0000644000175000017500000000342112422221767017614 0ustar weaselweasel Cypherpunk Remailers (cleartext only) [<TMPL_VAR NAME="SITE_NAME">] ">

Remailer Reliability Stats []

Up Plaintext version

Available Stats:
Cypherpunk (Type I) Cypherpunk (Type I) Mixmaster (Type II) Combined
v1 (rsa) (dsa) (cleartext) v1 V2
v2 (rsa) (dsa) (cleartext) v2

Cypherpunk Remailers (cleartext only)

This is an automatically generated list of remailer reliability statistics. Please see the Legend below for interpretative data.



Created by Echolot .
Last update: . echolot-2.1.9/templates/mlist.html0000644000175000017500000000335612422221767016532 0ustar weaselweasel Mixmaster Remailers [<TMPL_VAR NAME="SITE_NAME">] ">

Remailer Reliability Stats []

Up Plaintext version

Available Stats:
Cypherpunk (Type I) Cypherpunk (Type I) Mixmaster (Type II) Combined
v1 (rsa) (dsa) (cleartext) v1 V2
v2 (rsa) (dsa) (cleartext) v2

Mixmaster Remailers

This is an automatically generated list of remailer reliability statistics. Please see the Legend below for interpretative data.



Created by Echolot .
Last update: . echolot-2.1.9/templates/thesaurusindex.html0000644000175000017500000000322612422221767020451 0ustar weaselweasel Thesaurus [<TMPL_VAR NAME="SITE_NAME">] ">

Thesaurus []

Up

nickAddressconfhelpkeystatsadminkey
">
N/A
">
N/A
">
N/A
">
N/A
">
N/A


Created by Echolot .
Last update: . echolot-2.1.9/templates/rlist2-clear.html0000644000175000017500000000343112422221767017677 0ustar weaselweasel Cypherpunk Remailers (v2, cleartext only) [<TMPL_VAR NAME="SITE_NAME">] ">

Remailer Reliability Stats []

Up Plaintext version

Available Stats:
Cypherpunk (Type I) Cypherpunk (Type I) Mixmaster (Type II) Combined
v1 (rsa) (dsa) (cleartext) v1 V2
v2 (rsa) (dsa) (cleartext) v2

Cypherpunk Remailers (v2, cleartext only)

This is an automatically generated list of remailer reliability statistics. Please see the Legend below for interpretative data.



Created by Echolot .
Last update: . echolot-2.1.9/templates/LICENSE0000644000175000017500000000241112422221767015510 0ustar weaselweasel The files in this directory are part of Echolot. In order to keep the templates short, the license statement is in this file. It applies to the files in this directory, namley: * clist.html * echolot.css * echolot.html * fromlinesindex.html * mlist.html * mlist2.html * rlist-clear.html * rlist-dsa.html * rlist-rsa.html * rlist.html * rlist2-clear.html * rlist2-dsa.html * rlist2-rsa.html * rlist2.html * thesaurusindex.html * v1legend.html * v2legend.html Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader Echolot 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA echolot-2.1.9/templates/fromlinesindex.html0000644000175000017500000000341112422221767020420 0ustar weaselweasel From Headers [<TMPL_VAR NAME="SITE_NAME">] ">

From Headers []

Up

Default From: Headers

The default From headers of all remailers when sending e-mail. Middleman remailers are not listed here.

nickDisclaimerFrom Linetype
top bot

User Supplied From: Headers

If a message sent with a user supplied From header differs from one without, the remailer is listed here. The difference can either be an additional disclaimer, actually allowing (partial) From headers, etc.

nickDisclaimerFrom Linetype
top bot


Created by Echolot .
Last update: . echolot-2.1.9/templates/rlist.html0000644000175000017500000000335712422221767016540 0ustar weaselweasel Cypherpunk Remailers [<TMPL_VAR NAME="SITE_NAME">] ">

Remailer Reliability Stats []

Up Plaintext version

Available Stats:
Cypherpunk (Type I) Cypherpunk (Type I) Mixmaster (Type II) Combined
v1 (rsa) (dsa) (cleartext) v1 V2
v2 (rsa) (dsa) (cleartext) v2

Cypherpunk Remailers

This is an automatically generated list of remailer reliability statistics. Please see the Legend below for interpretative data.



Created by Echolot .
Last update: . echolot-2.1.9/templates/rlist2.html0000644000175000017500000000337212422221767016617 0ustar weaselweasel Cypherpunk Remailers (v2) [<TMPL_VAR NAME="SITE_NAME">] ">

Remailer Reliability Stats []

Up Plaintext version

Available Stats:
Cypherpunk (Type I) Cypherpunk (Type I) Mixmaster (Type II) Combined
v1 (rsa) (dsa) (cleartext) v1 V2
v2 (rsa) (dsa) (cleartext) v2

Cypherpunk Remailers (v2)

This is an automatically generated list of remailer reliability statistics. Please see the Legend below for interpretative data.



Created by Echolot .
Last update: . echolot-2.1.9/templates/rlist-rsa.html0000644000175000017500000000340512422221767017315 0ustar weaselweasel Cypherpunk Remailers (RSA only) [<TMPL_VAR NAME="SITE_NAME">] ">

Remailer Reliability Stats []

Up Plaintext version

Available Stats:
Cypherpunk (Type I) Cypherpunk (Type I) Mixmaster (Type II) Combined
v1 (rsa) (dsa) (cleartext) v1 V2
v2 (rsa) (dsa) (cleartext) v2

Cypherpunk Remailers (RSA only)

This is an automatically generated list of remailer reliability statistics. Please see the Legend below for interpretative data.



Created by Echolot .
Last update: . echolot-2.1.9/templates/mlist2.html0000644000175000017500000000337012422221767016610 0ustar weaselweasel Mixmaster Remailers (v2) [<TMPL_VAR NAME="SITE_NAME">] ">

Remailer Reliability Stats []

Up Plaintext version

Available Stats:
Cypherpunk (Type I) Cypherpunk (Type I) Mixmaster (Type II) Combined
v1 (rsa) (dsa) (cleartext) v1 V2
v2 (rsa) (dsa) (cleartext) v2

Mixmaster Remailers (v2)

This is an automatically generated list of remailer reliability statistics. Please see the Legend below for interpretative data.



Created by Echolot .
Last update: . echolot-2.1.9/templates/rlist2-rsa.html0000644000175000017500000000341512422221767017400 0ustar weaselweasel Cypherpunk Remailers (v2, RSA only) [<TMPL_VAR NAME="SITE_NAME">] ">

Remailer Reliability Stats []

Up Plaintext version

Available Stats:
Cypherpunk (Type I) Cypherpunk (Type I) Mixmaster (Type II) Combined
v1 (rsa) (dsa) (cleartext) v1 V2
v2 (rsa) (dsa) (cleartext) v2

Cypherpunk Remailers (v2, RSA only)

This is an automatically generated list of remailer reliability statistics. Please see the Legend below for interpretative data.



Created by Echolot .
Last update: . echolot-2.1.9/templates/v2legend.html0000644000175000017500000000612412422221767017104 0ustar weaselweasel

Legend

The stats table shows the 12-day performance history of each remailer. Pings (test messages) are sent to each remailer and response time is measured.

Latent-Hist shows the average default response time for each day:

0less than 20 minutes
1less than 1 hour
...
9less than 9 hours
Aless than 12 hours
Bless than 18 hours
Cless than 24 hours
...
Gless than 48 hours
Hmore than 48 hours
?No responses received / No data

Latent shows the average default response time in HH:MM format.

Uptime-Hist shows the Uptime percentage (responses received divided by pings sent) for each day:

+100% (Responses were received for all pings sent)
990-99.9% (About 9 responses were received for every 10 pings)
...
110-19.9%
00-9.9%
?No pings sent / No data

Uptime shows the average Uptime percentage for 12 days.

Options shows an abbreviated form of the strings listed in the Remailer-Capabilities section above:

Dmiddle (Remailer is middleman and chains to other remailers)
Ppost (Supports news posting (Anon-Post-To or Post)
M/R/2mix/remix/remix2 (Supported Mixmaster features)
Hhybrid (Supports CPunk directives in Mix messages)
G/2repgp/repgp2
Opgponly (Requires Cypherpunk messages to be PGP encrypted)
Xext (Supports extended directive features)
Amax (Supports Max-Size, Max-Count, and Max-Date directives)
Ttest (Supports the Test-To directive)
Llatent (Supports the Latent-Time directive)
e/Eek/ekx (Supports Encrypt-Key/-3DES,-CAST directives)
Uesub (Supports the Encrypt-Subject directive)
Iinflt (Supports the Inflate directive)
Nrhop (Supports the Rand-Hop directive)
#klen - The digit indicates the maximum message size:
9Max is greater than 900K
8Max is less than 900K
...
1Max is less than 200K
0Max is less than 100K
echolot-2.1.9/templates/rlist-dsa.html0000644000175000017500000000340512422221767017277 0ustar weaselweasel Cypherpunk Remailers (DSA only) [<TMPL_VAR NAME="SITE_NAME">] ">

Remailer Reliability Stats []

Up Plaintext version

Available Stats:
Cypherpunk (Type I) Cypherpunk (Type I) Mixmaster (Type II) Combined
v1 (rsa) (dsa) (cleartext) v1 V2
v2 (rsa) (dsa) (cleartext) v2

Cypherpunk Remailers (DSA only)

This is an automatically generated list of remailer reliability statistics. Please see the Legend below for interpretative data.



Created by Echolot .
Last update: . echolot-2.1.9/doc/0002755000175000017500000000000012422222123013241 5ustar weaselweaselecholot-2.1.9/doc/pingd.10000644000175000017500000003423012422222123014424 0ustar weaselweasel.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "PINGD 1" .TH PINGD 1 "2014-10-23" "2.1.9" "Echolot" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" pingd \- echolot ping daemon .SH "SYNOPSIS" .IX Header "SYNOPSIS" .IP "\fBpingd\fR \fBstart\fR" 4 .IX Item "pingd start" .PD 0 .IP "\fBpingd\fR \fBstop\fR" 4 .IX Item "pingd stop" .IP "\fBpingd\fR \fBprocess\fR" 4 .IX Item "pingd process" .IP "\fBpingd\fR \fBadd\fR \fIaddress\fR [\fIaddress\fR ...]" 4 .IX Item "pingd add address [address ...]" .IP "\fBpingd\fR \fBdelete\fR \fIaddress\fR [\fIaddress\fR ...]" 4 .IX Item "pingd delete address [address ...]" .IP "\fBpingd\fR \fBdisable\fR \fIaddress\fR [\fIaddress\fR ...]" 4 .IX Item "pingd disable address [address ...]" .IP "\fBpingd\fR \fBenable\fR \fIaddress\fR [\fIaddress\fR ...]" 4 .IX Item "pingd enable address [address ...]" .IP "\fBpingd\fR \fBset\fR option=value [option=value..] \fIaddress\fR [\fIaddress\fR ...]" 4 .IX Item "pingd set option=value [option=value..] address [address ...]" .IP "\fBpingd\fR \fBsetremailercaps\fR \fIcapsstring\fR" 4 .IX Item "pingd setremailercaps capsstring" .IP "\fBpingd\fR \fBdeleteremailercaps\fR \fIaddress\fR" 4 .IX Item "pingd deleteremailercaps address" .IP "\fBpingd\fR \fBgetkeyconf\fR [\fIaddress\fR [\fIaddress\fR ...]]" 4 .IX Item "pingd getkeyconf [address [address ...]]" .IP "\fBpingd\fR \fBsendpings\fR [\fIaddress\fR [\fIaddress\fR ...]]" 4 .IX Item "pingd sendpings [address [address ...]]" .IP "\fBpingd\fR \fBsendchainpings\fR \fIaddress\fR\fB:\fR\fIaddress\fR [\fIaddress\fR\fB:\fR\fIaddress\fR ...]" 4 .IX Item "pingd sendchainpings address:address [address:address ...]" .IP "\fBpingd\fR \fBbuildstats\fR" 4 .IX Item "pingd buildstats" .IP "\fBpingd\fR \fBbuildkeys\fR" 4 .IX Item "pingd buildkeys" .IP "\fBpingd\fR \fBbuildthesaurus\fR" 4 .IX Item "pingd buildthesaurus" .IP "\fBpingd\fR \fBbuildfromlines\fR" 4 .IX Item "pingd buildfromlines" .IP "\fBpingd\fR \fBsummary\fR" 4 .IX Item "pingd summary" .IP "\fBpingd\fR \fBdumpconf\fR" 4 .IX Item "pingd dumpconf" .PD .SH "DESCRIPTION" .IX Header "DESCRIPTION" pingd is the heart of echolot. Echolot is a pinger for anonymous remailers. .PP A Pinger in the context of anonymous remailers is a program that regularily sends messages through remailers to check their reliability. It then calculates reliability statistics which are used by remailer clients to choose the chain of remailers to use. .PP Additionally it collects configuration parameters and keys of all remailers and offers them in a format readable by remailer clients. .PP When called without parameters pingd schedules tasks like sending pings, processing incoming mail and requesting remailer-xxx data and runs them at configurable intervalls. .SH "COMMANDS" .IX Header "COMMANDS" .IP "\fBstart\fR" 4 .IX Item "start" Start the ping daemon. .IP "\fBstop\fR" 4 .IX Item "stop" Send the running pingd process a \s-1SIGTERM.\s0 .IP "\fBprocess\fR" 4 .IX Item "process" Sends a \s-1HUP\s0 signal to the daemon which instructs it to process the commands. .Sp For other effects of sending the \s-1HUP\s0 Signal see the \s-1SIGNALS\s0 section below. .IP "\fBadd\fR \fIaddress\fR [\fIaddress\fR ...]" 4 .IX Item "add address [address ...]" Add \fIaddress\fR to the list of remailers to query for keys and confs. .IP "\fBdelete\fR \fIaddress\fR [\fIaddress\fR ...]" 4 .IX Item "delete address [address ...]" Delete \fIaddress\fR from the list of remailers to query for keys and confs. Delete all statistics and keys for that remailer. .Sp Note that echolot will add back this remailer if it learns of it from other remailers again. If you do not want that, use the disable command. .IP "\fBdisable\fR \fIaddress\fR [\fIaddress\fR ...]" 4 .IX Item "disable address [address ...]" Shorthand for \fBset showit=off\fR \fBpingit=off\fR \fBfetch=off\fR. This makes echolot completely ignore that remailer, until you enable it again. .IP "\fBenable\fR \fIaddress\fR [\fIaddress\fR ...]" 4 .IX Item "enable address [address ...]" Shorthand for \fBset showit=on\fR \fBpingit=on\fR \fBfetch=on\fR. .IP "\fBset\fR option=value [option=value..] \fIaddress\fR [\fIaddress\fR ...]" 4 .IX Item "set option=value [option=value..] address [address ...]" Possible options and values: .RS 4 .IP "\fBshowit=\fR{\fBon\fR,\fBoff\fR}" 4 .IX Item "showit={on,off}" Set \fBshowit\fR (show remailer in mlist, rlist etc.) for remailer \fIaddress\fR to either \fBon\fR or \fBoff\fR. .IP "\fBpingit=\fR{\fBon\fR,\fBoff\fR}" 4 .IX Item "pingit={on,off}" Set \fBpingit\fR (send out pings to that remailer) for remailer \fIaddress\fR to either \fBon\fR or \fBoff\fR. .IP "\fBfetch=\fR{\fBon\fR,\fBoff\fR}" 4 .IX Item "fetch={on,off}" Set \fBfetch\fR (fetch remailer-key and remailer-conf) for remailer \fIaddress\fR to either \fBon\fR or \fBoff\fR. .RE .RS 4 .RE .IP "\fBsetremailercaps\fR \fIcapsstring\fR" 4 .IX Item "setremailercaps capsstring" Some remailers (Mixmaster V2 \- currently lcs and passthru2) don't return a useable remailer-conf message. For such remailers you need to set it manually. .Sp For instance: .Sp .Vb 2 \& ./pingd setremailercaps \*(Aq$remailer{"passthru2"} = " mix middle";\*(Aq \& ./pingd setremailercaps \*(Aq$remailer{"lcs"} = " mix klen1000";\*(Aq .Ve .IP "\fBdeleteremailercaps\fR \fIaddress\fR" 4 .IX Item "deleteremailercaps address" Delete remailer-conf data for \fIaddress\fR. The config data will be reset from the next valid remailer-conf reply by the remailer. .IP "\fBgetkeyconf\fR [\fIaddress\fR [\fIaddress\fR ...]]" 4 .IX Item "getkeyconf [address [address ...]]" Send a command to immediatly request keys and configuration from remailers. If no addresses are given requests will be sent to all remailers. .IP "\fBsendpings\fR [\fIaddress\fR [\fIaddress\fR ...]]" 4 .IX Item "sendpings [address [address ...]]" Send a command to immediatly send pings to the given remailers. If no addresses are given requests will be sent to all remailers. .IP "\fBsendchainpings\fR \fIaddress\fR\fB:\fR\fIaddress\fR [\fIaddress\fR\fB:\fR\fIaddress\fR ...]" 4 .IX Item "sendchainpings address:address [address:address ...]" Send a command to immediatly send chain pings to the given chains. A chain is two remailer addresses seperated by a colon. .IP "\fBbuildstats\fR" 4 .IX Item "buildstats" Send a command to immediatly rebuild stats. .IP "\fBbuildkeys\fR" 4 .IX Item "buildkeys" Send a command to immediatly rebuild the keyrings. .IP "\fBbuildthesaurus\fR" 4 .IX Item "buildthesaurus" Send a command to immediatly rebuild the Thesaurus. .IP "\fBbuildfromlines\fR" 4 .IX Item "buildfromlines" Send a command to immediatly rebuild the From Header lines page. .IP "\fBsummary\fR" 4 .IX Item "summary" Print a status summary of all known addresses to the log (level notice). .IP "\fBdumpconf\fR" 4 .IX Item "dumpconf" Dumps the current configuration to standard output. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-\-basedir\fR" 4 .IX Item "--basedir" The home directory to which everything else is relative. See the \s-1BASE DIRECTORY\s0 section below. .IP "\fB\-\-verbose\fR" 4 .IX Item "--verbose" Verbose mode. Causes \fBpingd\fR to print debugging messages about its progress. .IP "\fB\-\-quiet\fR" 4 .IX Item "--quiet" Quiet mode. Be even quieter than normal. .IP "\fB\-\-help\fR" 4 .IX Item "--help" Print a short help message and exit sucessfully. .IP "\fB\-\-version\fR" 4 .IX Item "--version" Print version number and exit sucessfully. .IP "\fB\-\-nohup\fR" 4 .IX Item "--nohup" Usefull only with the \fBadd\fR, \fBset\fR, \fBsetremailercaps\fR, \&\fBdeleteremailercaps\fR, \fBgetkeyconf\fR, \fBsendpings\fR, \fBsendchainpings\fR, \&\fBbuildstats\fR, \fBbuildkeys\fR, \fBbuildthesaurus\fR, \fBbuildfromlines\fR, or \fBsummary\fR command. .Sp Don't send a \s-1HUP\s0 signal to the daemon which instructs it to process the commands after adding the command to the task list. .Sp By default such a signal is sent. .IP "\fB\-\-process\fR" 4 .IX Item "--process" Usefull only with the \fBstart\fR command. .Sp Read and process the commands file on startup. .IP "\fB\-\-detach\fR" 4 .IX Item "--detach" Usefull only with the \fBstart\fR command. .Sp Tell \fBpingd\fR to detach. .SH "BASE DIRECTORY" .IX Header "BASE DIRECTORY" The home directory to which everything else is relative. .PP Basedir defaults to whatever directory the \fBpingd\fR binary is located. It can be overridden by the \fB\s-1ECHOLOT_HOME\s0\fR environment variable which in turn is weaker than the \fB\-\-basedir\fR setting. .PP This directory is then used to locate the configuration file \fBpingd.conf\fR (see \&\s-1FILES\s0 below). .PP The \fBhomedir\fR setting in \fBpingd.conf\fR finally sets the base directory. .SH "FILES" .IX Header "FILES" The configuration file is searched in these places in this order: .IP "the file pointed to by the \fB\s-1ECHOLOT_CONF\s0\fR environment variable" 4 .IX Item "the file pointed to by the ECHOLOT_CONF environment variable" .PD 0 .IP "/pingd.conf" 4 .IX Item "/pingd.conf" .ie n .IP "$HOME/echolot/pingd.conf" 4 .el .IP "\f(CW$HOME\fR/echolot/pingd.conf" 4 .IX Item "$HOME/echolot/pingd.conf" .ie n .IP "$HOME/pingd.conf" 4 .el .IP "\f(CW$HOME\fR/pingd.conf" 4 .IX Item "$HOME/pingd.conf" .ie n .IP "$HOME/.pingd.conf" 4 .el .IP "\f(CW$HOME\fR/.pingd.conf" 4 .IX Item "$HOME/.pingd.conf" .IP "/etc/echolot/pingd.conf" 4 .IX Item "/etc/echolot/pingd.conf" .IP "/etc/pingd.conf" 4 .IX Item "/etc/pingd.conf" .PD .SH "ENVIRONMENT" .IX Header "ENVIRONMENT" .IP "\s-1ECHOLOT_CONF\s0 echolot config file (see section \s-1FILES\s0)" 4 .IX Item "ECHOLOT_CONF echolot config file (see section FILES)" .PD 0 .IP "\s-1ECHOLOT_HOME\s0 echolot base directory (see section \s-1BASE DIRECTORY\s0)" 4 .IX Item "ECHOLOT_HOME echolot base directory (see section BASE DIRECTORY)" .PD .SH "SIGNALS" .IX Header "SIGNALS" On \fB\s-1SIGINT\s0\fR, \fB\s-1SIGQUIT\s0\fR, and \fB\s-1SIGTERM\s0\fR \fBpingd\fR will schedule a shutdown for as soon as the current actions are finished or immediatly if no actions are currently being processed. It will then write all metadata and pingdata to disk and close all files cleanly before exiting. .PP On \fB\s-1SIGHUP\s0\fR will execute any pending commands from the commands file (\fBcommands.txt\fR by default). It also closes and reopens the file 'output' which is used for stdout and stderr when the daemon is running detached. This can be used if you want to rotate that file. .SH "AUTHOR" .IX Header "AUTHOR" Peter Palfrader .SH "BUGS" .IX Header "BUGS" Please report them at echolot-2.1.9/doc/methodology0000644000175000017500000000400512422221767015530 0ustar weaselweasel In order to test remailer A's reliability, Echolot sends an encrypted ping through a 1-hop chain every two hours. Pings are not sent strictly at 02:00, 04:00 etc, but at 2h*n + f(A, date) % 2h. ( f() is a function of the remailer name and the date of the current day (basically md5)) We record the timestamp each outgoing ping, and we record the time it took to return for incoming pings. The reliability of a node is the result of received/sent, with the following weighting applied: weight := w1 * w2; w1 is a function of a ping's age: age: 1 2 3 4 5 6 7 8 9 10 11 12 [days] weight 0.5 1.0 1.0 1.0 1.0 0.9 0.8 0.5 0.3 0.2 0.2 0.1 age is how long ago the ping was sent. So if a ping was sent 23 hour ago, it weighs 0.5, if it was sent 2 days ago, its weight is 1. Approaching 12 days, the weight approaches 0.0. w2 also considers this node's pings' latencies over the last 12 days: for pings that already returned, w2 is 1.0. otherwise: Let mod_age := (now - sent - 15m) * 0.8 w2 is the fraction of pings returned within mod_age seconds. Example: Assume a ping was sent 2 hours ago. mod_age is 84 minutes. If 100% of this node's pings were faster than 84 minutes, then w2 = 1. If only 30% were received within 84 minutes of sending out the ping, then w2 is 0.3, If no ping was ever faster than 84 minutes, then w2 is 0. The reported latency is the median of all received pings of the last 12 days. Chain pings are done in a similar fashion: We ping two-hop chains A, B. Each chain is pinged once a week (with a similar offset function as above). "Interesting chains" are pinged more often - daily. We report chains as broken if - we sent at least 3 pings AND - received/sent <= rel(A) * rel(B) * 0.3. rel(X) is remailer X's reliability in single-hop pings. We define interesting chains as chains - where we sent less than 3 pings, without getting any back. OR - where received/sent <= rel(A) * rel(B) * 0.3 (i.e. the chain is reported broken) echolot-2.1.9/doc/pingd.conf.pod0000644000175000017500000005731112422221767016014 0ustar weaselweasel# # # This file is part of Echolot - a Pinger for anonymous remailers. # # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2012, 2014 Peter Palfrader # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # =pod =head1 NAME pingd.conf - configuration file for the Echolot ping daemon =head1 DESCRIPTION The file B sets configuration parameters for Echolot pingd(1). It is a Perl script that gets eval()ed from within pingd. It has to set the values in the $CONFIG hash. =cut =head1 OPTIONS =head2 REQUIRED OPTIONS =over =item B A short name for your site/pinger. It is used in the statistics produced. Default: none Example: 'sitename' => 'testsite', =item B The local part of the pinger's email address. In C the localpart is C. Default: none Example: 'my_localpart' => 'pinger', =item B The domain part (FQDN) of the pinger's email address. In C the domain part is C. Default: none Example: 'my_domain' => 'remailer.example.com', =item B The email address of the human operator that runs this pinger. Default: none Example: 'operator_address' => 'remop@example.org', It is used in several templates. =back =head2 SYSTEM SPECIFIC OPTIONS =over =item B The B parameter specifies the separator between user names and address extensions (user+foo). If it is an empty string Echolot does not make use of user defined mailboxes but rather encodes the message type etc in a Comment/Realname part of an address. The use of recipient_delimiter is strongly recommended if your MTA setup supports it. Default: 'recipient_delimiter' => '+', Example: 'recipient_delimiter' => '-', 'recipient_delimiter' => '', Example addresses: with + as a recipient delimiter: pinger+conf.1=1035540778=1dd23d97@example.org without recipient delimiter: pinger@example.org (conf.2=1035541597=3baa2ae5) =item B Where to read strong random data from - currently used only for generating our secret. Default: 'dev_random' => '/dev/random', =item B Where to read weak random data from - currently used only for garbage generation. Default: 'dev_urandom' => '/dev/urandom', =item B Path to the sendmail binary. It is expected to accept the C<-f> and C<-t> parameters. Default: 'sendmail' => '/usr/sbin/sendmail', Example: 'sendmail' => '/usr/lib/sendmail', =back =head2 MAGIC NUMBERS =over =item B [integer] Echolot uses email addresses of the form C. MAC is a Message Authentication Code used to verify that the address was actually generated by this pinger using a secret which is set from random data the first time you run B. Echolot uses MD5 as the MAC hash function. B is the number of characters to include in the email address. Default: 'hash_len' => 8, Example: 'hash_len' => 4, =item B [integer] The length of one character in reliability and latency stats. One character usually stands for exactly one day (hence the name of this config option). Changing it in production use is probably a bad idea but shortening it might come in handy during debugging. Default: 'seconds_per_day' => 24*60*60, =item B [integer] How many days (or whatever you configured seconds_per_day to really be) to have in the stats. This is 12 days. Default: 'stats_days' => 12, =back =head2 NEW REMAILERS =over =item B [bool] Query new remailers for remailer-xxx replies by default. Default: 'fetch_new' => 1, Example: 'fetch_new' => 0, =item B [bool] Ping new remailers by default. Default: 'ping_new' => 1, Example: 'ping_new' => 0, =item B [bool] Show new remailers in public stats by default. Default: 'show_new' => 1, Example: 'show_new' => 0, =back =head2 STATISTICS GENERATION =over =item B [bool] Also build separate rlists with data from only DSA pings, only RSA pings and only unencrypted pings. Default: 'separate_rlists' => 0, Example: 'separate_rlists' => 1, =item B [bool] Build a combined list of all different stats too. While there is no standard format it is nice to read for the human eye. Default: 'combined_list' => 0, Example: 'combined_list' => 1, =item B [bool] Collect Thesaurus data and build Thesaurus Index. Default: 'thesaurus' => 1, Example: 'thesaurus' => 0, =item B [bool] Build a summary of default From: header lines and list remailers which allow overriding them. Default: 'fromlines' => 1, Example: 'fromlines' => 0, =item B In the statistics output remailers are sorted by reliability as the primary key. The secondary key is usually nickname. If you prefer to sort by latency rather than nick set this to 1 (-1 if you want to reverse the order). Default: 'stats_sort_by_latency' => 0, Example: 'stats_sort_by_latency' => 1, =back =head2 TIMERS AND COUNTERS =over =item B [seconds] How often to process incoming email. Default: 'processmail' => 60, # every minute Example: 'processmail' => 5*60, # every 5 minutes =item B [seconds] How often to build mlist etc. Default: 'buildstats' => 5*60, # every 5 minutes Example: 'buildstats' => 60*60, # hourly =item B [seconds] When building stats and we have chain pinging enabled (see B), how often to rebuild chain stats. This can be a CPU intensive task therefore it's not updated every time stats are built. Default: 'chainping_update' => 4*60*60, # chain stats should never # be older than 4 hours =item B [seconds] How often to build keyrings. Default: 'buildkeys' => 8*60*60, # every 8 hours Example: 'buildkeys' => 24*60*60, # daily =item B [seconds] How often to update thesaurus index page. Default: 'buildthesaurus' => 60*60, # hourly Example: 'buildthesaurus' => 24*60*60, # daily =item B [seconds] How often to check for prospective new remailer addresses and commit them to the list of remailers. Default: 'commitprospectives' => 8*60*60, # every 8 hours Example: 'commitprospectives' => 24*60*60, # daily =item B [seconds] How often to expire old keys, pings and remailers Default: 'expire' => 24*60*60, # daily Example: 'expire' => 8*60*60, # every 8 hours =item B [seconds] =item B [integer] How often to query remailers for new keys and configuration data (remailer-xxx). Some requests are sent every B seconds. The same request to the same remailer is sent only every B time. Default: 'getkeyconf_interval' => 5*60, # send out requests # every 5 minutes 'getkeyconf_every_nth_time' => 24*60/5, # send out the same # request to the same # remailer once a day Example: 'getkeyconf_interval' => 10*60, 'getkeyconf_every_nth_time' => 2*24*60/10, # new request every # other day =item B [seconds] How often to check assumed dead remailers for resurrection. Default: 'check_resurrection' => 7*24*60*60, # weekly Example: 'check_resurrection' => 14*24*60*60, # every other week =item B [seconds] =item B [integer] How often to send pings. Pings are sent every B seconds. The same remailer is pinged every B time pings are sent (This means the same remailer is pinged every B * B seconds). It is done this way in order to avoid spikes. Default: 'pinger_interval' => 5*60, # send out pings every 5 minutes 'ping_every_nth_time' => 24, # send out pings to the same remailer every 24 calls, i.e. every 2 hours Example: 'pinger_interval' => 60, # send out pings every minute 'ping_every_nth_time' => 60, # send out pings to the same remailer every 60 calls, i.e. every hour =item B [seconds] =item B [integer] =item B [integer] How often to send chain pings. Chain-Pings are sent every B seconds. The same chain is pinged every B time chain-pings are sent. Chains in I (ic), that are chains that are either known or believed to be bad or are not tested enough yet (see B), should be tested more often: They are checked every B time chain-pings are sent. Default: 'chainpinger_interval' => 5*60, # send out pings every 5 minutes 'chainping_every_nth_time' => 2016, # send out pings to the same chain every 2016 calls, i.e. week 'chainping_ic_every_nth_time' => 288, # send out pings to broken or unknown chains every 288 calls, i.e. daily =item B [integer] How many times to request remailer-xxx from a remailer (done every B seconds, daily per default) without a reply before it is assumed dead. Default: 'addresses_default_ttl' => 5, # getkeyconf seconds (days if getkeyconf is 24*60*60, the default) Example: 'addresses_default_ttl' => 7, =item B [integer] How many times to request remailer-xxx from an assumed dead remailer (done every B seconds, weekly per default) without a reply before it is really considered dead. Default: 'check_resurrection_ttl' => 8, # check_resurrection seconds (weeks if check_resurrection is 7*24*60*60, the default) Example: 'check_resurrection_ttl' => 4, =item B [seconds] How long to keep information about a prospective address in the database. Addresses that are not committed to the list of remailer addresses are expired after this time. Default: 'prospective_addresses_ttl' => 5*24*60*60, # 5 days Example: 'prospective_addresses_ttl' =>14*24*60*60, # 2 weeks =item B [integer] How many different remailers need to list an address in a remailer-conf reply to get it committed to the list of remailer addresses. Default: 'reliable_auto_add_min' => 6, Example: 'reliable_auto_add_min' => 3, =item B [seconds] After how long to expire received keys if they were not updated by remailer-key replies. Default: 'expire_keys' => 5*24*60*60, # 5 days Example: 'expire_keys' => 7*24*60*60, # 1 week =item B [seconds] After how long to expire received remailer-conf replies. Default: 'expire_confs' => 5*24*60*60, # 5 days Example: 'expire_confs' => 7*24*60*60, # 1 week =item B [seconds] After how long to expire pings. 12 is the value of choice because that is the time frame the statistics show. You should not make this smaller than 12 days. Default: 'expire_pings' => 12*24*60*60, # 12 days =item B [seconds] After how long to expire chain pings. This should probably be set to the same as B. Default: 'expire_chainpings' => 12*24*60*60, # 12 days =item B [seconds] After how long to expire files in the thesaurus directory. Default: 'expire_thesaurus' => 21*24*60*60, # 2 weeks Example: 'expire_thesaurus' => 7*24*60*60, # 1 week =item B [seconds] After how long to expire header From: lines. Default: 'expire_fromlines' => 5*24*60*60, # 5 days Example: 'expire_fromlines' => 7*24*60*60, # 1 week =item B [seconds] How often to clean old files from the temp directory. Default: 'cleanup_tmpdir' => 24*60*60, # daily =item B [seconds] How often to make backups of metadata and rotate them. If gzip is set, backups are compressed. Default: 'metadata_backup' => 8*60*60, # 8 hours Example: 'metadata_backup' => 24*60*60, # daily =item B [integer] How many backups of metadata to keep. Default: 'metadata_backup_count' => 32, # keep the last 32 backups Example: 'metadata_backup_count' => 4, # keep 4 rotations =item B [seconds] How often to print a status summary to the log. Default: 'summary' => 24*60*60, # daily Default: 'summary' => 12*60*60, # twice a day =back =head2 DIRECTORIES AND FILES AND RELATED OPTIONS =over =item B The base directory of the Echolot installation. All other filenames and directory names are local to this directory. B changes into this directory upon startup. Default: The directory in which pingd is. Example: 'homedir' => '/home/pinger/echolot', =item B The Maildir directory or Mbox which is searched for new messages. Default: 'mailin' => 'mail', Example: 'mailin' => '/var/mail/echolot', =item B The Maildir directory where messages are put that could not be parsed. Default: 'mailerrordir' => 'mail-errors', =item B [bool] Whether to keep error messages at all Default: 'save_errormails' => 0, Example: 'save_errormails' => 1, =item B The directory where statistics and keyrings are put. Default: 'resultdir' => 'results', =item B The directory where Thesaurus data is put. Default: 'thesaurusdir' => 'results/thesaurus', =item B The Thesaurus index file. Default: 'thesaurusindexfile' => 'results/thesaurus/index', =item B The From Lines index file. Default: 'fromlinesindexfile' => 'results/from', =item B The directory where private stats and keyrings are put (Remailers that have show set to false are shown here too). Default: 'private_resultdir' => 'results.private', =item B The file to write the index.html to (relative to the result directory). Default: 'indexfilebasename' => 'echolot', Example: 'indexfilebasename' => 'index', =item B The directory which is used as temporary GnuPG home for all keyring and encryption/decryption actions. Default: 'gnupghome' => 'gnupghome', =item B Name of the GnuPG executable. If it is not in your PATH make sure to include path information. If B is an empty string, the C default (usually B) is used. Default: 'gnupg' => '', Example: 'gnupg' => '/home/pinger/bin/myGnuPG', =item B Name of the gzip executable. If it is not in your PATH make sure to include path information. Default: 'gzip' => 'gzip', =item B The directory which is used as temporary Mixmaster home for all keyring and encryption/decryption actions. Default: 'mixhome' => 'mixhome', Example: 'mixhome' => '/home/pinger/Mix', =item B Name of the mixmaster executable. If it is not in your PATH make sure to include path information. Default: 'mixmaster' => 'mix', Example: 'mixmaster' => '/home/pinger/Mix/mix', =item B General purpose temp directory. Make sure it is not shared with other applications. Default: 'tmpdir' => 'tmp', =item B A file where commands to the daemon process are stored. The client puts commands (like add a new remailer) in it and then sends a HUP to the daemon process which reads and empties the file. Default: 'commands_file' => 'commands.txt', =item B The daemon's PID file. The daemon's Process ID is stored in this file. As long as it exists pingd refuses to start up in daemon mode. Default: 'pidfile' => 'pingd.pid', =item B File listing broken type I remailer chains. If it does not exist, the part is skipped in generated stats. Otherwise its content is copied in verbatim. Default: 'broken1' => 'broken1.txt', Example content: (havenco cmeclax) (frog3 nycrem) =item B File listing broken type II remailer chains. If it does not exist, the part is skipped in generated stats. Otherwise its content is copied in verbatim. Default: 'broken2' => 'broken2.txt', Example content: (freedom lcs) (* xganon) =item B File listing remailers that have the same operator or share a machine or other important infrastructure. If it does not exist, the part is skipped in generated stats. Otherwise its content is copied in verbatim. Default: 'sameop' => 'sameop.txt', Example content: (xganon2 xganon) (cracker redneck) =back =head2 LOGGING =over =item B File to write logs to. This file is reopened on SIGHUP. Default: 'logfile' => 'pingd.log', Example: 'logfile' => '/var/log/echolot/pingd.log', =item B Minimum severity of messages to include in log file. Possible values are B, B, B, B, B, B, B, B, and B. Default: 'loglevel' => 'info', Example: 'loglevel' => 'debug', =back =head2 MISCELLANEOUS =over =item B [bool] Whether to write meta files for each created file. These files include meta information for http servers and http clients like the date when a specific page expires. Default: 'write_meta_files' => 1, =item B The extension that such metafiles (see above) should have. Default: 'meta_extension' => '.meta', =item B [integer] Pings usually are quite short. Some 100 bytes are sufficient to relay all the information that is required. To make them not stand out that obviously, pings are padded using random garbage of random length. B is the top limit for the amount of bytes to add. The actual number is randomly generated and uniformly distributed over [0, B] Default: 'random_garbage' => '8192', =back =head2 CHAIN PINGING =over =item B [bool] Whether or not to do chain pings. Chain pings test all chains of two remailers and come up with a list of broken chains. This produces a non-trivial amount of traffic. Default: 'do_chainpings' => 1, =item B [bool] Show the results of our chainpinging in public stats. Default: 'show_chainpings' => 1, =item B What proportion of the expected replies derived from one-hop stats must return before a chain is not declared broken. Default: 'chainping_fudge' => 0.3, # if less than 0.3 * rel1 * rel2 make it, the chain is really broken =item B The factor of time in addition to the guessed latency derived from one-hop stats before a chain ping is considered lost Default: 'chainping_grace' => 1.5, # don't count pings sent no longer than 1.5 * (lat1 + lat2) ago =item B [seconds] What time frame is taken into account when calculating chain stats. This should probably be smaller than B. Default: 'chainping_period' => 12*24*60*60, # 12 days =item B [seconds] Have at least as many sent (and not within grace) chain pings before declaring a chain broken. Default: 'chainping_minsample' => 3, # have at least sent 3 pings before judging any chain =item B How many chains C<(A x)> must be bad before C<(A *)> is listed. The value is given as a proportion of all available remailers. Default: chainping_allbad_factor => 0.5, # at least 50% of possible chains (A x) need to fail for (A *) to be listed in broken chains =back =head2 PINGING TYPES =over =item B B determines which ping types are sent. It is a hash that has the following keys: =over =item B Send out CPunk pings to CPunk remailers with their DSA key. =item B Send out CPunk pings to CPunk remailers with their RSA key. =item B Send out unencrypted pings to CPunk remailers that don't have pgponly in their capsstring. =item B Pings mixmaster remailers. =back Default: 'do_pings' => { 'cpunk-dsa' => 1, 'cpunk-rsa' => 1, 'cpunk-clear' => 1, 'mix' => 1 }, =item B B controls some aspects of chain pinging. It's a hash over chaintypes - currently B and B. Each entry is a reference to an array which specifies the preference for key types in that chaintype. Default: which_chainpings => { 'cpunk' => [ qw{cpunk-dsa cpunk-rsa cpunk-clear} ], 'mix' => [ qw{mix} ] }, This means that in the case of cpunk chain pings we prefer using cpunk-dsa over cpunk-rsa which in turn we prefer to cpunk-clear. For mix there's only mix. =item B Not all pings have the same influence on the average reliability calcluated. Very new pings don't count fully since there is some margin of error. Similarly very old pings are not that interesting either. By default days 1 to 4 count fully (with weight 1), the older they are the less they count. Default: pings_weight => [ qw{0.5 1.0 1.0 1.0 1.0 0.9 0.8 0.5 0.3 0.2 0.2 0.1 } ], =back =head2 TEMPLATES =over =item B The template files are used to generate the HTML version of all Echolot output. It is a hash of hashes which each have following keys: B, B, B, B, B, B, B, B, B, B, B, and B. The outer hash keys are for language selection. Default: 'templates' => { 'default' => { 'thesaurusindexfile' => 'templates/thesaurusindex.html', 'mlist' => 'templates/mlist.html', 'mlist2' => 'templates/mlist2.html', 'rlist' => 'templates/rlist.html', 'rlist-rsa' => 'templates/rlist-rsa.html', 'rlist-dsa' => 'templates/rlist-dsa.html', 'rlist-clear' => 'templates/rlist-clear.html', 'rlist2' => 'templates/rlist2.html', 'rlist2-rsa' => 'templates/rlist2-rsa.html', 'rlist2-dsa' => 'templates/rlist2-dsa.html', 'rlist2-clear' => 'templates/rlist2-clear.html', 'clist' => 'templates/clist.html', }, 'de' => { 'thesaurusindexfile' => 'templates/thesaurusindex.de.html', .... }, 'pl' => { 'thesaurusindexfile' => 'templates/thesaurusindex.pl.html', .... } }; =item B Location of the CSS file. This is copied to resultdir/echolot.css. Default: 'echolot_css' => 'templates/echolot.css', =back =head2 STRINGS =over =item B The text to send along with remailer-xxx queries. The template variables address and operator_address are substituted for their real values. Default: 'remailerxxxtext' => "Hello,\n". "\n". "This message requests remailer configuration data. The pinging software thinks\n". " is a remailer. Either it has been told so by the\n". "maintainer of the pinger or it found the address in a remailer-conf or\n". "remailer-key reply of some other remailer.\n". "\n". "If this is _not_ a remailer, you can tell this pinger that and it will stop\n". "sending you those requests immediately (otherwise it will try a few more times).\n". "Just reply and make sure the following is the first line of your message:\n". " not a remailer\n". "\n". "If you want to talk to a human please mail .\n", =back =head1 AUTHOR Peter Palfrader Epeter@palfrader.orgE =head1 BUGS Please report them at EURL:http://alioth.debian.org/projects/echolot/E =cut echolot-2.1.9/doc/pingd.conf.50000644000175000017500000010640612422222123015361 0ustar weaselweasel.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "PINGD.CONF 5" .TH PINGD.CONF 5 "2014-10-23" "2.1.9" "Echolot" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" pingd.conf \- configuration file for the Echolot ping daemon .SH "DESCRIPTION" .IX Header "DESCRIPTION" The file \fBpingd.conf\fR sets configuration parameters for Echolot \fIpingd\fR\|(1). It is a Perl script that gets \fIeval()\fRed from within pingd. It has to set the values in the \f(CW$CONFIG\fR hash. .SH "OPTIONS" .IX Header "OPTIONS" .SS "\s-1REQUIRED OPTIONS\s0" .IX Subsection "REQUIRED OPTIONS" .IP "\fBsitename\fR" 4 .IX Item "sitename" A short name for your site/pinger. It is used in the statistics produced. .Sp .Vb 2 \& Default: none \& Example: \*(Aqsitename\*(Aq => \*(Aqtestsite\*(Aq, .Ve .IP "\fBmy_localpart\fR" 4 .IX Item "my_localpart" The local part of the pinger's email address. .Sp In \f(CW\*(C`pinger@remailer.example.com\*(C'\fR the localpart is \f(CW\*(C`pinger\*(C'\fR. .Sp .Vb 2 \& Default: none \& Example: \*(Aqmy_localpart\*(Aq => \*(Aqpinger\*(Aq, .Ve .IP "\fBmy_domain\fR" 4 .IX Item "my_domain" The domain part (\s-1FQDN\s0) of the pinger's email address. .Sp In \f(CW\*(C`pinger@remailer.example.com\*(C'\fR the domain part is \f(CW\*(C`remailer.example.com\*(C'\fR. .Sp .Vb 2 \& Default: none \& Example: \*(Aqmy_domain\*(Aq => \*(Aqremailer.example.com\*(Aq, .Ve .IP "\fBoperator_address\fR" 4 .IX Item "operator_address" The email address of the human operator that runs this pinger. .Sp .Vb 2 \& Default: none \& Example: \*(Aqoperator_address\*(Aq => \*(Aqremop@example.org\*(Aq, .Ve .Sp It is used in several templates. .SS "\s-1SYSTEM SPECIFIC OPTIONS\s0" .IX Subsection "SYSTEM SPECIFIC OPTIONS" .IP "\fBrecipient_delimiter\fR" 4 .IX Item "recipient_delimiter" The \fBrecipient_delimiter\fR parameter specifies the separator between user names and address extensions (user+foo). .Sp If it is an empty string Echolot does not make use of user defined mailboxes but rather encodes the message type etc in a Comment/Realname part of an address. .Sp The use of recipient_delimiter is strongly recommended if your \s-1MTA\s0 setup supports it. .Sp .Vb 3 \& Default: \*(Aqrecipient_delimiter\*(Aq => \*(Aq+\*(Aq, \& Example: \*(Aqrecipient_delimiter\*(Aq => \*(Aq\-\*(Aq, \& \*(Aqrecipient_delimiter\*(Aq => \*(Aq\*(Aq, .Ve .Sp Example addresses: .Sp with + as a recipient delimiter: pinger+conf.1=1035540778=1dd23d97@example.org .Sp without recipient delimiter: pinger@example.org (conf.2=1035541597=3baa2ae5) .IP "\fBdev_random\fR" 4 .IX Item "dev_random" Where to read strong random data from \- currently used only for generating our secret. .Sp .Vb 1 \& Default: \*(Aqdev_random\*(Aq => \*(Aq/dev/random\*(Aq, .Ve .IP "\fBdev_urandom\fR" 4 .IX Item "dev_urandom" Where to read weak random data from \- currently used only for garbage generation. .Sp .Vb 1 \& Default: \*(Aqdev_urandom\*(Aq => \*(Aq/dev/urandom\*(Aq, .Ve .IP "\fBsendmail\fR" 4 .IX Item "sendmail" Path to the sendmail binary. It is expected to accept the \f(CW\*(C`\-f\*(C'\fR and \f(CW\*(C`\-t\*(C'\fR parameters. .Sp .Vb 2 \& Default: \*(Aqsendmail\*(Aq => \*(Aq/usr/sbin/sendmail\*(Aq, \& Example: \*(Aqsendmail\*(Aq => \*(Aq/usr/lib/sendmail\*(Aq, .Ve .SS "\s-1MAGIC NUMBERS\s0" .IX Subsection "MAGIC NUMBERS" .IP "\fBhash_len\fR [integer]" 4 .IX Item "hash_len [integer]" Echolot uses email addresses of the form \f(CW\*(C`foo+some_data=MAC@domain\*(C'\fR. \s-1MAC\s0 is a Message Authentication Code used to verify that the address was actually generated by this pinger using a secret which is set from random data the first time you run \fBpingd\fR. Echolot uses \s-1MD5\s0 as the \s-1MAC\s0 hash function. .Sp \&\fBhash_len\fR is the number of characters to include in the email address. .Sp .Vb 2 \& Default: \*(Aqhash_len\*(Aq => 8, \& Example: \*(Aqhash_len\*(Aq => 4, .Ve .IP "\fBseconds_per_day\fR [integer]" 4 .IX Item "seconds_per_day [integer]" The length of one character in reliability and latency stats. One character usually stands for exactly one day (hence the name of this config option). Changing it in production use is probably a bad idea but shortening it might come in handy during debugging. .Sp .Vb 1 \& Default: \*(Aqseconds_per_day\*(Aq => 24*60*60, .Ve .IP "\fBstats_days\fR [integer]" 4 .IX Item "stats_days [integer]" How many days (or whatever you configured seconds_per_day to really be) to have in the stats. This is 12 days. .Sp .Vb 1 \& Default: \*(Aqstats_days\*(Aq => 12, .Ve .SS "\s-1NEW REMAILERS\s0" .IX Subsection "NEW REMAILERS" .IP "\fBfetch_new\fR [bool]" 4 .IX Item "fetch_new [bool]" Query new remailers for remailer-xxx replies by default. .Sp .Vb 2 \& Default: \*(Aqfetch_new\*(Aq => 1, \& Example: \*(Aqfetch_new\*(Aq => 0, .Ve .IP "\fBping_new\fR [bool]" 4 .IX Item "ping_new [bool]" Ping new remailers by default. .Sp .Vb 2 \& Default: \*(Aqping_new\*(Aq => 1, \& Example: \*(Aqping_new\*(Aq => 0, .Ve .IP "\fBshow_new\fR [bool]" 4 .IX Item "show_new [bool]" Show new remailers in public stats by default. .Sp .Vb 2 \& Default: \*(Aqshow_new\*(Aq => 1, \& Example: \*(Aqshow_new\*(Aq => 0, .Ve .SS "\s-1STATISTICS GENERATION\s0" .IX Subsection "STATISTICS GENERATION" .IP "\fBseparate_rlists\fR [bool]" 4 .IX Item "separate_rlists [bool]" Also build separate rlists with data from only \s-1DSA\s0 pings, only \s-1RSA\s0 pings and only unencrypted pings. .Sp .Vb 2 \& Default: \*(Aqseparate_rlists\*(Aq => 0, \& Example: \*(Aqseparate_rlists\*(Aq => 1, .Ve .IP "\fBcombined_list\fR [bool]" 4 .IX Item "combined_list [bool]" Build a combined list of all different stats too. While there is no standard format it is nice to read for the human eye. .Sp .Vb 2 \& Default: \*(Aqcombined_list\*(Aq => 0, \& Example: \*(Aqcombined_list\*(Aq => 1, .Ve .IP "\fBthesaurus\fR [bool]" 4 .IX Item "thesaurus [bool]" Collect Thesaurus data and build Thesaurus Index. .Sp .Vb 2 \& Default: \*(Aqthesaurus\*(Aq => 1, \& Example: \*(Aqthesaurus\*(Aq => 0, .Ve .IP "\fBfromlines\fR [bool]" 4 .IX Item "fromlines [bool]" Build a summary of default From: header lines and list remailers which allow overriding them. .Sp .Vb 2 \& Default: \*(Aqfromlines\*(Aq => 1, \& Example: \*(Aqfromlines\*(Aq => 0, .Ve .IP "\fBstats_sort_by_latency\fR" 4 .IX Item "stats_sort_by_latency" In the statistics output remailers are sorted by reliability as the primary key. The secondary key is usually nickname. If you prefer to sort by latency rather than nick set this to 1 (\-1 if you want to reverse the order). .Sp .Vb 2 \& Default: \*(Aqstats_sort_by_latency\*(Aq => 0, \& Example: \*(Aqstats_sort_by_latency\*(Aq => 1, .Ve .SS "\s-1TIMERS AND COUNTERS\s0" .IX Subsection "TIMERS AND COUNTERS" .IP "\fBprocessmail\fR [seconds]" 4 .IX Item "processmail [seconds]" How often to process incoming email. .Sp .Vb 2 \& Default: \*(Aqprocessmail\*(Aq => 60, # every minute \& Example: \*(Aqprocessmail\*(Aq => 5*60, # every 5 minutes .Ve .IP "\fBbuildstats\fR [seconds]" 4 .IX Item "buildstats [seconds]" How often to build mlist etc. .Sp .Vb 2 \& Default: \*(Aqbuildstats\*(Aq => 5*60, # every 5 minutes \& Example: \*(Aqbuildstats\*(Aq => 60*60, # hourly .Ve .IP "\fBchainping_update\fR [seconds]" 4 .IX Item "chainping_update [seconds]" When building stats and we have chain pinging enabled (see \fBdo_chainpings\fR), how often to rebuild chain stats. This can be a \s-1CPU\s0 intensive task therefore it's not updated every time stats are built. .Sp .Vb 2 \& Default: \*(Aqchainping_update\*(Aq => 4*60*60, # chain stats should never \& # be older than 4 hours .Ve .IP "\fBbuildkeys\fR [seconds]" 4 .IX Item "buildkeys [seconds]" How often to build keyrings. .Sp .Vb 2 \& Default: \*(Aqbuildkeys\*(Aq => 8*60*60, # every 8 hours \& Example: \*(Aqbuildkeys\*(Aq => 24*60*60, # daily .Ve .IP "\fBbuildthesaurus\fR [seconds]" 4 .IX Item "buildthesaurus [seconds]" How often to update thesaurus index page. .Sp .Vb 2 \& Default: \*(Aqbuildthesaurus\*(Aq => 60*60, # hourly \& Example: \*(Aqbuildthesaurus\*(Aq => 24*60*60, # daily .Ve .IP "\fBcommitprospectives\fR [seconds]" 4 .IX Item "commitprospectives [seconds]" How often to check for prospective new remailer addresses and commit them to the list of remailers. .Sp .Vb 2 \& Default: \*(Aqcommitprospectives\*(Aq => 8*60*60, # every 8 hours \& Example: \*(Aqcommitprospectives\*(Aq => 24*60*60, # daily .Ve .IP "\fBexpire\fR [seconds]" 4 .IX Item "expire [seconds]" How often to expire old keys, pings and remailers .Sp .Vb 2 \& Default: \*(Aqexpire\*(Aq => 24*60*60, # daily \& Example: \*(Aqexpire\*(Aq => 8*60*60, # every 8 hours .Ve .IP "\fBgetkeyconf_interval\fR [seconds]" 4 .IX Item "getkeyconf_interval [seconds]" .PD 0 .IP "\fBgetkeyconf_every_nth_time\fR [integer]" 4 .IX Item "getkeyconf_every_nth_time [integer]" .PD How often to query remailers for new keys and configuration data (remailer-xxx). Some requests are sent every \fBgetkeyconf_interval\fR seconds. The same request to the same remailer is sent only every \&\fBgetkeyconf_every_nth_time\fR time. .Sp .Vb 8 \& Default: \*(Aqgetkeyconf_interval\*(Aq => 5*60, # send out requests \& # every 5 minutes \& \*(Aqgetkeyconf_every_nth_time\*(Aq => 24*60/5, # send out the same \& # request to the same \& # remailer once a day \& Example: \*(Aqgetkeyconf_interval\*(Aq => 10*60, \& \*(Aqgetkeyconf_every_nth_time\*(Aq => 2*24*60/10, # new request every \& # other day .Ve .IP "\fBcheck_resurrection\fR [seconds]" 4 .IX Item "check_resurrection [seconds]" How often to check assumed dead remailers for resurrection. .Sp .Vb 2 \& Default: \*(Aqcheck_resurrection\*(Aq => 7*24*60*60, # weekly \& Example: \*(Aqcheck_resurrection\*(Aq => 14*24*60*60, # every other week .Ve .IP "\fBpinger_interval\fR [seconds]" 4 .IX Item "pinger_interval [seconds]" .PD 0 .IP "\fBping_every_nth_time\fR [integer]" 4 .IX Item "ping_every_nth_time [integer]" .PD How often to send pings. Pings are sent every \fBpinger_interval\fR seconds. The same remailer is pinged every \fBping_every_nth_time\fR time pings are sent (This means the same remailer is pinged every \fBpinger_interval\fR * \&\fBping_every_nth_time\fR seconds). It is done this way in order to avoid spikes. .Sp .Vb 4 \& Default: \*(Aqpinger_interval\*(Aq => 5*60, # send out pings every 5 minutes \& \*(Aqping_every_nth_time\*(Aq => 24, # send out pings to the same remailer every 24 calls, i.e. every 2 hours \& Example: \*(Aqpinger_interval\*(Aq => 60, # send out pings every minute \& \*(Aqping_every_nth_time\*(Aq => 60, # send out pings to the same remailer every 60 calls, i.e. every hour .Ve .IP "\fBchainpinger_interval\fR [seconds]" 4 .IX Item "chainpinger_interval [seconds]" .PD 0 .IP "\fBchainping_every_nth_time\fR [integer]" 4 .IX Item "chainping_every_nth_time [integer]" .IP "\fBchainping_ic_every_nth_time\fR [integer]" 4 .IX Item "chainping_ic_every_nth_time [integer]" .PD How often to send chain pings. Chain-Pings are sent every \&\fBchainpinger_interval\fR seconds. The same chain is pinged every \&\fBchainping_every_nth_time\fR time chain-pings are sent. Chains in \&\fIIntensive Care\fR (ic), that are chains that are either known or believed to be bad or are not tested enough yet (see \&\fBchainping_minsample\fR), should be tested more often: They are checked every \fBchainping_ic_every_nth_time\fR time chain-pings are sent. .Sp .Vb 3 \& Default: \*(Aqchainpinger_interval\*(Aq => 5*60, # send out pings every 5 minutes \& \*(Aqchainping_every_nth_time\*(Aq => 2016, # send out pings to the same chain every 2016 calls, i.e. week \& \*(Aqchainping_ic_every_nth_time\*(Aq => 288, # send out pings to broken or unknown chains every 288 calls, i.e. daily .Ve .IP "\fBaddresses_default_ttl\fR [integer]" 4 .IX Item "addresses_default_ttl [integer]" How many times to request remailer-xxx from a remailer (done every \&\fBgetkeyconf\fR seconds, daily per default) without a reply before it is assumed dead. .Sp .Vb 2 \& Default: \*(Aqaddresses_default_ttl\*(Aq => 5, # getkeyconf seconds (days if getkeyconf is 24*60*60, the default) \& Example: \*(Aqaddresses_default_ttl\*(Aq => 7, .Ve .IP "\fBcheck_resurrection_ttl\fR [integer]" 4 .IX Item "check_resurrection_ttl [integer]" How many times to request remailer-xxx from an assumed dead remailer (done every \&\fBcheck_resurrection\fR seconds, weekly per default) without a reply before it is really considered dead. .Sp .Vb 2 \& Default: \*(Aqcheck_resurrection_ttl\*(Aq => 8, # check_resurrection seconds (weeks if check_resurrection is 7*24*60*60, the default) \& Example: \*(Aqcheck_resurrection_ttl\*(Aq => 4, .Ve .IP "\fBprospective_addresses_ttl\fR [seconds]" 4 .IX Item "prospective_addresses_ttl [seconds]" How long to keep information about a prospective address in the database. Addresses that are not committed to the list of remailer addresses are expired after this time. .Sp .Vb 2 \& Default: \*(Aqprospective_addresses_ttl\*(Aq => 5*24*60*60, # 5 days \& Example: \*(Aqprospective_addresses_ttl\*(Aq =>14*24*60*60, # 2 weeks .Ve .IP "\fBreliable_auto_add_min\fR [integer]" 4 .IX Item "reliable_auto_add_min [integer]" How many different remailers need to list an address in a remailer-conf reply to get it committed to the list of remailer addresses. .Sp .Vb 2 \& Default: \*(Aqreliable_auto_add_min\*(Aq => 6, \& Example: \*(Aqreliable_auto_add_min\*(Aq => 3, .Ve .IP "\fBexpire_keys\fR [seconds]" 4 .IX Item "expire_keys [seconds]" After how long to expire received keys if they were not updated by remailer-key replies. .Sp .Vb 2 \& Default: \*(Aqexpire_keys\*(Aq => 5*24*60*60, # 5 days \& Example: \*(Aqexpire_keys\*(Aq => 7*24*60*60, # 1 week .Ve .IP "\fBexpire_confs\fR [seconds]" 4 .IX Item "expire_confs [seconds]" After how long to expire received remailer-conf replies. .Sp .Vb 2 \& Default: \*(Aqexpire_confs\*(Aq => 5*24*60*60, # 5 days \& Example: \*(Aqexpire_confs\*(Aq => 7*24*60*60, # 1 week .Ve .IP "\fBexpire_pings\fR [seconds]" 4 .IX Item "expire_pings [seconds]" After how long to expire pings. 12 is the value of choice because that is the time frame the statistics show. You should not make this smaller than 12 days. .Sp .Vb 1 \& Default: \*(Aqexpire_pings\*(Aq => 12*24*60*60, # 12 days .Ve .IP "\fBexpire_chainpings\fR [seconds]" 4 .IX Item "expire_chainpings [seconds]" After how long to expire chain pings. This should probably be set to the same as \fBchainping_period\fR. .Sp .Vb 1 \& Default: \*(Aqexpire_chainpings\*(Aq => 12*24*60*60, # 12 days .Ve .IP "\fBexpire_thesaurus\fR [seconds]" 4 .IX Item "expire_thesaurus [seconds]" After how long to expire files in the thesaurus directory. .Sp .Vb 2 \& Default: \*(Aqexpire_thesaurus\*(Aq => 21*24*60*60, # 2 weeks \& Example: \*(Aqexpire_thesaurus\*(Aq => 7*24*60*60, # 1 week .Ve .IP "\fBexpire_fromlines\fR [seconds]" 4 .IX Item "expire_fromlines [seconds]" After how long to expire header From: lines. .Sp .Vb 2 \& Default: \*(Aqexpire_fromlines\*(Aq => 5*24*60*60, # 5 days \& Example: \*(Aqexpire_fromlines\*(Aq => 7*24*60*60, # 1 week .Ve .IP "\fBcleanup_tmpdir\fR [seconds]" 4 .IX Item "cleanup_tmpdir [seconds]" How often to clean old files from the temp directory. Default: 'cleanup_tmpdir' => 24*60*60, # daily .IP "\fBmetadata_backup\fR [seconds]" 4 .IX Item "metadata_backup [seconds]" How often to make backups of metadata and rotate them. If gzip is set, backups are compressed. .Sp .Vb 2 \& Default: \*(Aqmetadata_backup\*(Aq => 8*60*60, # 8 hours \& Example: \*(Aqmetadata_backup\*(Aq => 24*60*60, # daily .Ve .IP "\fBmetadata_backup_count\fR [integer]" 4 .IX Item "metadata_backup_count [integer]" How many backups of metadata to keep. .Sp .Vb 2 \& Default: \*(Aqmetadata_backup_count\*(Aq => 32, # keep the last 32 backups \& Example: \*(Aqmetadata_backup_count\*(Aq => 4, # keep 4 rotations .Ve .IP "\fBsummary\fR [seconds]" 4 .IX Item "summary [seconds]" How often to print a status summary to the log. .Sp .Vb 2 \& Default: \*(Aqsummary\*(Aq => 24*60*60, # daily \& Default: \*(Aqsummary\*(Aq => 12*60*60, # twice a day .Ve .SS "\s-1DIRECTORIES AND FILES AND RELATED OPTIONS\s0" .IX Subsection "DIRECTORIES AND FILES AND RELATED OPTIONS" .IP "\fBhomedir\fR" 4 .IX Item "homedir" The base directory of the Echolot installation. All other filenames and directory names are local to this directory. \fBpingd\fR changes into this directory upon startup. .Sp .Vb 2 \& Default: The directory in which pingd is. \& Example: \*(Aqhomedir\*(Aq => \*(Aq/home/pinger/echolot\*(Aq, .Ve .IP "\fBmailin\fR" 4 .IX Item "mailin" The Maildir directory or Mbox which is searched for new messages. .Sp .Vb 2 \& Default: \*(Aqmailin\*(Aq => \*(Aqmail\*(Aq, \& Example: \*(Aqmailin\*(Aq => \*(Aq/var/mail/echolot\*(Aq, .Ve .IP "\fBmailerrordir\fR" 4 .IX Item "mailerrordir" The Maildir directory where messages are put that could not be parsed. .Sp .Vb 1 \& Default: \*(Aqmailerrordir\*(Aq => \*(Aqmail\-errors\*(Aq, .Ve .IP "\fBsave_errormails\fR [bool]" 4 .IX Item "save_errormails [bool]" Whether to keep error messages at all .Sp .Vb 2 \& Default: \*(Aqsave_errormails\*(Aq => 0, \& Example: \*(Aqsave_errormails\*(Aq => 1, .Ve .IP "\fBresultdir\fR" 4 .IX Item "resultdir" The directory where statistics and keyrings are put. .Sp .Vb 1 \& Default: \*(Aqresultdir\*(Aq => \*(Aqresults\*(Aq, .Ve .IP "\fBthesaurusdir\fR" 4 .IX Item "thesaurusdir" The directory where Thesaurus data is put. .Sp .Vb 1 \& Default: \*(Aqthesaurusdir\*(Aq => \*(Aqresults/thesaurus\*(Aq, .Ve .IP "\fBthesaurusindexfile\fR" 4 .IX Item "thesaurusindexfile" The Thesaurus index file. .Sp .Vb 1 \& Default: \*(Aqthesaurusindexfile\*(Aq => \*(Aqresults/thesaurus/index\*(Aq, .Ve .IP "\fBfromlinesindexfile\fR" 4 .IX Item "fromlinesindexfile" The From Lines index file. .Sp .Vb 1 \& Default: \*(Aqfromlinesindexfile\*(Aq => \*(Aqresults/from\*(Aq, .Ve .IP "\fBprivate_resultdir\fR" 4 .IX Item "private_resultdir" The directory where private stats and keyrings are put (Remailers that have show set to false are shown here too). .Sp .Vb 1 \& Default: \*(Aqprivate_resultdir\*(Aq => \*(Aqresults.private\*(Aq, .Ve .IP "\fBindexfilebasename\fR" 4 .IX Item "indexfilebasename" The file to write the index.html to (relative to the result directory). .Sp .Vb 2 \& Default: \*(Aqindexfilebasename\*(Aq => \*(Aqecholot\*(Aq, \& Example: \*(Aqindexfilebasename\*(Aq => \*(Aqindex\*(Aq, .Ve .IP "\fBgnupghome\fR" 4 .IX Item "gnupghome" The directory which is used as temporary GnuPG home for all keyring and encryption/decryption actions. .Sp .Vb 1 \& Default: \*(Aqgnupghome\*(Aq => \*(Aqgnupghome\*(Aq, .Ve .IP "\fBgnupg\fR" 4 .IX Item "gnupg" Name of the GnuPG executable. If it is not in your \s-1PATH\s0 make sure to include path information. .Sp If \fBgnupg\fR is an empty string, the \f(CW\*(C`GnuPG::Interface\*(C'\fR default (usually \fBgpg\fR) is used. .Sp .Vb 2 \& Default: \*(Aqgnupg\*(Aq => \*(Aq\*(Aq, \& Example: \*(Aqgnupg\*(Aq => \*(Aq/home/pinger/bin/myGnuPG\*(Aq, .Ve .IP "\fBgzip\fR" 4 .IX Item "gzip" Name of the gzip executable. If it is not in your \s-1PATH\s0 make sure to include path information. .Sp .Vb 1 \& Default: \*(Aqgzip\*(Aq => \*(Aqgzip\*(Aq, .Ve .IP "\fBmixhome\fR" 4 .IX Item "mixhome" The directory which is used as temporary Mixmaster home for all keyring and encryption/decryption actions. .Sp .Vb 2 \& Default: \*(Aqmixhome\*(Aq => \*(Aqmixhome\*(Aq, \& Example: \*(Aqmixhome\*(Aq => \*(Aq/home/pinger/Mix\*(Aq, .Ve .IP "\fBmixmaster\fR" 4 .IX Item "mixmaster" Name of the mixmaster executable. If it is not in your \s-1PATH\s0 make sure to include path information. .Sp .Vb 2 \& Default: \*(Aqmixmaster\*(Aq => \*(Aqmix\*(Aq, \& Example: \*(Aqmixmaster\*(Aq => \*(Aq/home/pinger/Mix/mix\*(Aq, .Ve .IP "\fBtmpdir\fR" 4 .IX Item "tmpdir" General purpose temp directory. Make sure it is not shared with other applications. .Sp .Vb 1 \& Default: \*(Aqtmpdir\*(Aq => \*(Aqtmp\*(Aq, .Ve .IP "\fBcommands_file\fR" 4 .IX Item "commands_file" A file where commands to the daemon process are stored. The client puts commands (like add a new remailer) in it and then sends a \s-1HUP\s0 to the daemon process which reads and empties the file. .Sp .Vb 1 \& Default: \*(Aqcommands_file\*(Aq => \*(Aqcommands.txt\*(Aq, .Ve .IP "\fBpidfile\fR" 4 .IX Item "pidfile" The daemon's \s-1PID\s0 file. The daemon's Process \s-1ID\s0 is stored in this file. As long as it exists pingd refuses to start up in daemon mode. .Sp .Vb 1 \& Default: \*(Aqpidfile\*(Aq => \*(Aqpingd.pid\*(Aq, .Ve .IP "\fBbroken1\fR" 4 .IX Item "broken1" File listing broken type I remailer chains. If it does not exist, the part is skipped in generated stats. Otherwise its content is copied in verbatim. .Sp .Vb 4 \& Default: \*(Aqbroken1\*(Aq => \*(Aqbroken1.txt\*(Aq, \& Example content: \& (havenco cmeclax) \& (frog3 nycrem) .Ve .IP "\fBbroken2\fR" 4 .IX Item "broken2" File listing broken type \s-1II\s0 remailer chains. If it does not exist, the part is skipped in generated stats. Otherwise its content is copied in verbatim. .Sp .Vb 4 \& Default: \*(Aqbroken2\*(Aq => \*(Aqbroken2.txt\*(Aq, \& Example content: \& (freedom lcs) \& (* xganon) .Ve .IP "\fBsameop\fR" 4 .IX Item "sameop" File listing remailers that have the same operator or share a machine or other important infrastructure. If it does not exist, the part is skipped in generated stats. Otherwise its content is copied in verbatim. .Sp .Vb 4 \& Default: \*(Aqsameop\*(Aq => \*(Aqsameop.txt\*(Aq, \& Example content: \& (xganon2 xganon) \& (cracker redneck) .Ve .SS "\s-1LOGGING\s0" .IX Subsection "LOGGING" .IP "\fBlogfile\fR" 4 .IX Item "logfile" File to write logs to. This file is reopened on \s-1SIGHUP.\s0 .Sp .Vb 2 \& Default: \*(Aqlogfile\*(Aq => \*(Aqpingd.log\*(Aq, \& Example: \*(Aqlogfile\*(Aq => \*(Aq/var/log/echolot/pingd.log\*(Aq, .Ve .IP "\fBloglevel\fR" 4 .IX Item "loglevel" Minimum severity of messages to include in log file. Possible values are \&\fBtrace\fR, \fBdebug\fR, \fBinfo\fR, \fBnotice\fR, \fBwarning\fR, \fBerror\fR, \fBcritical\fR, \fBalert\fR, and \&\fBemergency\fR. .Sp .Vb 2 \& Default: \*(Aqloglevel\*(Aq => \*(Aqinfo\*(Aq, \& Example: \*(Aqloglevel\*(Aq => \*(Aqdebug\*(Aq, .Ve .SS "\s-1MISCELLANEOUS\s0" .IX Subsection "MISCELLANEOUS" .IP "\fBwrite_meta_files\fR [bool]" 4 .IX Item "write_meta_files [bool]" Whether to write meta files for each created file. These files include meta information for http servers and http clients like the date when a specific page expires. .Sp .Vb 1 \& Default: \*(Aqwrite_meta_files\*(Aq => 1, .Ve .IP "\fBmeta_extension\fR" 4 .IX Item "meta_extension" The extension that such metafiles (see above) should have. .Sp .Vb 1 \& Default: \*(Aqmeta_extension\*(Aq => \*(Aq.meta\*(Aq, .Ve .IP "\fBrandom_garbage\fR [integer]" 4 .IX Item "random_garbage [integer]" Pings usually are quite short. Some 100 bytes are sufficient to relay all the information that is required. To make them not stand out that obviously, pings are padded using random garbage of random length. .Sp \&\fBrandom_garbage\fR is the top limit for the amount of bytes to add. The actual number is randomly generated and uniformly distributed over [0, \fBrandom_garbage\fR] .Sp .Vb 1 \& Default: \*(Aqrandom_garbage\*(Aq => \*(Aq8192\*(Aq, .Ve .SS "\s-1CHAIN PINGING\s0" .IX Subsection "CHAIN PINGING" .IP "\fBdo_chainpings\fR [bool]" 4 .IX Item "do_chainpings [bool]" Whether or not to do chain pings. Chain pings test all chains of two remailers and come up with a list of broken chains. This produces a non-trivial amount of traffic. .Sp .Vb 1 \& Default: \*(Aqdo_chainpings\*(Aq => 1, .Ve .IP "\fBshow_chainpings\fR [bool]" 4 .IX Item "show_chainpings [bool]" Show the results of our chainpinging in public stats. .Sp .Vb 1 \& Default: \*(Aqshow_chainpings\*(Aq => 1, .Ve .IP "\fBchainping_fudge\fR" 4 .IX Item "chainping_fudge" What proportion of the expected replies derived from one-hop stats must return before a chain is not declared broken. .Sp .Vb 1 \& Default: \*(Aqchainping_fudge\*(Aq => 0.3, # if less than 0.3 * rel1 * rel2 make it, the chain is really broken .Ve .IP "\fBchainping_grace\fR" 4 .IX Item "chainping_grace" The factor of time in addition to the guessed latency derived from one-hop stats before a chain ping is considered lost .Sp .Vb 1 \& Default: \*(Aqchainping_grace\*(Aq => 1.5, # don\*(Aqt count pings sent no longer than 1.5 * (lat1 + lat2) ago .Ve .IP "\fBchainping_period\fR [seconds]" 4 .IX Item "chainping_period [seconds]" What time frame is taken into account when calculating chain stats. This should probably be smaller than \fBexpire_chainpings\fR. .Sp .Vb 1 \& Default: \*(Aqchainping_period\*(Aq => 12*24*60*60, # 12 days .Ve .IP "\fBchainping_minsample\fR [seconds]" 4 .IX Item "chainping_minsample [seconds]" Have at least as many sent (and not within grace) chain pings before declaring a chain broken. .Sp .Vb 1 \& Default: \*(Aqchainping_minsample\*(Aq => 3, # have at least sent 3 pings before judging any chain .Ve .IP "\fBchainping_allbad_factor\fR" 4 .IX Item "chainping_allbad_factor" How many chains \f(CW\*(C`(A x)\*(C'\fR must be bad before \f(CW\*(C`(A *)\*(C'\fR is listed. The value is given as a proportion of all available remailers. .Sp .Vb 1 \& Default: chainping_allbad_factor => 0.5, # at least 50% of possible chains (A x) need to fail for (A *) to be listed in broken chains .Ve .SS "\s-1PINGING TYPES\s0" .IX Subsection "PINGING TYPES" .IP "\fBdo_pings\fR" 4 .IX Item "do_pings" \&\fBdo_pings\fR determines which ping types are sent. It is a hash that has the following keys: .RS 4 .IP "\fBcpunk-dsa\fR" 4 .IX Item "cpunk-dsa" Send out CPunk pings to CPunk remailers with their \s-1DSA\s0 key. .IP "\fBcpunk-rsa\fR" 4 .IX Item "cpunk-rsa" Send out CPunk pings to CPunk remailers with their \s-1RSA\s0 key. .IP "\fBcpunk-clear\fR" 4 .IX Item "cpunk-clear" Send out unencrypted pings to CPunk remailers that don't have pgponly in their capsstring. .IP "\fBmix\fR" 4 .IX Item "mix" Pings mixmaster remailers. .RE .RS 4 .Sp .Vb 6 \& Default: \*(Aqdo_pings\*(Aq => { \& \*(Aqcpunk\-dsa\*(Aq => 1, \& \*(Aqcpunk\-rsa\*(Aq => 1, \& \*(Aqcpunk\-clear\*(Aq => 1, \& \*(Aqmix\*(Aq => 1 \& }, .Ve .RE .IP "\fBwhich_chainpings\fR" 4 .IX Item "which_chainpings" \&\fBwhich_chainpings\fR controls some aspects of chain pinging. It's a hash over chaintypes \- currently \fBmix\fR and \fBcpunk\fR. Each entry is a reference to an array which specifies the preference for key types in that chaintype. .Sp .Vb 4 \& Default: which_chainpings => { \& \*(Aqcpunk\*(Aq => [ qw{cpunk\-dsa cpunk\-rsa cpunk\-clear} ], \& \*(Aqmix\*(Aq => [ qw{mix} ] \& }, .Ve .Sp This means that in the case of cpunk chain pings we prefer using cpunk-dsa over cpunk-rsa which in turn we prefer to cpunk-clear. For mix there's only mix. .IP "\fBpings_weight\fR" 4 .IX Item "pings_weight" Not all pings have the same influence on the average reliability calcluated. Very new pings don't count fully since there is some margin of error. Similarly very old pings are not that interesting either. .Sp By default days 1 to 4 count fully (with weight 1), the older they are the less they count. .Sp .Vb 1 \& Default: pings_weight => [ qw{0.5 1.0 1.0 1.0 1.0 0.9 0.8 0.5 0.3 0.2 0.2 0.1 } ], .Ve .SS "\s-1TEMPLATES\s0" .IX Subsection "TEMPLATES" .IP "\fBtemplates\fR" 4 .IX Item "templates" The template files are used to generate the \s-1HTML\s0 version of all Echolot output. It is a hash of hashes which each have following keys: \&\fBthesaurusindexfile\fR, \&\fBmlist\fR, \&\fBmlist2\fR, \&\fBrlist\fR, \&\fBrlist-rsa\fR, \&\fBrlist-dsa\fR, \&\fBrlist-clear\fR, \&\fBrlist2\fR, \&\fBrlist2\-rsa\fR, \&\fBrlist2\-dsa\fR, \&\fBrlist2\-clear\fR, and \&\fBclist\fR. .Sp The outer hash keys are for language selection. .Sp .Vb 10 \& Default: \*(Aqtemplates\*(Aq => { \& \*(Aqdefault\*(Aq => { \& \*(Aqthesaurusindexfile\*(Aq => \*(Aqtemplates/thesaurusindex.html\*(Aq, \& \*(Aqmlist\*(Aq => \*(Aqtemplates/mlist.html\*(Aq, \& \*(Aqmlist2\*(Aq => \*(Aqtemplates/mlist2.html\*(Aq, \& \*(Aqrlist\*(Aq => \*(Aqtemplates/rlist.html\*(Aq, \& \*(Aqrlist\-rsa\*(Aq => \*(Aqtemplates/rlist\-rsa.html\*(Aq, \& \*(Aqrlist\-dsa\*(Aq => \*(Aqtemplates/rlist\-dsa.html\*(Aq, \& \*(Aqrlist\-clear\*(Aq => \*(Aqtemplates/rlist\-clear.html\*(Aq, \& \*(Aqrlist2\*(Aq => \*(Aqtemplates/rlist2.html\*(Aq, \& \*(Aqrlist2\-rsa\*(Aq => \*(Aqtemplates/rlist2\-rsa.html\*(Aq, \& \*(Aqrlist2\-dsa\*(Aq => \*(Aqtemplates/rlist2\-dsa.html\*(Aq, \& \*(Aqrlist2\-clear\*(Aq => \*(Aqtemplates/rlist2\-clear.html\*(Aq, \& \*(Aqclist\*(Aq => \*(Aqtemplates/clist.html\*(Aq, \& }, \& \*(Aqde\*(Aq => { \& \*(Aqthesaurusindexfile\*(Aq => \*(Aqtemplates/thesaurusindex.de.html\*(Aq, \& .... \& }, \& \*(Aqpl\*(Aq => { \& \*(Aqthesaurusindexfile\*(Aq => \*(Aqtemplates/thesaurusindex.pl.html\*(Aq, \& .... \& } \& }; .Ve .IP "\fBecholot_css\fR" 4 .IX Item "echolot_css" Location of the \s-1CSS\s0 file. This is copied to resultdir/echolot.css. .Sp .Vb 1 \& Default: \*(Aqecholot_css\*(Aq => \*(Aqtemplates/echolot.css\*(Aq, .Ve .SS "\s-1STRINGS\s0" .IX Subsection "STRINGS" .IP "\fBremailerxxxtext\fR" 4 .IX Item "remailerxxxtext" The text to send along with remailer-xxx queries. The template variables address and operator_address are substituted for their real values. .Sp .Vb 10 \& Default: \*(Aqremailerxxxtext\*(Aq => "Hello,\en". \& "\en". \& "This message requests remailer configuration data. The pinging software thinks\en". \& " is a remailer. Either it has been told so by the\en". \& "maintainer of the pinger or it found the address in a remailer\-conf or\en". \& "remailer\-key reply of some other remailer.\en". \& "\en". \& "If this is _not_ a remailer, you can tell this pinger that and it will stop\en". \& "sending you those requests immediately (otherwise it will try a few more times).\en". \& "Just reply and make sure the following is the first line of your message:\en". \& " not a remailer\en". \& "\en". \& "If you want to talk to a human please mail .\en", .Ve .SH "AUTHOR" .IX Header "AUTHOR" Peter Palfrader .SH "BUGS" .IX Header "BUGS" Please report them at