Detecting hosts with dynamic addresses (was Re: [Mimedefang] Another silly idea)

John Rudd john at rudd.cc
Wed May 3 11:41:25 EDT 2006


On May 3, 2006, at 5:43 AM, David F. Skoll wrote:

> For what it's worth, this is my code to detect a likely-looking dynamic
> IP address, based on the PTR record.
>

and, here's my filter_sender (where I do my similar checks) ...



sub filter_sender {
    my ($sender, $ip, $hostname, $helo) = @_;
    my ($iaddr, $name, $aliases, $addrtype, $length, @addrs, $addr, 
$tmp);
    my $found = 0;

    if ($sender =~ /\.local$/) {
       # no .local senders, even from my own IP addrs or authenticated 
senders
       # CGP does weird things with .local addresses

       md_syslog('warning', "Rejecting .local sender address $sender");
       return ('REJECT', "We do not accept .local sender addresses");
       }

    elsif (authenticated_user() || exempt_ip_addr($ip)) {
       # exempt certain IP addrs or authenticated senders from further 
checks

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

    elsif ($helo =~ /(\@|\.)rudd\.cc\>?$/i) {
       # if it got here, then it's not one of my ip addrs, so reject
       # it if it claims a helo/ehlo of *.rudd.cc

       md_syslog('warning', "Rejecting forged helo address $helo $ip 
$hostname");
       return ('REJECT', "You're not actually in the rudd.cc domain");
       }

    elsif ($sender =~ /(\@|\.)rudd\.cc\>?$/i) {
       # if they really are from my domain, they'd be caught by my 
exemptions
       # or use my webmail system if they can't use SMTP-AUTH that moment

       md_syslog ('warning', "Rejecting forged local sender $sender 
$ip");
       return ('REJECT', "You're not actually from the rudd.cc domain");
       }

    elsif ( ($sender =~ /^\<?noreply\@/i) &&
            ($sender !~ /(\@|\.)freshmeat\.net\>?$/i)  &&  # freshmeat 
uses it
            ($sender !~ /(\@|\.)adc\.apple\.com\>?$/i) &&  # apple adc 
uses it
            ($sender !~ /(\@|\.)google\.com\>?$/i) ) {     # google uses 
it
       # noreply@ seems to mostly be used by spammers these days
       # with just a couple known exceptions

       md_syslog ('warning', "Rejecting noreply sender $sender $ip");
       return ('REJECT', "We don't accept noreply senders");
       }

    elsif ($hostname ne "[$ip]") {
       # assume that $hostname was created from a rDNS check of $ip
       # so, we don't need to do that again with the gethostbyaddr
       # what we're doing is checking that $hostname isn't forged
       # by being sure that the @addrs it returns has $ip in it
       # (and, again, my own blocks or authentic senders are exempted 
above)

       ($name, $aliases, $addrtype, $length, @addrs) = 
gethostbyname($hostname);

       unless (defined ($name)) {
          md_syslog('warning',
                    "Tempfail can't verify reverse DNS for $hostname 
$ip");
          return ('TEMPFAIL',
                  "Can't verify that your reverse DNS matches your 
forward DNS" .
                     " - either your PTR record returns something that 
doesn't" .
                     " exist, or DNS for it is timing out - try again 
later");
          }

       foreach $addr (@addrs) {
          $tmp = join (".",  unpack('C4', $addr));
          if ($tmp eq $ip) {
             $found = 1;
             }
          }

       if (! ($found) ) { # we got an answer, it doesn't include $ip
          # ! $found should mean that the rDNS is forged to some other 
hostname

          md_syslog('warning', "Forged rDNS - $hostname doesn't return 
$ip");
          md_syslog('warning', "rDNS mismatch for $ip $name $hostname");
          return ('REJECT',
                  "Forged DNS: reverse DNS doesn't match forward DNS " .
                      "- fix your PTR and A records.");
          }
       else {
          # verified their reverse DNS matches, now lets do some 
checking.

          # IP address components that might be in the hostname:
          my ($a, $b, $c, $d) = split(/\./, $ip);
          # decimal encoded IP address that might be in the hostname:
          my $e = ($a * 256 * 256 * 256) + ($b * 256 * 256) + ($c * 256) 
+ $d;
          # hexadecimal encoded versions of IP address components:
          my $f = lc(sprintf("%x", $a));
          my $g = lc(sprintf("%x", $b));
          my $h = lc(sprintf("%x", $c));
          my $i = lc(sprintf("%x", $d));
          my $j = lc(sprintf("%x", $e)); # should be redundant to 
"$f$g$h$i"

          $hostname = lc($hostname); # instead of lots of //i matches

          if ($hostname =~ /\.kr$/) {
             # korean hostnames only seem to send me spam
             md_syslog('warning', "Blocking .kr relay $hostname");
             return ('REJECT', "We don't accept email from .kr 
hostnames");
             }
          elsif ($hostname =~ /\.mx$/) {
             # mexican hostnames only seem to send me spam
             md_syslog('warning', "Blocking .mx relay $hostname");
             return ('REJECT', "We don't accept email from .mx 
hostnames");
             }
          elsif ( ($hostname =~ /(catv|cable|dsl|ddns|dhcp)/   ) ||
                  ($hostname =~ /(dial-?up|dynamic|$e|$j)/     ) ||
                  ($hostname =~ /($a.?0*$b|$b.?0*$c|$c.?0*$d)/ ) ||
                  ($hostname =~ /($d.?0*$c|$c.?0*$b|$b.?0*$a)/ ) ||
                  ($hostname =~ /($f.?0*$g|$g.?0*$h|$h.?0*$i)/ ) ||
                  ($hostname =~ /($i.?0*$h|$h.?0*$g|$g.?0*$f)/ ) ) {
             # The IP has a few key words in its hostname, or it has 
components
             # of its IP address in its hostname, then its probably an 
ISP
             # assigned dynamic or dial-up host, and should either get 
specific
             # DNS to avoid looking like a client, or use its ISP's mail 
server
             md_syslog('warning', "Blocking end-client host $hostname 
$ip");
             return ('REJECT',
                     "ISP clients IPs should use their ISPs mail 
server");
             }
          else { # otherwise, accept it
             return ('CONTINUE', "ok");
             }
          }
       }

    elsif ($hostname eq "[$ip]") {
       # everyone else better have valid rDNS, or use an ISP whose 
customer
       # relay that has valid rDNS.  Tempfail in case it was a  transient
       # DNS error.

       md_syslog('warning', "Tempfail for no reverse DNS $ip");
       return ('TEMPFAIL',
               "You don't have valid reverse DNS - get a PTR record");
       }

    # we shouldn't get here ... the last two if conditions should cover
    # _every_ message that wasn't caught by the first 5, so, tempfail
    # if we get here
    return ('TEMPFAIL', "This shouldn't happen.");
    }






More information about the MIMEDefang mailing list