Merge tag 'upstream/1.14'

Upstream version 1.14
This commit is contained in:
Jan Wagner 2013-11-05 17:32:43 +01:00
commit 49660f29ce
9 changed files with 1178 additions and 202 deletions

View file

@ -36,22 +36,10 @@ case "$1" in
${PFWCMD} ${PFWARG} -vv --daemon --file=${PFWCFG} --interface=${PFWINET} --port=${PFWPORT} --user=${PFWUSER} --group=${PFWGROUP} --pidfile=${PFWPID};
;;
stop*) if [ -f "${PFWPID}" ]; then
echo "Stopping ${P1}...";
kill `cat ${PFWPID}`;
else
echo "Pidfile \"${PFWPID}\" not found" ;
false;
fi ;
stop*) ${PFWCMD} --interface=${PFWINET} --port=${PFWPORT} --pidfile=${PFWPID} --kill;
;;
reload*) if [ -f "${PFWPID}" ]; then
echo "Stopping ${P1}...";
kill -HUP `cat ${PFWPID}`;
else
echo "Pidfile \"${PFWPID}\" not found" ;
false;
fi ;
reload*) ${PFWCMD} --interface=${PFWINET} --port=${PFWPORT} --pidfile=${PFWPID} -- reload;
;;
restart*) $0 stop;
@ -60,7 +48,7 @@ case "$1" in
;;
*) echo "Unknown argument \"$1\"" >&2;
echo "Usage: `basename $0` {start|stop|reload|restart}" >&2;
echo "Usage: `basename $0` {start|stop|debug|reload|restart}" >&2;
exit 1;;
esac
exit $?

View file

@ -1,3 +1,57 @@
1.14
=====
- feature: new compare operators *
====================================================================
ITEM == VALUE true if ITEM equals VALUE
ITEM => VALUE true if ITEM >= VALUE
ITEM =< VALUE true if ITEM <= VALUE
ITEM =~ VALUE true if ITEM ~= /^VALUE$/i
*ITEM != VALUE false if ITEM equals VALUE
*ITEM !> VALUE false if ITEM >= VALUE
*ITEM !< VALUE false if ITEM <= VALUE
*ITEM !~ VALUE false if ITEM ~= /^VALUE$/i
ITEM = VALUE default behaviour (see ITEMS section)
====================================================================
- feature: added --nodaemon option
- code: non dns items first: if a rule contains dns and non dns items, the
lookups will only be done if all non dns items matched
- bugfix: empty pcre with empty sender_(ns|mx)_names was parsed incorrectly.
this bug affects postfwd versions 1.12 - 1.13
- bugfix: negated pcre items with '~=' operator were parsed incorrectly.
this bug affects postfwd version 1.13
1.13
=====
- feature: enabled dns cache for sender(ns|mx) and helo address
- feature: new options --dns_max_ns_lookups and --dns_max_mx_lookups
- bugfix: workaround: Net::Server died if a unix domain socket
filename without a dot ('.') was used (B. Frauendienst)
1.12
=====
- feature: new items sender_ns_names and sender_ns_addrs
- feature: new items sender_mx_names and sender_mx_addrs
- feature: new item helo_address, please see docs for more
- feature: added --proto switch, to enable the use of unix domain sockets
(thanks to Bernhard Frauendienst)
- feature: added command-line options --kill and --reload
(of course you can still use TERM and HUP signals)
- feature: dnsbl txt lookups only for dnsbls with at least one a record.
use --dns_async_txt for the old behaviour (see docs for more).
- code: small performance improvement (5-10%) for pcre (~= or =~) items
- bugfix: network 0.0.0.0/0 did not work as expected on all platforms
- bugfix: postfwd tried to chop() an uninitialized value when sending
garbage (non policy delegation protocol requests) to it.
1.11
=====
- feature: the ask() action allows to delegate the policy decision to another
policy service (like postgrey). a new parameter allows to specify
answer patterns which should be ignored by postfwd. please look
at the 'ACTIONS' section in the manual (postfwd2 -m) for details.
- feature: new options --noidlestats and --norulelog
- feature: more informative --version
- feature: documentation updates
**************************************************************************************************
@ -6,7 +60,6 @@ ATTENTION: requirements changed - postfwd since v1.10pre8 now uses Net::DNS.
NOTE: please see the docs ('postfwd -m' or 'perldoc postfwd') for more information
**************************************************************************************************
1.10pre8b
==========
- bugfix: fixed two warnings about logging of undefined values in verbose mode

View file

