]> Frank Brehm's Git Trees - my-stuff/greylist.git/commitdiff
Neu gemacht
authorFrank Brehm <frank@brehm-online.com>
Tue, 1 Apr 2008 09:14:48 +0000 (09:14 +0000)
committerFrank Brehm <frank@brehm-online.com>
Tue, 1 Apr 2008 09:14:48 +0000 (09:14 +0000)
git-svn-id: http://svn.brehm-online.com/svn/my-stuff/greylist@9 ec8d2aa5-1599-4edb-8739-2b3a1bc399aa

greylist-test.0.txt [new file with mode: 0644]
greylist-test.1.txt [new file with mode: 0644]
greylist.pl

diff --git a/greylist-test.0.txt b/greylist-test.0.txt
new file mode 100644 (file)
index 0000000..67c339b
--- /dev/null
@@ -0,0 +1,15 @@
+request=smtpd_access_policy
+protocol_state=RCPT
+protocol_name=SMTP
+helo_name=ich
+queue_id=8045F2AB23
+sender=saeger@acwain.com
+recipient=frank@brehm-online.com
+client_address=77.6.103.140
+client_name=acwain.net
+instance=123.456.7
+sasl_method=plain
+sasl_username=you
+sasl_sender=
+size=12345
+
diff --git a/greylist-test.1.txt b/greylist-test.1.txt
new file mode 100644 (file)
index 0000000..46acb7f
--- /dev/null
@@ -0,0 +1,24 @@
+cert_fingerprint=
+sasl_method=
+sasl_sender=
+size=0
+helo_name=[190.65.153.50]
+reverse_client_name=unknown
+queue_id=
+encryption_cipher=
+encryption_protocol=
+etrn_domain=
+ccert_subject=
+request=smtpd_access_policy
+protocol_state=RCPT
+recipient=frank@brehm-online.com
+sasl_username=
+instance=1b88.47f145ed.dc854.0
+protocol_name=ESMTP
+encryption_keysize=0
+recipient_count=0
+ccert_issuer=
+sender=sebirock@web.de
+client_name=unknown
+client_address=190.65.153.50
+
index 931e1470d4cac7ac62d6cb7a16c684361fc52974..2a0343da80d85d9640081e04b9f25976caedd9e7 100755 (executable)
@@ -8,101 +8,196 @@ use warnings;
 use DBI;
 use Config::General;
 use Sys::Syslog qw(:DEFAULT setlogsock);
+use File::Basename;
+use Getopt::Long;
+use Pod::Usage;
+use Carp qw(cluck);
 
-#
-# Usage: greylist.pl
-#
-# Delegated Postfix SMTPD policy server. This server implements
-# greylisting. State is kept in a Mysql database.  Logging is
-# sent to syslogd.
-#
-# How it works: each time a Postfix SMTP server process is started
-# it connects to the policy service socket, and Postfix runs one
-# instance of this PERL script.  By default, a Postfix SMTP server
-# process terminates after 100 seconds of idle time, or after serving
-# 100 clients. Thus, the cost of starting this PERL script is smoothed
-# out over time.
-#
-# To run this from /etc/postfix/master.cf:
-#
-#    policy  unix  -       n       n       -       -       spawn
-#      user=nobody argv=/usr/bin/perl /usr/libexec/postfix/greylist.pl
-#
-# To use this from Postfix SMTPD, use in /etc/postfix/main.cf:
-#
-#    smtpd_recipient_restrictions =
-#      ...
-#      reject_unauth_destination
-#      check_policy_service unix:private/policy
-#      ...
-#
-# NOTE: specify check_policy_service AFTER reject_unauth_destination
-# or else your system can become an open relay.
-#
-# To test this script by hand, execute:
-#
-#    % perl greylist.pl
-#
-# Each query is a bunch of attributes. Order does not matter, and
-# the demo script uses only a few of all the attributes shown below:
-#
-#    request=smtpd_access_policy
-#    protocol_state=RCPT
-#    protocol_name=SMTP
-#    helo_name=some.domain.tld
-#    queue_id=8045F2AB23
-#    sender=foo@bar.tld
-#    recipient=bar@foo.tld
-#    client_address=1.2.3.4
-#    client_name=another.domain.tld
-#    instance=123.456.7
-#    sasl_method=plain
-#    sasl_username=you
-#    sasl_sender=
-#    size=12345
-#    [empty line]
-#
-# The policy server script will answer in the same style, with an
-# attribute list followed by a empty line:
-#
-#    action=dunno
-#    [empty line]
-#
-#   database format
-#   create table grey ( address       varchar( 15 )           not null,
-#                       sender        varchar( 200)           not null,
-#                       recipient     varchar(200)            not null,
-#                       count         int unsigned  default 1 not null,
-#                       request_time  timestamp               not null,
-#                       expire_time   timestamp               not null,
-#                       primary key   ( address, sender, recipient ) );
-# 
-#   create table white ( address      varchar( 15 )           not null,
-#                        mask         varchar( 15 )           not null,
-#                        count        int unsgined  default 0 not null,
-#                        descr        text                              );
-#   create table black ( address      varchar( 15 )           not null,
-#                        mask         varchar( 15 )           not null,
-#                        count        int unsgined  default 0 not null,
-#                        descr        text                              );
+=head1 NAME
+
+greylist.pl
+
+=head1 DESCRIPTION
+
+Delegated Postfix SMTPD policy server. This server implements greylisting.
+State is kept in a Mysql database. Logging is sent to syslogd.
+
+=head1 SYNOPSIS
+
+ greylist.pl [-c <configfile>] [-v[ -v...]|-D <debuglevel>]
+ greylist.pl -h
+ greylist.pl -V
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-c <configfile>> - Konfigurationsdatei
+
+Wenn nicht angegeben, wird C</etc/postfix/greylist/greylist.cf> verwendet.
+
+=item B<-v> Verbose-Level (Debug-Level)
+
+Wird durch Mehrfach-Aufzaehlung erhoeht.
+
+=item B<-D level> - Debug-Level
 
