Return-Path: Delivered-To: apmail-spamassassin-users-archive@www.apache.org Received: (qmail 72411 invoked from network); 14 Mar 2011 12:41:57 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 14 Mar 2011 12:41:57 -0000 Received: (qmail 67652 invoked by uid 500); 14 Mar 2011 12:41:54 -0000 Delivered-To: apmail-spamassassin-users-archive@spamassassin.apache.org Received: (qmail 67596 invoked by uid 500); 14 Mar 2011 12:41:54 -0000 Mailing-List: contact users-help@spamassassin.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list users@spamassassin.apache.org Received: (qmail 67589 invoked by uid 99); 14 Mar 2011 12:41:54 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 14 Mar 2011 12:41:54 +0000 X-ASF-Spam-Status: No, hits=-0.0 required=10.0 tests=SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (athena.apache.org: local policy) Received: from [62.75.149.26] (HELO vr.theca-tabellaria.de) (62.75.149.26) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 14 Mar 2011 12:41:49 +0000 Received: from uucp by vr.theca-tabellaria.de with local-rmail (Exim 4.73) (envelope-from ) id 1Pz75R-0006AW-QW for users@spamassassin.apache.org; Mon, 14 Mar 2011 13:41:26 +0100 Received: from pyxis.theca-tabellaria.de (localhost [127.0.0.1]) by pyxis.theca-tabellaria.de (8.14.4/8.14.4/Debian/GNU) with ESMTP id p2ECf4WQ023317 for ; Mon, 14 Mar 2011 13:41:04 +0100 Received: from localhost (madires@localhost) by pyxis.theca-tabellaria.de (8.14.4/8.14.4/Submit) with ESMTP id p2ECf4N6023312 for ; Mon, 14 Mar 2011 13:41:04 +0100 Date: Mon, 14 Mar 2011 13:41:04 +0100 (CET) From: Markus Reschke To: users@spamassassin.apache.org Subject: plugin for CIDR matching Message-ID: User-Agent: Alpine 2.00 (DEB 1167 2008-08-23) MIME-Version: 1.0 Content-Type: TEXT/PLAIN; format=flowed; charset=US-ASCII Hi! Currently I'm writing a small SA plugin for checking if IP addresses of relaying MTAs (in the Received: lines) are within a list of defined CIDR blocks. Most admins filter specific CIDR blocks, e.g. from known SPAMming ISPs, at the MTA level. That way all emails from the given CIDR blocks are rejected. But some users like to get those emails too. There could be an important email or one from a potential customer - whatever. My SA plugin can solve that problem by adding a SPAM score to matching emails. The email may be flagged as SPAM but it's received. I've tested the plugin on two servers. Please test it too! You'll need two perl modules (Net::IP::AddrRanges and Net::CIDR) from CPAN. Any (positive :-) feedback is welcome. And now the plugin (docs included): # # Copyright 2011 by Markus Reschke # # <@LICENSE> # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to you under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # =head1 NAME CIDRlight - CIDR check plugin for SpamAssassin =head1 SYNOPSIS loadplugin Mail::SpamAssassin::Plugin::CIDRlight add_cidrlight_blocks [ ...] header eval:check_cidrlight_blocks() =head1 EXAMPLE Example for cf-file: loadplugin Mail::SpamAssassin::Plugin::CIDRlight /etc/spamassasin/CIDRlight.pm add_cidrlight_blocks 10.0.0.0/8 1.2.3.4 add_cidrlight_blocks 192.168.0.0/16 header CIDR eval:check_cidrlight_blocks() describe CIDR CIDRlight: Received from evil network score CIDR 5.0 =head1 DESCRIPTION This plugin checks if an email was sent via a specific network by comparing the IP adresses in the "Received:" lines with defined CIDR blocks. If the IP address matches one of the CIDR blocks in the list, the eval function returns 1. IPv4 and IPv6 are supported. =cut package Mail::SpamAssassin::Plugin::CIDRlight; use Mail::SpamAssassin::Plugin; use Mail::SpamAssassin::Logger; use Net::IP::AddrRanges; use Net::CIDR; use strict; use warnings; use bytes; use re 'taint'; use vars qw(@ISA); @ISA = qw(Mail::SpamAssassin::Plugin); # version our $VERSION = '0.18'; # create empty block list my $blocks = Net::IP::AddrRanges->new(); # # constructor # sub new { #my ($class, $mailsaobject) = @_; my $class = shift; my $mailsaobject = shift; # some boilerplate... $class = ref($class) || $class; my $self = $class->SUPER::new($mailsaobject); bless ($self, $class); # eval rules $self->register_eval_rule ("check_cidrlight_blocks"); info("CIDRlight: plugin enabled."); dbg("CIDRlight: registered Mail::SpamAssassin::Plugin::CIDRlight: $self"); return $self; } # # parse config # sub parse_config { my ($self, $opts) = @_; # add CIDR blocks to local list if ($opts->{key} eq "add_cidrlight_blocks") { foreach my $temp (split(/\s+/, $opts->{value})) { my $value = lc($temp); my $check = ''; $check = Net::CIDR::cidrvalidate($value); if (! defined $check) { dbg("CIDRlight: invalid CIDR block %s.", $value); } else { $blocks->add($value); dbg("CIDRlight: added %s.", $value); } } $self->inhibit_further_callbacks(); return 1; } return 0; } # # and the eval rule itself # sub check_cidrlight_blocks { my ($self, $permsgstatus) = @_; my $received = $permsgstatus->get("Received"); my @recs = split('\n',$received); for (@recs) { my $ip = ''; my $check; # IPv4 if ($_ =~ /.+\[(\d+\.\d+\.\d+\.\d+)\]\)/) { $ip = $1; } # IPv6 link local with zone ID elsif ($_ =~ /.+\[(fe80::.*)%.+\]\)/) { $ip = lc($1); } # IPv6 condensed elsif ($_ =~ /.+\[(.*::.*)\]\)/) { $ip = lc($1); } # IPv6 non condensed elsif ($_ =~ /.+\[(.*:[0-9a-f]{1,4}:.*)\]\)/) { $ip = lc($1); } # check if ($ip) { $check = Net::CIDR::cidrvalidate($ip); if (! defined $check) { dbg("CIDRlight: got invalid IP address from Received line (%s).", $ip); } else { dbg("CIDRlight: got %s from Received line.", $ip); if ($blocks->find($ip)) { dbg("CIDRlight: got block match for %s.", $ip); return 1; } } } } return 0; } 1; PS: I'm working on a more advanced version too (supporting named lists, e.g. individual CIDR block lists for multiple providers/networks). Best regards Markus -- / Markus Reschke \ / madires@theca-tabellaria.de \ / FidoNet 2:244/1661 \ \ / \ / \ /