@ -1,15 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>postfwd - postfix firewall daemon</title>
<link rel="stylesheet" type="text/css" href="http://www.jpkessler.de/css/postfwd.css">
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
<meta name="description" content="postfwd a postfix firewall policy daemon">
<meta name="author" content="jpk">
<meta name="keywords" content="postfwd, postfwd usage, postfwd manual, postfix, policy, policy delegation, firewall, postfix acl, postfix acls, pfwpolicy, postfw, restrictions, IT-Security, IT-Consulting, Jan, Peter, Kessler">
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rev="made" href="mailto:feedback@suse.de" />
</head>
<body>
<body style="background-color: white">
<p><a name="__index__"></a></p>
<!-- INDEX BEGIN -->
@ -64,6 +62,7 @@
-d, --daemon run postfwd as daemon
-i, --interface &lt;dev&gt; listen on interface &lt;dev&gt;
-p, --port &lt;port&gt; listen on port &lt;port&gt;
--proto &lt;proto&gt; socket type (tcp or unix)
-u, --user &lt;name&gt; set uid to user &lt;name&gt;
-g, --group &lt;name&gt; set gid to group &lt;name&gt;
-R, --chroot &lt;path&gt; chroot the daemon to &lt;path&gt;
@ -87,12 +86,17 @@
-t, --test testing, always returns &quot;dunno&quot;
-v, --verbose verbose logging, use twice (-vv) to increase level
-S, --summary &lt;int&gt; show some usage statistics every &lt;int&gt; seconds
--no-rulestats disables per rule statistics
--norulelog disbles rule logging
--norulestats disables per rule statistics
--noidlestats disables statistics when idle
-n, --nodns disable dns
--nodnslog disable dns logging
--dns_async_txt perform dnsbl A and TXT lookups simultaneously
--dns_timeout timeout in seconds for asynchonous dns queries
--dns_timeout_max maximum of dns timeouts until a dnsbl will be deactivated
--dns_timeout_interval interval in seconds for dns timeout maximum counter
--dns_max_ns_lookups max names to look up with sender_ns_addrs
--dns_max_mx_lookups max names to look up with sender_mx_addrs
-I, --instantcfg re-reads rulefiles for every new request</pre>
<pre>
Informational (use only at command-line!):
@ -207,6 +211,15 @@ arguments. Please see the COMMAND LINE section below for more information on thi
<pre>
recipient_localpart, - the local-/domainpart of the recipient address
recipient_domain</pre>
<pre>
helo_address - postfwd tries to look up the helo_name. use
helo_address=!!(0.0.0.0/0) to check for unknown.</pre>
<pre>
sender_ns_names, - postfwd tries to look up the names/ip addresses
sender_ns_addrs of the nameservers for the sender domain part.</pre>
<pre>
sender_mx_names, - postfwd tries to look up the names/ip addresses
sender_mx_addrs of the mx records for the sender domain part.</pre>
<pre>
version - postfwd version, contains &quot;postfwd n.nn&quot;
this enables version based checks in your rulesets
@ -230,6 +243,11 @@ for details:</p>
score=5.0 mask = maximum floating point value
rbl=zen.spamhaus.org mask = &lt;name&gt;/&lt;reply&gt;/&lt;maxcache&gt;[,...]
rblcount=2 mask = numeric, will match if rbl hits &gt;= 2
helo_address=&lt;a.b.c.d/nn&gt; mask = CIDR[,CIDR,...]
sender_ns_names=some.domain.tld mask = PCRE
sender_mx_names=some.domain.tld mask = PCRE
sender_ns_addrs=&lt;a.b.c.d/nn&gt; mask = CIDR[,CIDR,...]
sender_mx_addrs=&lt;a.b.c.d/nn&gt; mask = CIDR[,CIDR,...]
# ------------------------------
# Postfix version 2.1 and later:
# ------------------------------
@ -352,6 +370,16 @@ rule containing only an action statement:</p>
# size limit 1.5mb per hour per client
id=SIZE01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \
action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)</pre>
<pre>
ask (&lt;addr&gt;:&lt;port&gt;[:&lt;ignore&gt;])
allows to delegate the policy decision to another policy service (e.g. postgrey). the first
and the second argument (address and port) are mandatory. a third optional argument may be
specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
# example1: query postgrey and return it's answer to postfix
id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031)
# example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
# and continue parsing postfwd's ruleset
id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031:^dunno$)</pre>
<pre>
wait (&lt;delay&gt;)
pauses the program execution for &lt;delay&gt; seconds. use this for
@ -476,6 +504,11 @@ The following arguments will control it's behaviour in this case.</p>
<pre>
-p, --port &lt;port&gt;
postfwd listens on the specified port (default tcp/10040).</pre>
<pre>
--proto &lt;type&gt;
The protocol type for postfwd's socket. Currently you may use 'tcp' or 'unix' here.
To use postfwd with a unix domain socket, run it as follows:
postfwd --proto=unix --port=/somewhere/postfwd.socket</pre>
<pre>
-u, --user &lt;name&gt;
Changes real and effective user to &lt;name&gt;.</pre>
@ -599,6 +632,17 @@ The following arguments will control it's behaviour in this case.</p>
--dns_timeout_interval (default=1200)
The dnsbl timeout counter will be cleaned after this interval in seconds. Use this
in conjunction with the --dns_timeout_max parameter.</pre>
<pre>
--dns_async_txt
Perform dnsbl A and TXT lookups simultaneously (otherwise only for listings with at
least one A record). This needs more network bandwidth due to increased queries but
might increase throughput because the lookups can be parallelized.</pre>
<pre>
--dns_max_ns_lookups (default=0)
maximum ns names to lookup up with sender_ns_addrs item. use 0 for no maximum.</pre>
<pre>
--dns_max_mx_lookups (default=0)
maximum mx names to lookup up with sender_mx_addrs item. use 0 for no maximum.</pre>
<pre>
-I, --instantcfg
The config files, specified by -f will be re-read for every request
@ -854,7 +898,7 @@ The parser stops rule processing and returns the action to postfix. Other rules
The parser evaluates the given action and continues with the next rule (except for the <code>jump()</code> or <code>quit()</code> actions - please see the <a href="#actions">ACTIONS</a> section
for more information). Nothing will be sent to postfix.</p>
<p>If no rule has matched and the end of the ruleset is reached postfwd will return dunno without logging anything unless in verbose mode. You may
simply place a last `catch-all´ rule to change that behaviour:</p>
simply place a last `catch-all´ rule to change that behaviour:</p>
<pre>
... &lt;your rules&gt; ...
id=DEFAULT ; action=dunno</pre>
@ -996,17 +1040,8 @@ POSSIBILITY OF SUCH DAMAGE.</p>
</p>
<hr />
<h1><a name="author">AUTHOR</a></h1>
<p>Jan&nbsp;Peter&nbsp;Kessler&nbsp;&lt;info (AT) postfwd (DOT) org&gt;. Let me know, if you have any suggestions.</p>
<p><center>
<table border="1" color="black" frame="hsides" rules="none" width="100%">
<td width="33%" align="left"><small>http://www.postfwd.org/doc.html</small>
<td width="34%" align="center"><small>2007 by <a href="http://www.jpkessler.de/">Jan Peter Kessler</a></small>
<td width="33%" align="right"><small>info (AT) postfwd (DOT) org</small>
</table>
</center></p>
<p>Jan&nbsp;Peter&nbsp;Kessler&nbsp;&lt;info&nbsp;(AT)&nbsp;postfwd&nbsp;(DOT)&nbsp;org&gt;. Let me know, if you have any suggestions.</p>
</body>
</html>

