HELO.pm 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #!/usr/bin/perl
  2. #
  3. # Helo.pm - description
  4. #
  5. # Copyright (C) 2008 Martin Zobel-Helas
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2, or (at your option)
  10. # any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software Foundation,
  19. # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20. #
  21. # define the Package name
  22. package Gandalf::Checks::HELO;
  23. use warnings;
  24. use strict;
  25. use constant {GOOD => 1, BAD => 0};
  26. use Time::HiRes qw(sleep);
  27. use List::Util qw(sum);
  28. my @tests = (helo_reverse => {function => \&test_helo_reverse,
  29. bad => 1,
  30. good => -0.5,
  31. },
  32. helo_numeric => {function => \&test_helo_numeric,
  33. bad => 1.5,
  34. good => 0,
  35. },
  36. helo_seems_dialup => {function => \&test_helo_seems_dialup,
  37. bad => 3.75,
  38. good => 0
  39. },
  40. );
  41. my %tests = @tests;
  42. # we want @tests[0,2,4,6,...] etc.
  43. my @tests_order = @tests[map {$_ * 2} 0..(@tests/2-1)];
  44. # called by the policy daemon; will need to be renamed and possibly
  45. # reconfigured as needed
  46. sub run {
  47. my ($policy,$config,$variables) = @_;
  48. # go through, and run the tests
  49. my %test_results;
  50. my @tests_to_run = @tests_order;
  51. my $test;
  52. my $loop_number = 0;
  53. my $time_start = time;
  54. while ($test = shift @tests_to_run) {
  55. $loop_number++;
  56. my ($status,$rerun) =
  57. $tests{$test}{function}->(variables => $variables,
  58. policy => $policy,
  59. config => $config,
  60. test_results => \%test_results,
  61. );
  62. if (defined $rerun and $rerun) {
  63. # if we've hit the timeout, and a test hasn't completed,
  64. # bail out.
  65. if ((time - $time_start) > $config->{helo_test_timeout}) {
  66. last;
  67. }
  68. # if we've looped around once and still have tests to run,
  69. # wait.
  70. if (@test_to_run < $loop_number) {
  71. sleep 0.5;
  72. $loop_number = 0;
  73. }
  74. push @tests_to_run,$test;
  75. }
  76. else {
  77. $test_results{$test} = {status => $status,
  78. score => $status == GOOD? $tests{$test}{good}:$tests{$test}{bad},
  79. };
  80. }
  81. }
  82. # calculate results
  83. my $final_score = sum(map {$->{score}} values %test_results);
  84. return $final_score;
  85. }
  86. my @test_common_options = (variables => HASHREF,
  87. test_results => HASHREF,
  88. policy => OBJECT,
  89. config => HASHREF,
  90. noblock => {type => BOOLEAN,
  91. default => 1,
  92. },
  93. );
  94. sub test_helo_reverse {
  95. my %param = validate_with(params => \@_,
  96. spec => {@test_common_options,
  97. },
  98. );
  99. my $helostring = $param{variables}{helo_name};
  100. my $clientaddress = $param{variables}{client_address};
  101. if ($helostring eq $clientaddress) {
  102. # Great, the client told us his correct name!
  103. return GOOD;
  104. } else {
  105. # That didn't match. Let's see.
  106. # try if the helo names resolves, and has perhaps more than one dns
  107. # record....
  108. my $results = resolve_dns(query => $helostring,
  109. noblock => $param{noblock},
  110. );
  111. if ($param{noblock} and not defined $results or
  112. not ref $results) {
  113. return ($results,1);
  114. }
  115. if (first { $_->string() eq $clientaddress } $results->answer()) {
  116. return GOOD;
  117. }
  118. }
  119. # For now, just give "bad" score.
  120. return BAD;
  121. }
  122. sub test_helo_numeric {
  123. my %param = validate_with(params => \@_,
  124. spec => {@test_common_options,
  125. },
  126. );
  127. my $helostring = $param{variables}{helo_name};
  128. if($helostring =~ /\d$/) {
  129. return GOOD;
  130. } else {
  131. return BAD;
  132. }
  133. }
  134. sub test_helo_seems_dialup {
  135. my %param = validate_with(params => \@_,
  136. spec => {@test_common_options,
  137. },
  138. );
  139. my $helostring = $param{variables}{helo_name};
  140. if (($helostring =~
  141. /(\.dip\.|cable|ppp|dial|dsl|dyn|client|rev.*?(ip|home)*).*?\..*?\./i
  142. ) || ($helostring =~
  143. /[a-z\.\-\_]+\d{1,3}[-._]\d{1,3}[-._]\d{1,3}[-._]\d{1,3}/i
  144. )) {
  145. # our client at least behaves correctly, we get a dialup pattern as helo string
  146. return BAD;
  147. }
  148. return GOOD;
  149. }
  150. # return a true value
  151. 1;
  152. __END__