my %badnetworks = (
    '58.71.0.0/17',	'REJECT',
    '62.117.127.0/25',	'REJECT',
    '66.165.224.0/20',	'REJECT',
    '69.72.128.0/17',	'REJECT',
    '69.240.0.0/12',	'REJECT',
    '82.208.169.0/24',	'REJECT',
    '84.13.0.0/17',	'REJECT',
    '198.59.0.0/15',	'REJECT',
    '212.44.242.0/16',	'REJECT',
    '212.145.160.0/21',	'REJECT',
    '212.145.192.0/20',	'REJECT',
    '216.191.0.0/16',	'REJECT',
    '217.165.0.0/21',	'REJECT',
    '217.165.32.0/22',	'REJECT',
    '218.78.0.0/15',	'REJECT',
    '218.80.0.0/14',	'REJECT',
    '220.224.0.0/14',	'REJECT',
    '222.136.0.0/11',	'REJECT',
    # local mail
    '127.0.0.1/32',	'CONTINUE',
    '192.168.1.0/24',	'CONTINUE',
    # wildcard action
    '0.0.0.0/0',	'CONTINUE',
);

# return an int, rather than a packed string
sub Inet_ntoa($) {
    my $num = shift;
    inet_ntoa(pack('N', $num));
}

sub Inet_aton($) {
    my $str = shift;
    unpack('N', inet_aton($str));
}

sub cmpbymask() {
    my ($addra, $lena) = split('/', $a);
    my ($addrb, $lenb) = split('/', $b);

    $lena <=> $lenb;
}

sub filter_relay($$) {
    my ($hostip, $hostname) = @_;

    md_syslog('debug', "$QueueID: relay: $hostip, $hostname");

    my $ip = Inet_aton($hostip);

    # search the hash in order of most specific netmask to least...
    # interim fix until we use Net::CIDR::Lite
    foreach my $lhs (reverse(sort cmpbymask keys(%badnetworks))) {
        my $action = $badnetworks{$lhs};

        my ($net, $len) = split('/', $lhs);

        # md_syslog('debug', "$QueueID: net=$net, len=$len");

        $net = Inet_aton($net);

        my $mask = (0xffffffff << (32 - $len)) & 0xffffffff;

        # check for a match... if so, we return immediately
        if (($ip & $mask) == $net) {
            my $msg = ($action eq 'CONTINUE') ? 'OK'
                       : "This network is blacklisted";

            md_syslog('debug', "$QueueID: match: $hostip " . Inet_ntoa($net) . "/$len");
            md_syslog('info', "$QueueID: relay: $action: $msg");
            return ($action, $msg);
        }
    }

    # we shouldn't hit this, but if we do... default action is to accept
    md_syslog('debug', "$QueueID: no match; defaulting");
    return ('CONTINUE', "OK");
}

sub filter_sender($$$$) {
    my ($sender, $hostip, $hostname, $helo) = @_;

    md_syslog('info', "$QueueID: sender: $hostip, $hostname said \"helo $helo\"");

    # dotted quads need to be bracketed
    if ($helo =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) {
        md_syslog('info', "$QueueID: no brackets: $helo");
        return ('REJECT', "Incorrect format for address-literal");
    }

    # ok, got the format right... now is the address correct?
    # this might be wrong if our clients are behind a NATting gateway;
    # if that's the case, we need to preface this with accepting everyone
    # whose $hostip matches a certain address or address range
    if ($helo =~ /^\[(\d{1,3})\.(\d{1,3}).(\d{1,3})\.(\d{1,3})\]$/) {
        if ($helo ne "[$hostip]") {
            md_syslog('info', "$QueueID: wrong ip: $hostip claims to be $helo");
            return ('REJECT',
                    "Header forgery attempt, [$hostip] claims to be $helo");
        }
    }

    # put this in explicitly, in case the test below is disabled.
    if ($helo eq 'localhost') {
        md_syslog('info', "$QueueID: localhost: $hostip ($hostname)");
        return ('REJECT', "Nothing local about you");
    }

    # doesn't contain any dots
    if (index($helo, '.') == -1) {
        md_syslog('info', "$QueueID: not fqdn: $helo");
        return ('REJECT', "Expected fully-qualified domain name");
    }

    return ('CONTINUE', "OK");
}