View file

@ -15,6 +15,7 @@ SYNOPSIS
-d, --daemon run postfwd as daemon
-i, --interface <dev> listen on interface <dev>
-p, --port <port> listen on port <port>
--proto <proto> socket type (tcp or unix)
-u, --user <name> set uid to user <name>
-g, --group <name> set gid to group <name>
-R, --chroot <path> chroot the daemon to <path>
@ -38,12 +39,17 @@ SYNOPSIS
-t, --test testing, always returns "dunno"
-v, --verbose verbose logging, use twice (-vv) to increase level
-S, --summary <int> show some usage statistics every <int> seconds
--no-rulestats disables per rule statistics
--norulelog disbles rule logging
--norulestats disables per rule statistics
--noidlestats disables statistics when idle
-n, --nodns disable dns
--nodnslog disable dns logging
--dns_async_txt perform dnsbl A and TXT lookups simultaneously
--dns_timeout timeout in seconds for asynchonous dns queries
--dns_timeout_max maximum of dns timeouts until a dnsbl will be deactivated
--dns_timeout_interval interval in seconds for dns timeout maximum counter
--dns_max_ns_lookups max names to look up with sender_ns_addrs
--dns_max_mx_lookups max names to look up with sender_mx_addrs
-I, --instantcfg re-reads rulefiles for every new request
Informational (use only at command-line!):
@ -179,6 +185,15 @@ DESCRIPTION
recipient_localpart, - the local-/domainpart of the recipient address
recipient_domain
helo_address - postfwd tries to look up the helo_name. use
helo_address=!!(0.0.0.0/0) to check for unknown.
sender_ns_names, - postfwd tries to look up the names/ip addresses
sender_ns_addrs of the nameservers for the sender domain part.
sender_mx_names, - postfwd tries to look up the names/ip addresses
sender_mx_addrs of the mx records for the sender domain part.
version - postfwd version, contains "postfwd n.nn"
this enables version based checks in your rulesets
(e.g. for migration). works with old versions too,
@ -204,6 +219,11 @@ DESCRIPTION
score=5.0 mask = maximum floating point value
rbl=zen.spamhaus.org mask = <name>/<reply>/<maxcache>[,...]
rblcount=2 mask = numeric, will match if rbl hits >= 2
helo_address=<a.b.c.d/nn> mask = CIDR[,CIDR,...]
sender_ns_names=some.domain.tld mask = PCRE
sender_mx_names=some.domain.tld mask = PCRE
sender_ns_addrs=<a.b.c.d/nn> mask = CIDR[,CIDR,...]
sender_mx_addrs=<a.b.c.d/nn> mask = CIDR[,CIDR,...]
# ------------------------------
# Postfix version 2.1 and later:
# ------------------------------
@ -351,6 +371,16 @@ DESCRIPTION
id=SIZE01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \
action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)
ask (<addr>:<port>[:<ignore>])
allows to delegate the policy decision to another policy service (e.g. postgrey). the first
and the second argument (address and port) are mandatory. a third optional argument may be
specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
# example1: query postgrey and return it's answer to postfix
id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031)
# example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
# and continue parsing postfwd's ruleset
id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031:^dunno$)
wait (<delay>)
pauses the program execution for <delay> seconds. use this for
delaying or throtteling connections.
@ -492,6 +522,11 @@ DESCRIPTION
-p, --port <port>
postfwd listens on the specified port (default tcp/10040).
--proto <type>
The protocol type for postfwd's socket. Currently you may use 'tcp' or 'unix' here.
To use postfwd with a unix domain socket, run it as follows:
postfwd --proto=unix --port=/somewhere/postfwd.socket
-u, --user <name>
Changes real and effective user to <name>.
@ -618,6 +653,17 @@ DESCRIPTION
The dnsbl timeout counter will be cleaned after this interval in seconds. Use this
in conjunction with the --dns_timeout_max parameter.
--dns_async_txt
Perform dnsbl A and TXT lookups simultaneously (otherwise only for listings with at
least one A record). This needs more network bandwidth due to increased queries but
might increase throughput because the lookups can be parallelized.
--dns_max_ns_lookups (default=0)
maximum ns names to lookup up with sender_ns_addrs item. use 0 for no maximum.
--dns_max_mx_lookups (default=0)
maximum mx names to lookup up with sender_mx_addrs item. use 0 for no maximum.
-I, --instantcfg
The config files, specified by -f will be re-read for every request
postfwd receives. This enables on-the-fly configuration changes
@ -905,7 +951,7 @@ DESCRIPTION
If no rule has matched and the end of the ruleset is reached postfwd
will return dunno without logging anything unless in verbose mode. You
may simply place a last `catch-all´ rule to change that behaviour:
may simply place a last `catch-all´ rule to change that behaviour:
... <your rules> ...
id=DEFAULT ; action=dunno