+Numerische Angabe des Debug-Levels.
+
+I<Hinweise>:
+
+Die Parameter C<-v> und C<-D> wirken sich gleich aus.
+Wenn beide angegeben werden, wird der hoehere von beiden verwendet.
+
+=item B<-h|-?>
+
+Gibt diesen Hilfebildschirm aus und beendet sich.
+
+=item B<-V>
+
+Gibt die Versionsnummer dieses Programms aus und beendet sich.
+
+=back
+
+=head1 DESCRIPTION
+
+How it works: each time a Postfix SMTP server process is started
+it connects to the policy service socket, and Postfix runs one
+instance of this PERL script.  By default, a Postfix SMTP server
+process terminates after 100 seconds of idle time, or after serving
+100 clients. Thus, the cost of starting this PERL script is smoothed
+out over time.
+
+To run this from /etc/postfix/master.cf:
+
+   policy  unix  -       n       n       -       -       spawn
+     user=nobody argv=/usr/bin/perl /usr/libexec/postfix/greylist.pl
+
+To use this from Postfix SMTPD, use in /etc/postfix/main.cf:
+
+   smtpd_recipient_restrictions =
+       ...
+       reject_unauth_destination
+       check_policy_service unix:private/policy
+       ...
+
+NOTE: specify check_policy_service AFTER reject_unauth_destination
+or else your system can become an open relay.
+
+To test this script by hand, execute:
+
+   % perl greylist.pl
+
+Each query is a bunch of attributes. Order does not matter, and
+the demo script uses only a few of all the attributes shown below:
+
+   request=smtpd_access_policy
+   protocol_state=RCPT
+   protocol_name=SMTP
+   helo_name=some.domain.tld
+   queue_id=8045F2AB23
+   sender=foo@bar.tld
+   recipient=bar@foo.tld
+   client_address=1.2.3.4
+   client_name=another.domain.tld
+   instance=123.456.7
+   sasl_method=plain
+   sasl_username=you
+   sasl_sender=
+   size=12345
+   [empty line]
+
+The policy server script will answer in the same style, with an
+attribute list followed by a empty line:
+
+   action=dunno
+   [empty line]
+
+  database format
+  create table grey ( address       varchar( 15 )           not null,
+                      sender        varchar( 200)           not null,
+                      recipient     varchar(200)            not null,
+                      count         int unsigned  default 1 not null,
+                      request_time  timestamp               not null,
+                      expire_time   timestamp               not null,
+                      primary key   ( address, sender, recipient ) );
+
+  create table white ( address      varchar( 15 )           not null,
+                       mask         varchar( 15 )           not null,
+                       count        int unsgined  default 0 not null,
+                       descr        text                              );
+  create table black ( address      varchar( 15 )           not null,
+                       mask         varchar( 15 )           not null,
+                       count        int unsgined  default 0 not null,
+                       descr        text                              );
+
+=cut
+
+my $Revis = <<'ENDE';
+  $Revision$
+ENDE
+$Revis =~ s/^.*:\s*(\S+)\s*\$.*/$1/s;
+our $VERSION = "1.0." . $Revis;
 
 ######################
 ## Global Variables
 ######################
