File: require_resolvable_fromhost

package info (click to toggle)
qpsmtpd 0.40-3
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 1,024 kB
  • ctags: 393
  • sloc: perl: 6,462; sh: 383; makefile: 54
file content (135 lines) | stat: -rw-r--r-- 3,566 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use Qpsmtpd::DSN;
use Net::DNS qw(mx);
use Socket;
use Net::IP qw(:PROC);
use Qpsmtpd::TcpServer;

my %invalid = ();
my $has_ipv6 = Qpsmtpd::TcpServer::has_ipv6();

sub hook_mail {
  my ($self, $transaction, $sender, %param) = @_;

  return DECLINED
        if ($self->qp->connection->notes('whitelistclient'));

  foreach my $i ($self->qp->config("invalid_resolvable_fromhost")) {
    $i =~ s/^\s*//;
    $i =~ s/\s*$//;
    if ($i =~ m#^((\d{1,3}\.){3}\d{1,3})/(\d\d?)#) {
      $invalid{$1} = $3;
    }
  }

  if ($sender ne "<>" 
      and $self->qp->config("require_resolvable_fromhost")
      and !$self->check_dns($sender->host)) {
    if ($sender->host) {
      # default of temp_resolver_failed is DENYSOFT
      return Qpsmtpd::DSN->temp_resolver_failed("Could not resolve " . $sender->host);
    } 
    else {
      # default of addr_bad_from_system is DENY, we use DENYSOFT here to
      # get the same behaviour as without Qpsmtpd::DSN...
      return Qpsmtpd::DSN->addr_bad_from_system(DENYSOFT, 
                               "FQDN required in the envelope sender");
    }
  }
  return DECLINED;

}

sub check_dns {
  my ($self, $host) = @_;
  my @host_answers;

  # for stuff where we can't even parse a hostname out of the address
  return 0 unless $host;

  return 1 if $host =~ m/^\[(\d{1,3}\.){3}\d{1,3}\]$/;

  my $res = new Net::DNS::Resolver;
  $res->tcp_timeout(30);
  $res->udp_timeout(30);
  my @mx = mx($res, $host);
  foreach my $mx (@mx) {
    return mx_valid($self, $mx->exchange, $host);
  }
  my $query = $res->search($host);
  if ($query) {
    foreach my $rrA ($query->answer) {
      push(@host_answers, $rrA);
    }
  }
  if ($has_ipv6) {
    my $query = $res->search($host, 'AAAA');
    if ($query) {
      foreach my $rrAAAA ($query->answer) {
        push(@host_answers, $rrAAAA);
      }
    }
  } 
  if (@host_answers) {
    foreach my $rr (@host_answers) {
      return is_valid($rr->address) if $rr->type eq "A" or $rr->type eq "AAAA";
      return mx_valid($self, $rr->exchange, $host) if $rr->type eq "MX";
    }
  }
  else {
    $self->log(LOGWARN, "$$ query for $host failed: ", $res->errorstring)
      unless $res->errorstring eq "NXDOMAIN";
  }
  return 0;
}

sub is_valid {
  my $ip = shift;
  my ($net,$mask);
  ### while (($net,$mask) = each %invalid) {
  ###         ... does NOT reset to beginning, will start on
  ###         2nd invocation after where it denied the first time..., so
  ###         2nd time the same "MAIL FROM" would be accepted!
  foreach $net (keys %invalid) {
    $mask = $invalid{$net};
    $mask = pack "B32", "1"x($mask)."0"x(32-$mask);
    return 0 
      if join(".", unpack("C4", inet_aton($ip) & $mask)) eq $net;
  }
  return 1; 
}

sub mx_valid {
  my ($self, $name, $host) = @_;
  my $res   = new Net::DNS::Resolver;
  # IP in MX
  return is_valid($name) if ip_is_ipv4($name) or ip_is_ipv6($name);

  my @mx_answers;
  my $query = $res->search($name, 'A');
  if ($query) {
    foreach my $rrA ($query->answer) {
      push(@mx_answers, $rrA);
    }
  }
  if ($has_ipv6) {
    my $query = $res->search($name, 'AAAA');
    if ($query) {
      foreach my $rrAAAA ($query->answer) {
        push(@mx_answers, $rrAAAA);
      }
    }
  }
  if (@mx_answers) {
    foreach my $rr (@mx_answers) {
      next unless $rr->type eq "A" or $rr->type eq "AAAA";
      return is_valid($rr->address);
    }
  }
  else {
    $self->log(LOGWARN, "$$ query for $host failed: ", $res->errorstring)
      unless $res->errorstring eq "NXDOMAIN";
  }
  return 0;
}

# vim: ts=2 sw=2 expandtab syn=perl