View file

@ -2,8 +2,8 @@
###################################################################################################
##
## ATTENTION: This example configuration uses features which require at least postfwd 1.10pre6!
## Please see the manual ('postfwd -m') for example syntax for prior versions.
## ATTENTION: Do NOT use this configuration without your own customizations!
## Please see the manual ('postfwd -m') for more information.
##
###################################################################################################
@ -12,6 +12,11 @@
## Definitions
##
# Greylisting with postgrey @ 127.0.0.1:10031
&&GREYLIST { \
action=ask(127.0.0.1:10031); \
};
# Maintenance times
&&MAINTENANCE { \
date=15.01.2007 - 15.01.2007 ; \
@ -124,10 +129,10 @@ id=RATE_002 ; &&DYNAMIC ; \
id=GREY_001 ; action=dunno ; &&STATIC
id=GREY_002 ; action=dunno ; $$client_name~=$$(sender_domain)$
id=GREY_003 ; action=dunno ; HIT_dnswls>=1
id=GREY_004 ; action=greylisting ; &&DYNAMIC
id=GREY_005 ; action=greylisting ; HIT_dnsbls>=1
id=GREY_004 ; action=&&GREYLIST ; &&DYNAMIC
id=GREY_005 ; action=&&GREYLIST ; HIT_dnsbls>=1
# Greylisting should be safe during out-of-office times
id=GREY_006 ; action=greylisting ; days=Sat-Sun
id=GREY_007 ; action=greylisting ; days=Mon-Fri ; time=!!06:00:00-20:00:00
id=GREY_006 ; action=&&GREYLIST ; days=Sat-Sun
id=GREY_007 ; action=&&GREYLIST ; days=Mon-Fri ; time=!!06:00:00-20:00:00

View file

@ -1,4 +1,4 @@
.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.14
.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32
.\"
.\" Standard preamble:
.\" ========================================================================
@ -128,8 +128,8 @@
.rm #[ #] #H #V #F C
.\" ========================================================================
.\"
.IX Title "POSTFWD 8"
.TH POSTFWD 8 "2008-09-14" "perl v5.8.5" "User Contributed Perl Documentation"
.IX Title "MANUAL1 8"
.TH MANUAL1 8 "2009-06-27" "perl v5.8.8" "User Contributed Perl Documentation"
.SH "NAME"
postfwd \- postfix firewall daemon
.SH "SYNOPSIS"
@ -147,11 +147,12 @@ postfwd [\s-1OPTIONS\s0] [\s-1SOURCE1\s0, \s-1SOURCE2\s0, ...]
\& -s, --scores <v>=<r> returns <r> when score exceeds <v>
.Ve
.PP
.Vb 10
.Vb 11
\& Networking:
\& -d, --daemon run postfwd as daemon
\& -i, --interface <dev> listen on interface <dev>
\& -p, --port <port> listen on port <port>
\& --proto <proto> socket type (tcp or unix)
\& -u, --user <name> set uid to user <name>
\& -g, --group <name> set gid to group <name>
\& -R, --chroot <path> chroot the daemon to <path>
@ -174,17 +175,22 @@ postfwd [\s-1OPTIONS\s0] [\s-1SOURCE1\s0, \s-1SOURCE2\s0, ...]
\& --cleanup-rates cleanup interval in seconds for rate cache
.Ve
.PP
.Vb 11
.Vb 16
\& Optional:
\& -t, --test testing, always returns "dunno"
\& -v, --verbose verbose logging, use twice (-vv) to increase level
\& -S, --summary <int> show some usage statistics every <int> seconds
\& --no-rulestats disables per rule statistics
\& --norulelog disbles rule logging
\& --norulestats disables per rule statistics
\& --noidlestats disables statistics when idle
\& -n, --nodns disable dns
\& --nodnslog disable dns logging
\& --dns_async_txt perform dnsbl A and TXT lookups simultaneously
\& --dns_timeout timeout in seconds for asynchonous dns queries
\& --dns_timeout_max maximum of dns timeouts until a dnsbl will be deactivated
\& --dns_timeout_interval interval in seconds for dns timeout maximum counter
\& --dns_max_ns_lookups max names to look up with sender_ns_addrs
\& --dns_max_mx_lookups max names to look up with sender_mx_addrs
\& -I, --instantcfg re-reads rulefiles for every new request
.Ve
.PP
@ -346,6 +352,21 @@ Rules can span multiple lines by adding a trailing backslash \*(L"\e\*(R" charac
\& recipient_domain
.Ve
.PP
.Vb 2
\& helo_address - postfwd tries to look up the helo_name. use
\& helo_address=!!(0.0.0.0/0) to check for unknown.
.Ve
.PP
.Vb 2
\& sender_ns_names, - postfwd tries to look up the names/ip addresses
\& sender_ns_addrs of the nameservers for the sender domain part.
.Ve
.PP
.Vb 2
\& sender_mx_names, - postfwd tries to look up the names/ip addresses
\& sender_mx_addrs of the mx records for the sender domain part.
.Ve
.PP
.Vb 6
\& version - postfwd version, contains "postfwd n.nn"
\& this enables version based checks in your rulesets
@ -361,7 +382,7 @@ Feel free to combine them the way you need it (have a look at the \s-1EXAMPLES\s
Most values can be specified as regular expressions (\s-1PCRE\s0). Please see the table below
for details:
.PP
.Vb 38
.Vb 43
\& # ==========================================================
\& # ITEM=VALUE TYPE
\& # ==========================================================
@ -373,6 +394,11 @@ for details:
\& score=5.0 mask = maximum floating point value
\& rbl=zen.spamhaus.org mask = <name>/<reply>/<maxcache>[,...]
\& rblcount=2 mask = numeric, will match if rbl hits >= 2
\& helo_address=<a.b.c.d/nn> mask = CIDR[,CIDR,...]
\& sender_ns_names=some.domain.tld mask = PCRE
\& sender_mx_names=some.domain.tld mask = PCRE
\& sender_ns_addrs=<a.b.c.d/nn> mask = CIDR[,CIDR,...]
\& sender_mx_addrs=<a.b.c.d/nn> mask = CIDR[,CIDR,...]
\& # ------------------------------
\& # Postfix version 2.1 and later:
\& # ------------------------------
@ -536,6 +562,18 @@ postfwd actions control the behaviour of the program. Currently you can specify
\& action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)
.Ve
.PP
.Vb 9
\& ask (<addr>:<port>[:<ignore>])
\& allows to delegate the policy decision to another policy service (e.g. postgrey). the first
\& and the second argument (address and port) are mandatory. a third optional argument may be
\& specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
\& # example1: query postgrey and return it's answer to postfix
\& id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031)
\& # example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
\& # and continue parsing postfwd's ruleset
\& id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031:^dunno$)
.Ve
.PP
.Vb 3
\& wait (<delay>)
\& pauses the program execution for <delay> seconds. use this for
@ -712,6 +750,13 @@ The following arguments will control it's behaviour in this case.
\& postfwd listens on the specified port (default tcp/10040).
.Ve
.PP
.Vb 4
\& --proto <type>
\& The protocol type for postfwd's socket. Currently you may use 'tcp' or 'unix' here.
\& To use postfwd with a unix domain socket, run it as follows:
\& postfwd --proto=unix --port=/somewhere/postfwd.socket
.Ve
.PP
.Vb 2
\& -u, --user <name>
\& Changes real and effective user to <name>.
@ -891,6 +936,23 @@ These parameters influence the way postfwd is working. Any of them can be combin
\& in conjunction with the --dns_timeout_max parameter.
.Ve
.PP
.Vb 4
\& --dns_async_txt
\& Perform dnsbl A and TXT lookups simultaneously (otherwise only for listings with at
\& least one A record). This needs more network bandwidth due to increased queries but
\& might increase throughput because the lookups can be parallelized.
.Ve
.PP
.Vb 2
\& --dns_max_ns_lookups (default=0)
\& maximum ns names to lookup up with sender_ns_addrs item. use 0 for no maximum.
.Ve
.PP
.Vb 2
\& --dns_max_mx_lookups (default=0)
\& maximum mx names to lookup up with sender_mx_addrs item. use 0 for no maximum.
.Ve
.PP
.Vb 6
\& -I, --instantcfg
\& The config files, specified by -f will be re-read for every request
@ -1225,7 +1287,7 @@ The parser evaluates the given action and continues with the next rule (except f
for more information). Nothing will be sent to postfix.
.PP
If no rule has matched and the end of the ruleset is reached postfwd will return dunno without logging anything unless in verbose mode. You may
simply place a last `catch\-all´ rule to change that behaviour:
simply place a last `catch\-all´ rule to change that behaviour:
.PP
.Vb 2
\& ... <your rules> ...