-my ( $dbh, $config );
+my ( $dbh, $configfile );
 ######################
 
 # read configuration
-($config) = $0 =~ m#^(.+\.)pl$#;
-$config .= "cf";
-#
+$configfile = dirname($0) . "/greylist.cf";
+
+$| = 1;
+
+Getopt::Long::Configure('bundling');
+
+my ( $test_mode, $help, $show_version, $cmdline_verbose, $DebugLevel, );
+
+unless (
+    GetOptions(
+        "configfile|conf|c=s"  => \$configfile,
+        "test|t"               => \$test_mode,
+        "help|h|?"             => \$help,
+        "verbose|v+"           => \$cmdline_verbose,
+        "version|V"            => \$show_version,
+        "DebugLevel|Debug|D=i" => \$DebugLevel,
+    )
+    )
 {
-  my $conf;
-  $conf = new Config::General($config);
-  $config = { $conf->getall() };
+    pod2usage( { -exitval => 1, -verbose => 1 } );
+} ## end unless ( GetOptions( "configfile|conf|c=s" => ...
+
+$cmdline_verbose ||= 0;
+$cmdline_verbose = $DebugLevel if $DebugLevel and $DebugLevel > $cmdline_verbose;
+
+my $verbose = $cmdline_verbose;
+
+if ($help) {
+    $verbose ? pod2usage( -exitstatus => 1, -verbose => 2 ) : pod2usage(1);
 }
 
+if ($show_version) {
+    print "Version $0: " . $VERSION . "\n";
+    print "Version BsDaemon::Common: " . $BsDaemon::Common::VERSION . "\n";
+    print "\n";
+    exit 0;
+}
+
+die "Konfiguration '" . $configfile . "' nicht gefunden.\n" unless -f $configfile;
+
+#
+my $config;
+debug( "reading config file '%s'", $configfile );
+eval {
+    my $conf;
+    $conf   = new Config::General($configfile);
+    $config = { $conf->getall() };
+};
+die "Konnte Konfiguration '" . $configfile . "' nicht lesen: " . $@ . "\n" if $@;
+
+$verbose = 1 if $config->{'verbose'};
+
 $0 = 'postfix/greylist';
 
 #### main ####
@@ -116,34 +211,36 @@ openlog $0, $config->{'syslog'}{'options'}, $config->{'syslog'}{'facility'};
 #
 # Unbuffer standard output.
 #
