Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • Note that this is my first SA plugin, so any feedback is welcome
  • This Plugin

ToDo

  • Words are hardcoded. Should be a configuration parameter instead.
  • Instead of checking for specific words, it might be better to "check if the image contains a certain amount of text", since it is not very likely that people send legitimate mail with text in imagescan only be a hint about spam, so don't set the score to high.

– Author: Maarten de Boer, mdeboer at iua dot upf dot edu

Changelog

Version 2:

  • Use convert instead of giftopnm, because I received some mails with .gif's that were actually .jpg's. convert handles that ok.
  • Some words added

Code

...

Lars Uffmann, lu at cachescrubber dot org, converted to a module by Cord Beermann, cord@Wunder-Nett.org

Code

relayed_by_dialup.cf

No Format
loadplugin Ocr OcrRelayedByDialup /etc/spamassassin/relayed_by_dialup.pm
body OCRheader RELAYED_BY_DIALUP        eval:checkrelayed_by_ocrdialup()
describe OCR Check if text in attached images contains spam words
score OCR 3.0

...

RELAYED_BY_DIALUP      Sent directly from dynamic IP address
score RELAYED_BY_DIALUP         1

relayed_by_dialup.pm

No Format
# Ocr plugin, version 2
package Ocr;

use strict;
use Mail::SpamAssassin written by Lars Uffmann <lu -at- cachescrubber -dot- org>
# converted to a SA-module by Cord Beermann <cord@Wunder-Nett.org>
# Licence: same as Spamassassin

package RelayedByDialup;
use Mail::SpamAssassin::Util;
use Mail::SpamAssassin::Plugin;

our @ISA = qw (Mail::SpamAssassin::Plugin);

# constructor: register the eval rule$dbg_text = 'dynamic_relay: ';

sub new {
   my ( $class, $mailsa ) = @_;
   $class = ref($class) || $class;
   my $self = $class->SUPER::new($mailsa);
  bless bless( $self, $class );
   $self->register_eval_rule ("checkrelayed_by_ocrdialup");
   return $self;
}

sub checkrelayed_by_ocrdialup {
   my ( $self, $pms $permsgstatus) = @_;
   my $cnt$match = 0;
  # foreach my $p ( $pms->{msg}->find_parts("image") ) {
      my ( $ctype, $boundary, $charset, $name ) =
    we need the reveived header from _our_ first MX.
  # we can only match this if we have at least 1 untrusted header
  Mail::SpamAssassin::Plugin::dbg("dynamic_relay: starting");
  unless ($permsgstatus->{num_relays_untrusted} > 0) {
    Mail::SpamAssassin::UtilPlugin::parse_content_type(
  dbg("dynamic_relay: num_relays_untrusted undefined");
    return 0;
  } else {
    my $relay = $p$permsgstatus->get_header('content-type') )>{relays_untrusted}->[0];
    Mail::SpamAssassin::Plugin::dbg ("dynamic_relay: mx=" . $relay->{by});
      if ( $ctype$relay->{no_reverse_dns} || $relay->{rdns} eq "image/gif"'' ) {
      Mail::SpamAssassin::Plugin::dbg($dbg_text . "cannot openperform, OCR,no "|/usr/bin/convert -flatten - pnm:-|/usr/bin/gocr -i - > /tmp/spamassassin.ocr.$$"rDNS");
      return 0;
    }

     foreach $p ( $p->decode() if (_is_dynamic_ip($relay->{ip}, $relay->{rdns})) {
      Mail::SpamAssassin::Plugin::dbg($dbg_text . "match: " . $relay->{ip} . print"=" OCR $p. $relay->{rdns});
      return 1;
    }
    Mail::SpamAssassin::Plugin::dbg($dbg_text . "tried: " . $relay->{ip} . "=" close OCR. $relay->{rdns});
    return 0;
  }
}

sub  open OCR, "/tmp/spamassassin.ocr.$$";
         my @words =
           ( 'company', 'money', 'stock', 'million', 'thousand', 'buy', 'price', 'don\'t' );
         while (<OCR>) {
            my $w;
            foreach $w (@words) {
               if (m/$w/i) {
                  $cnt++;
               }
            }
         }
         unlink "/tmp/spamassassin.ocr.$$";
      }
   }
   return ( $cnt > 1 );
}

1;
_is_dynamic_ip {
  my @ip = split(/\./, $_[0]);
  my $name = $_[1];
  return 0 unless ($name);
  # convert addresses in hex to dotted decimal notation.
  $name =~ s/\b([a-f0-9]{8})\b/join(".", unpack("C*", pack("H8", $1)))/eg;
  # try shorter suffixes of $IP
  return 1 if (_is_rr_dynamic_ip(join(".",@ip), $name));
  shift(@ip);
  return 1 if (_is_rr_dynamic_ip(join(".",@ip), $name));
  return 0;
  # will lead to false positives ...
  shift(@ip);
  return 1 if (_is_rr_dynamic_ip(join(".",@ip), $name));
  return 0;
}

sub _is_rr_dynamic_ip {
  my $ip = $_[0];
  my $is_ip = $_[1];
  my $is_ip_sum = 0;
  my $ip_test_sum = 0;
  # remove anything but digits > 0
  $is_ip =~ s/[^1-9]//g;
  # normalize the ip
  my $ip_test = $ip;
  # remove anything but digits > 0
  $ip_test =~ s/[0.]//g;
  my $diff = length($is_ip) - length($ip_test);
  return 0 if ($diff < 0);
  my $offset = 0;
  map { $ip_test_sum += $_ } split("", $ip_test);
  do {
    $is_ip_sum = 0;
    my $test = substr($is_ip, $offset++, length($ip_test));
    map { $is_ip_sum += $_ } split("", $test);
    return 1 if (($is_ip_sum == $ip_test_sum));
  } while ($diff--);
  return 0;
}

1;

...

CategoryRules