File diff suppressed because it is too large Load diff

160
tools/postfwd-client.pl Executable file
View file

@ -0,0 +1,160 @@
#!/usr/bin/perl -w
## MODULES
#use strict;
use warnings;
use IO::Socket;
use IO::Pipe;
use Getopt::Long 2.25 qw(:config no_ignore_case bundling);
BEGIN {
eval { require Time::HiRes };
if ($@) {
warn "$@";
warn "Failed to include optional module Time::HiRes.";
} else {
Time::HiRes->import( qw(time) );
};
};
## PARAMETERS
my $syntax = "USAGE: client.pl [ OPTIONS ] <addr>:<port>";
my $sendstr = 'ccert_fingerprint=
size=64063
helo_name=english-breakfast.cloud9.net
reverse_client_name=english-breakfast.cloud9.net
queue_id=
encryption_cipher=
encryption_protocol=
etrn_domain=
ccert_subject=
request=smtpd_access_policy
protocol_state=RCPT
recipient=someone@domain.local
instance=6748.46adf3f8.62156.0
protocol_name=ESMTP
encryption_keysize=0
recipient_count=0
ccert_issuer=
sender=owner-postfix-users@postfix.org
client_name=english-breakfast.cloud9.net
client_address=168.100.1.7
';
my $delay = 0.5;
our $pipe = new IO::Pipe;
use vars qw( %options %kinder $kind $wait );
## COMMAND LINE
GetOptions( \%options,
'verbose|v+',
'quiet|q+',
'process|p=i',
'count|c=i',
'timeout|t=i',
'file|f=s',
) or die "$syntax\n";
die "$syntax\n" unless $ARGV[0];
map { $options{$_} ||= 1 } qw(count process);
$options{verbose} ||= 0;
$options{timeout} ||= 3;
if (defined $options{file}) {
(-f $options{file}) || die "can not find file '".$options{file}."'\n";
open (REQUEST, "<".$options{file}) || die "can not open file '".$options{file}."'\n";
$sendstr = join "", <REQUEST>;
close (REQUEST);
};
## FORK
$| = 1;
my $starttime = time();
FORK: for (my $i=0;$i<$options{process};$i++) {
$kind = fork();
last FORK unless $kind;
$kinder{$kind} = 1;
};
## WHO AM I?
($kind) ? parent_process() : child_process() ;
die "should never see me\n";
exit(1);
## PARENT CODE
sub parent_process {
$pipe->reader();
use POSIX ":sys_wait_h";
undef my @status;
# wait until children have finished
print ("parent process waiting for ".(scalar keys %kinder)." pids ".(join ' ', (keys %kinder))."\n") unless $options{quiet};
PARENT: do {
# check pipe for finished children
push @status, <$pipe>;
# check children
CHILD: foreach (keys %kinder) {
$wait = waitpid($_,&WNOHANG);
last CHILD unless ($wait == -1);
delete $kinder{$_};
};
# sleep a while to reduce cpu usage
select(undef, undef, undef, $delay);
print ("parent process waiting for ".(scalar keys %kinder)." pids ".(join ' ', (keys %kinder))."\n") if ($options{verbose} > 1);
} until (($wait == -1) or (($#status + 1) >= $options{process}));
printf ("parent process finished after %.2f seconds.\n", (time() - $starttime)) unless $options{quiet};
# display results
my $parent_requests = my $parent_errors = my $parent_valid = my $parent_invalid = my $parent_time = 0;
foreach (@status) {
my($child_requests,$child_errors,$child_valid,$child_invalid,$child_time) = split ';', $_;
$parent_requests += $child_requests;
$parent_errors += $child_errors;
$parent_valid += $child_valid;
$parent_invalid += $child_invalid;
$parent_time = $child_time if ($child_time > $parent_time);
};
$parent_time = $parent_time - $starttime;
my $parent_rps = ($parent_time) ? ($parent_requests / $parent_time) : 0;
printf "%d requests, %d errors, %d valid, %d invalid, %.2fs total time, %.2f requests per second\n",
$parent_requests,$parent_errors,$parent_valid,$parent_invalid,$parent_time,$parent_rps;
exit (0);
};
## CHILD CODE
sub child_process {
$pipe->writer();
my $ok = my $nok = 0;
undef my $getstr;
# open socket
my($addr,$port) = split ':', $ARGV[0];
if ( ($addr and $port) and my $socket = new IO::Socket::INET (
PeerAddr => $addr,
PeerPort => $port,
Proto => 'tcp',
Timeout => $options{timeout},
Type => SOCK_STREAM ) ) {
# submit requests
for (my $i=0; $i<$options{count}; $i++) {
printf ("CHILD-$$: asking service $addr:$port\n") if $options{verbose};
print $socket "$sendstr";
$getstr = <$socket>; <$socket>;
chomp($getstr);
printf ("CHILD-$$: answer from $addr:$port -> '$getstr'\n") if $options{verbose};
$getstr =~ s/^(action=)//;
# check answer
if ($1 and $getstr) {
$ok++;
printf ("CHILD-$$: OK: answer from $addr:$port -> '$getstr'\n") unless ( $options{quiet} or (($options{count} * $options{process}) > 50) );
} else {
$nok++;
warn ("CHILD-$$: FAIL: invalid answer from $addr:$port -> '$getstr'\n");
};
};
} else {
warn ("CHILD-$$: can not open socket to $addr:$port\n");
};
# create summary
my $summary = $options{count}.';'.($options{count} - ($ok + $nok)).';'.$ok.';'.$nok.';'.time()."\n";
print ("CHILD-$$: child summary: $summary") if ($options{verbose} > 1);
# send summary to parent
print $pipe "$summary";
exit (0);
};

297
tools/rblcheck.pl Executable file
View file

@ -0,0 +1,297 @@
#!/usr/bin/perl -T -w
# includes
use strict;
use warnings;
use Getopt::Long 2.25 qw(:config no_ignore_case bundling);
use Net::DNS;
# include Time::HiRes if available
BEGIN {
eval { require Time::HiRes };
Time::HiRes->import( qw(time) ) unless $@;
};
# RBLs (ip based)
our @rbls = qw(
zz.countries.nerd.dk
query.bondedsender.org
exemptions.ahbl.org
spf.trusted-forwarder.org
list.dnswl.org
zen.spamhaus.org
b.barracudacentral.org
bl.spamcop.net
list.dsbl.org
multihop.dsbl.org
unconfirmed.dsbl.org
combined.njabl.org
dnsbl.sorbs.net
dnsbl.ahbl.org
ix.dnsbl.manitu.net
dnsbl-1.uceprotect.net
dnsbl-2.uceprotect.net
dnsbl-3.uceprotect.net
ips.backscatterer.org
sorbs.dnsbl.net.au
t1.dnsbl.net.au
korea.services.net
blackholes.five-ten-sg.com
cbl.anti-spam.org.cn
cblplus.anti-spam.org.cn
cblless.anti-spam.org.cn
bogons.cymru.com
dynamic.tqmrbl.com
relays.tqmrbl.com
clients.tqmrbl.com
hostkarma.junkemailfilter.com
sip.invaluement.com
);
# RHSBLs (domain based)
our @rhsbls = qw(
rhsbl.sorbs.net
rhsbl.ahbl.org
multi.surbl.org
dsn.rfc-ignorant.org
abuse.rfc-ignorant.org
whois.rfc-ignorant.org
bogusmx.rfc-ignorant.org
blackhole.securitysage.com
ex.dnsbl.org
rddn.dnsbl.net.au
block.rhs.mailpolice.com
dynamic.rhs.mailpolice.com
dnsbl.cyberlogic.net
hostkarma.junkemailfilter.com
);
# commandline syntax
our $syntax = <<__SYNTAX__;
Usage: rblcheck3.pl [OPTIONS] <objects>
-h, --help manual
-s, --short short output
-v, --verbose show dns nxdomain answers (not listed)
-n, --noerror do not show dns query timeouts
-t, --timeout=10 dns query timeout setting in seconds
--dnsstats show dns statistics
--rbls=<list> override builtin rbls with <list>
--rhsbls=<list> override builtin rhsbls with <list>
<objects> list of ips, hostnames and e-mail addresses
__SYNTAX__
# manual
our $examples = <<__EXAMPLES__;
Examples:
# check builtin rbls for 192.168.0.1 and rhsbls for host.example.com
rblcheck3.pl 192.168.0.1 host.example.com
# same as above
rblcheck3.pl host.example.com[192.168.0.1]
# check builtin rhsbls for the domain part "example.com",
# set dns timeout to 15 seconds
rblcheck3.pl -t 15 john.doe\@example.com
# check spamhaus and spamcop for 192.168.0.1
# short output without dns timeout information
rblcheck3.pl -ns --rbls=zen.spamhaus.org,bl.spamcop.net 192.168.0.1
__EXAMPLES__
# save current time
our $starttime = time();
# variables
use vars qw(
%dnshits %dnscache %options
@queries @lookups @timedout
);
# parse commandline switches
GetOptions( \%options,
"timeout|t=i",
"noerror|n",
"verbose|v",
"short|s+",
"dnsstats",
"rbls|rbl=s" => sub { push @{$options{rbls}}, (split /[,\s]+/, $_[1]) },
"rhsbls|rhsbl=s" => sub { push @{$options{rhsbls}}, (split /[,\s]+/, $_[1]) },
"help|h" => sub { print "\n$syntax\n$examples\n"; exit(1) },
) or die "\n$syntax\n";
# unbuffered output
#select STDERR; $| = 1;
#select STDOUT; $| = 1;
# optional: override dnsbl lists
@rbls = @{$options{rbls}} if defined $options{rbls};
@rhsbls = @{$options{rhsbls}} if defined $options{rhsbls};
# split client[ip] in two queries
map { push @queries, (/^([^\]]+)\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]$/) ? ($1, $2) : $_ } @ARGV;
# parse queries and create lookup list
foreach my $query (@queries) {
undef my $addr;
# prepare rbls
if ($query =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
$addr = join ".", reverse split /\./, $query;
foreach my $rbl (@rbls) {
$dnscache{$addr.".".$rbl}{type} = 'RBL';
$dnscache{$addr.".".$rbl}{query} = $query;
$dnscache{$addr.".".$rbl}{list} = $rbl;
push @lookups, $addr.".".$rbl;
};
# prepare rhsbls
} else {
# remove localpart if email address
$addr = ($query =~ /@([^@]+)$/) ? $1 : $query;
foreach my $rbl (@rhsbls) {
$dnscache{$addr.".".$rbl}{type} = 'RHSBL';
$dnscache{$addr.".".$rbl}{query} = $query;
$dnscache{$addr.".".$rbl}{list} = $rbl;
push @lookups, $addr.".".$rbl;
};
};
};
# main: process lookups
if ( @lookups ) {
my $ownres = Net::DNS::Resolver->new;
my $ownsel = IO::Select->new;
my %ownsock = ();
my @ownready = ();
my $bgsock = undef;
# send queries
QUERY: foreach my $query (@lookups) {
next QUERY unless $query;
# send A query
$dnscache{$query}{start} = time();
$bgsock = $ownres->bgsend($query, 'A');
$ownsel->add($bgsock);
$ownsock{$bgsock} = 'A:'.$query;
# send TXT query
$bgsock = $ownres->bgsend($query, 'TXT');
$ownsel->add($bgsock);
$ownsock{$bgsock} = 'TXT:'.$query;
};
# get answers
while ((scalar keys %ownsock) and (@ownready = $ownsel->can_read($options{timeout} || 10))) {
foreach my $sock (@ownready) {
if (defined $ownsock{$sock}) {
my $packet = $ownres->bgread($sock);
rbl_read_dns ($packet);
delete $ownsock{$sock};
} else {
$ownsel->remove($sock);
$sock = undef;
};
};
};
# timeout handling
my $now = time();
map { push @timedout, (split ':', $ownsock{$_})[1] } (keys %ownsock);
map { @{$dnscache{$_}{A}} = '**timeout**'; $dnscache{$_}{end} = $now; delete $dnscache{$_}{log} } (sort @timedout) if @timedout;
# print results
map { # timeout
unless (defined $dnscache{$_}{log}) {
$dnshits{timeouts}{$dnscache{$_}{list}}++;
show_dns ($_) unless $options{noerror};
# a-record
} elsif ($dnscache{$_}{log}) {
$dnshits{hits}{$dnscache{$_}{list}}++;
show_dns ($_);
# nxdomain
} else {
$dnshits{nxdomain}{$dnscache{$_}{list}}++;
show_dns ($_) if $options{verbose};
};
} @lookups;
printf STDOUT "\n # Finished %d lookups (%d items, %d rbls, %d rhsbls, %.1f%% timeouts) after %.2f seconds\n",
($#lookups + 1),
($#queries + 1),
($#rbls + 1), ($#rhsbls + 1),
(($#timedout + 1) / (($#lookups + 1) * 2)) * 100,
(time() - $starttime) unless defined $options{short};
if ($options{verbose} or $options{dnsstats}) {
printf "\n # DNS statistics\n";
if (defined $dnshits{hits}) {
print " #\n";
map { printf STDOUT " # ".$dnshits{hits}{$_}." hits for $_\n" } (sort {($dnshits{hits}{$b} || 0) <=> ($dnshits{hits}{$a} || 0)} keys %{$dnshits{hits}});
};
if (defined $dnshits{timeouts}) {
print " #\n";
map { printf STDOUT " # ".$dnshits{timeouts}{$_}." timeouts for $_\n" } (sort {($dnshits{timeouts}{$b} || 0) <=> ($dnshits{timeouts}{$a} || 0)} keys %{$dnshits{timeouts}});
};
};
print "\n";
};
exit(0);
# prints DNS result
sub show_dns {
my $que = shift;
my $out = "";
if (defined $options{short}) {
$out .= $dnscache{$que}{query}
."; ".$dnscache{$que}{list}
."; ".(join ', ', @{$dnscache{$que}{A}});
$out .= "; ".(join '. ', @{$dnscache{$que}{TXT}}) if defined $dnscache{$que}{TXT} and ($options{verbose} or ($options{short} < 2));
} else {
$out .= "\n ".sprintf ("%15s", $dnscache{$que}{query})." ".$dnscache{$que}{type}.": ".$dnscache{$que}{list};
$out .= " (cname: ".(join ', ', (keys %{$dnscache{$que}{CNAME}})).")" if defined $dnscache{$que}{CNAME};
$out .= "\n ".sprintf ("%15s", $dnscache{$que}{query})." ".(join ', ', @{$dnscache{$que}{A}});
$out .= " (time: ".sprintf ("%.1fs)", ($dnscache{$que}{end} - $dnscache{$que}{start}));
$out .= " (ttl: ".$dnscache{$que}{ttl}."s)" if defined $dnscache{$que}{ttl};
$out .= "\n ".sprintf ("%15s", $dnscache{$que}{query})." ".(join '. ', @{$dnscache{$que}{TXT}}) if defined $dnscache{$que}{TXT};
};
print STDOUT "$out\n";
};
# reads DNS answer
sub rbl_read_dns {
my($myresult) = shift;
my($now) = time();
my($que,$typ) = undef;
if ( defined $myresult ) {
# read question, for dns cache id
foreach ($myresult->question) {
$typ = ($_->qtype || '') unless $typ;
$que = ($_->qname || '') unless $que;
};
# not listed
unless ($myresult->answer) {
@{$dnscache{$que}{A}} = '<nxdomain>';
$dnscache{$que}{end} = $now;
$dnscache{$que}{log} = 0;
# parse answers
} else {
foreach ($myresult->answer) {
if ($_->type =~ /^(A|CNAME|TXT)$/) {
if ($_->type eq 'A') {
push @{$dnscache{$que}{A}}, ($_->address || '');
} elsif ($_->type eq 'TXT') {
my $res = (join(' ', $_->char_str_list()) || '');
push @{$dnscache{$que}{TXT}}, $res if $res;
} elsif ($_->type eq 'CNAME') {
$dnscache{$que}{CNAME}{$_->cname} = 1 if $_->cname;
};
$dnscache{$que}{ttl} = ($_->ttl || 0) unless defined $dnscache{$que}{ttl};
$dnscache{$que}{end} = $now;
$dnscache{$que}{log} = 1;
} else {
print STDERR "IGNORING query: $que, TYPE: '".($_->type || '')."'\n";
};
};
};
};
};