-select((select(STDOUT), $| = 1)[0]);
+select( ( select(STDOUT), $| = 1 )[0] );
 
 #
 # Receive a bunch of attributes, evaluate the policy, send the result.
 #
 {
-  my ( $attr, $action );
-  while (<STDIN>) {
-    if (/([^=]+)=(.*)\n/) {
-      $attr->{substr($1, 0, 512)} = substr($2, 0, 512);
-    } elsif ($_ eq "\n") {
-      if ($config->{'verbose'}) {
-        foreach (keys %$attr) {
-          syslog $config->{'syslog'}{'priority'}, 'Attribute: %s=%s', $_, $attr->{$_};
+    my ( $attr, $action );
+    while (<STDIN>) {
+        if (/([^=]+)=(.*)\n/) {
+            $attr->{ substr( $1, 0, 512 ) } = substr( $2, 0, 512 );
         }
-      }
-      fatal_exit( "unrecognized request type: '%s'", $attr->{'request'} )
-        unless $attr->{'request'} eq 'smtpd_access_policy';
-      $action = smtpd_access_policy($attr);
-      syslog $config->{'syslog'}{'priority'}, 'Action: %s', $action if $config->{'verbose'};
-      print STDOUT "action=$action\n\n";
-      $attr = {};
-    } else {
-      chop;
-      syslog $config->{'syslog'}{'priority'}, 'warning: ignoring garbage: %.100s', $_;
-    }
-  }
-  $dbh->disconnect() if $dbh;
+        elsif ( $_ eq "\n" ) {
+            if ($verbose) {
+                foreach ( sort { lc($a) cmp lc($b) } keys %$attr ) {
+                    debug( "Attribute: %s=%s", $_, $attr->{$_} );
+                }
+            }
+            fatal_exit( "unrecognized request type: '%s'", $attr->{'request'} )
+                unless $attr->{'request'} eq 'smtpd_access_policy';
+            $action = smtpd_access_policy($attr);
+            debug( "Action: %s", $action );
+            print STDOUT "action=$action\n\n";
+            $attr = {};
+        } ## end elsif ( $_ eq "\n" )
+        else {
+            chop;
+            do_log( "warning: ignoring garbage: %.100s", $_ );
+        }
+    } ## end while (<STDIN>)
+    $dbh->disconnect() if $dbh;
 }
 
 #########################################
@@ -155,31 +252,33 @@ select((select(STDOUT), $| = 1)[0]);
 # table.  Request attributes are available via the %attr hash.
 #
 sub smtpd_access_policy {
-    my ( $param ) = @_;
+    my ($param) = @_;
     my $age;
+
     #my ( $time_stamp, $now, $key );
 
     # Open the database on the fly.
     open_database() unless $dbh;
 
     # first look up the whitelist
-    if ( $config->{'database'}{'whitelist_table'} && db_read_whitelist( $param ) ) {
-        syslog $config->{'syslog'}{'priority'}, 'request address in whitelist';
+    if ( $config->{'database'}{'whitelist_table'} && db_read_whitelist($param) ) {
+        do_log('request address in whitelist');
         if ( $config->{'greylist'}{'whitelist_header_text'} ) {
             return 'PREPEND X-GreyList: ' . $config->{'greylist'}{'whitelist_header_text'};
-        } else {
+        }
+        else {
             return 'dunno';
         }
     }
-    if ( $config->{'database'}{'blacklist_table'} && db_read_blacklist( $param ) ) {
-        syslog $config->{'syslog'}{'priority'}, 'request address in blacklist';
-        return 'defer_if_permit ' . ( $config->{'greylist'}{'blacklist_reason'} || 'Service is unavailable');
+    if ( $config->{'database'}{'blacklist_table'} && db_read_blacklist($param) ) {
+        do_log('request address in blacklist');
+        return 'defer_if_permit ' . ( $config->{'greylist'}{'blacklist_reason'} || 'Service is unavailable' );
     }
 
     # timestamps will be generated within the database
     # Lookup the time stamp for this client/sender/recipient.
-    $age = db_read_greylist( $param );
-    unless ( $age ) {  # expired or unseen
+    $age = db_read_greylist($param);
+    unless ($age) {    # expired or unseen
         db_add_greylist($param);
     }
 
@@ -193,22 +292,50 @@ sub smtpd_access_policy {
     # In case of failure, specify ``DEFER_IF_PERMIT optional text...''
     # so that mail can still be blocked by other access restrictions.
     #
-    syslog $config->{'syslog'}{'priority'}, 'request age %d', $age;
+    do_log( "request age %d", $age );
     if ( $age > $config->{'greylist'}{'delay'} ) {
         return "dunno";
-    } else {
+    }
+    else {
         return 'defer_if_permit ' . ( $config->{'greylist'}{'delay_message'} || 'Service is unavailable' );
     }
-}
+
+} ## end sub smtpd_access_policy
 
 #
 # Log an error and abort.
 #
 sub fatal_exit {
     my $first = shift;
-    syslog 'err', 'fatal: '.$first, @_;
+    if ($test_mode) {
+        cluck 'fatal: ' . sprintf( $first, @_ ) . "\n";
+    }
+    else {
+        syslog 'err', 'fatal: ' . $first, @_;
+    }
     $dbh->disconnect() if $dbh;
     exit 1;
+} ## end sub fatal_exit
+
+#
+# Log a debug message
+#
+sub debug {
+    return unless $verbose;
+    do_log(@_);
+}
+
+#
+# Log a message
+#
+sub do_log {
+    my $first = shift;
+    if ($test_mode) {
+        warn sprintf( $first, @_ ) . "\n";
+    }
+    else {
+        syslog $config->{'syslog'}{'priority'}, $first, @_;
+    }
 }
 
 #
@@ -216,105 +343,111 @@ sub fatal_exit {
 #
 sub open_database {
     $dbh = DBI->connect(
-        "DBI:".$config->{'database'}{'db_type'}.':'.$config->{'database'}{'db_name'},
+        "DBI:" . $config->{'database'}{'db_type'} . ':' . $config->{'database'}{'db_name'},
         $config->{'database'}{'db_user'},
         $config->{'database'}{'db_pass'},
         { AutoCommit => 0, RaiseError => 0, PrintError => 0 }
     );
     fatal_exit( "Cannot open database %s: %s", $config->{'database'}{'db_name'}, $DBI::errstr ) unless $dbh;
-    syslog $config->{'syslog'}{'priority'}, "open db %s", $config->{'database'}{'db_name'} if $config->{'verbose'};
-}
+    debug( "open db %s", $config->{'database'}{'db_name'} );
+} ## end sub open_database
 
 #
 # Read database. Use a lock to avoid reading the database
-# while it is being changed. 
+# while it is being changed.
 #
 sub db_read_greylist {
     my $param = shift;
     my ( $sqlst, $sth, $val, $tofast );
 
-    $dbh->do( 'lock table '.$config->{'database'}{'greylist_table'}. ' write' ) or fatal_exit( "Cannot lock table: %s", $dbh->errstr );
+    $dbh->do( 'lock table ' . $config->{'database'}{'greylist_table'} . ' write' ) or fatal_exit( "Cannot lock table: %s", $dbh->errstr );
+
     # lc $attr{"client_address"}."/".$attr{"sender"}."/".$attr{"recipient"};
-    $sqlst = q( select unix_timestamp(now())-unix_timestamp(request_time) age from ).$config->{'database'}{'greylist_table'} .
-             q( where address = ? and sender = ? and recipient = ? and expire_time > now() );
-    $sth = $dbh->prepare( $sqlst ) or fatal_exit( 'Cannot prepare statement: %s with error: %s', $sqlst, $dbh->errstr );
+    $sqlst
+        = q( select unix_timestamp(now())-unix_timestamp(request_time) age from )
+        . $config->{'database'}{'greylist_table'}
+        . q( where address = ? and sender = ? and recipient = ? and expire_time > now() );
+    $sth = $dbh->prepare($sqlst) or fatal_exit( 'Cannot prepare statement: %s with error: %s', $sqlst, $dbh->errstr );
     $sth->execute( @$param{qw( client_address sender recipient )} ) or fatal_exit( 'Cannot execute: %s', $dbh->errstr );
 
-    $val = ($sth->fetchrow_hashref()||{})->{'age'};
+    $val = ( $sth->fetchrow_hashref() || {} )->{'age'};
     $sth->finish();
-    
-    if ( $val ) {
-      $tofast = $val < $config->{'greylist'}{'delay'};
-      # generate new expire time 
-      $sqlst = 'update '.$config->{'database'}{'greylist_table'} . ' set ' .
-        ($tofast?'':'request_time = request_time, ') .  # keep old time if old enough
-        'expire_time = date_add( now(), interval ? second ), count = count + 1 where address = ? and sender = ? and recipient = ?';
-      $dbh->do( $sqlst, {}, $config->{'greylist'}{$tofast?'expire_short':'expire_long'}, 
-                            @$param{qw( client_address sender recipient )} ) or
-        fatal_exit( "Cannot update record: %s", $dbh->errstr );
-    } else {
-      $val = 0; # prevent undef
+
+    if ($val) {
+        $tofast = $val < $config->{'greylist'}{'delay'};
+
+        # generate new expire time
+        $sqlst
+            = 'update '
+            . $config->{'database'}{'greylist_table'} . ' set '
+            . ( $tofast ? '' : 'request_time = request_time, ' )
+            .    # keep old time if old enough
+            'expire_time = date_add( now(), interval ? second ), count = count + 1 where address = ? and sender = ? and recipient = ?';
+        unless ($test_mode) {
+            $dbh->do(
+                $sqlst, {},
+                $config->{'greylist'}{ $tofast ? 'expire_short' : 'expire_long' },
+                @$param{qw( client_address sender recipient )}
+            ) or fatal_exit( "Cannot update record: %s", $dbh->errstr );
+        }
+    } ## end if ($val)
+    else {
+        $val = 0;    # prevent undef
     }
 
-    syslog $config->{'syslog'}{'priority'}, 
-        'lookup %s: %s', join('/', @$param{qw( client_address sender recipient )} ), $val if $config->{'verbose'};
+    do_log( 'lookup %s: %s', join( '/', @$param{qw( client_address sender recipient )} ), $val );
     $dbh->do("unlock tables");
     return $val;
-}
+} ## end sub db_read_greylist
 
 # add a new requester to db
 sub db_add_greylist {
     my $param = shift;
-    my ( $sqlst );
+    my ($sqlst);
 
     # replace already existing record because its expired anyway
-    $sqlst = 'replace into '.$config->{'database'}{'greylist_table'} .
-             ' (address, sender, recipient, expire_time ) values ( ?, ?, ?, date_add( now(), interval ? second ) ) ';
-    $dbh->do( $sqlst, {}, @$param{qw( client_address sender recipient )}, $config->{'greylist'}{'expire_short'} ) or
-       fatal_exit( 'Cannot insert record: %s', $dbh->errstr );
-}
+    $sqlst
+        = 'replace into '
+        . $config->{'database'}{'greylist_table'}
+        . ' (address, sender, recipient, expire_time ) values ( ?, ?, ?, date_add( now(), interval ? second ) ) ';
+    unless ($test_mode) {
+        $dbh->do( $sqlst, {}, @$param{qw( client_address sender recipient )}, $config->{'greylist'}{'expire_short'} )
+            or fatal_exit( 'Cannot insert record: %s', $dbh->errstr );
+    }
+} ## end sub db_add_greylist
 
 # read whitelist table to avoid greylisting
 sub db_read_whitelist {
-  my $param = shift;
-  open_database() unless $dbh;
+    my $param = shift;
+    open_database() unless $dbh;
 
-  my ( $sqlst, $res );
+    my ( $sqlst, $res );
 
-  $sqlst = 'update ' . $config->{'database'}{'whitelist_table'} . ' set count = count + 1' .
-           ' where INET_ATON( address ) & INET_ATON( mask ) = INET_ATON( ? ) & INET_ATON( mask )';
-  $res = $dbh->do( $sqlst, {}, $param->{'client_address'} ) or 
-      fatal_exit( 'Cannot update whitelist, statement is: %s with error %s', $sqlst,  $dbh->errstr );
-  return $res + 0; # transform number of rows to bool
-}
+    $sqlst
+        = 'update '
+        . $config->{'database'}{'whitelist_table'}
+        . ' set count = count + 1'
+        . ' where INET_ATON( address ) & INET_ATON( mask ) = INET_ATON( ? ) & INET_ATON( mask )';
+    $res = $dbh->do( $sqlst, {}, $param->{'client_address'} )
+        or fatal_exit( 'Cannot update whitelist, statement is: %s with error %s', $sqlst, $dbh->errstr );
+    return $res + 0;    # transform number of rows to bool
+} ## end sub db_read_whitelist
 
 # read blacklist table to never let mail through
 sub db_read_blacklist {
-  my $param = shift;
-  open_database() unless $dbh;
+    my $param = shift;
+    open_database() unless $dbh;
 
-  my ( $sqlst, $res );
+    my ( $sqlst, $res );
 
-  $sqlst = 'update ' . $config->{'database'}{'blacklist_table'} . ' set count = count + 1' .
-           ' where INET_ATON( address ) & INET_ATON( mask ) = INET_ATON( ? ) & INET_ATON( mask ) limit 1';
-  $res = $dbh->do( $sqlst, {}, $param->{'client_address'} ) or 
-      fatal_exit( 'Cannot prepare statement: %s with error %s', $sqlst,  $dbh->errstr );
-  return $res + 0; # false if no records where updated
-}
+    $sqlst
+        = 'update '
+        . $config->{'database'}{'blacklist_table'}
+        . ' set count = count + 1'
+        . ' where INET_ATON( address ) & INET_ATON( mask ) = INET_ATON( ? ) & INET_ATON( mask ) limit 1';
+    $res = $dbh->do( $sqlst, {}, $param->{'client_address'} )
+        or fatal_exit( 'Cannot prepare statement: %s with error %s', $sqlst, $dbh->errstr );
+    return $res + 0;    # false if no records where updated
+} ## end sub db_read_blacklist
 __END__
 
-#
-# We don't need getopt() for now.
-#
-# maybe later add a switch for config file
-#
-while ($option = shift(@ARGV)) {
-    if ($option eq "-v") {
-       $verbose = 1;
-    } else {
-       syslog $syslog_priority, "Invalid option: %s. Usage: %s [-v]",
-               $option, $0;
-       exit 1;
-    }
-}
-