line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Copyright (c) 1999-2003 by Ilya Martynov. All rights |
2
|
|
|
|
|
|
|
# reserved. |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# This program is free software; you can redistribute it and/or modify |
5
|
|
|
|
|
|
|
# it under the same terms as Perl itself. |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
package Mail::CheckUser; |
8
|
|
|
|
|
|
|
|
9
|
5
|
|
|
5
|
|
9077
|
use strict; |
|
5
|
|
|
|
|
12
|
|
|
5
|
|
|
|
|
198
|
|
10
|
5
|
|
|
5
|
|
22
|
use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS $VERSION); |
|
5
|
|
|
|
|
8
|
|
|
5
|
|
|
|
|
765
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
require Exporter; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
@EXPORT_OK = qw(check_email |
17
|
|
|
|
|
|
|
last_check |
18
|
|
|
|
|
|
|
check_hostname |
19
|
|
|
|
|
|
|
check_username); |
20
|
|
|
|
|
|
|
$EXPORT_TAGS{constants} = [qw(CU_OK |
21
|
|
|
|
|
|
|
CU_BAD_SYNTAX |
22
|
|
|
|
|
|
|
CU_UNKNOWN_DOMAIN |
23
|
|
|
|
|
|
|
CU_DNS_TIMEOUT |
24
|
|
|
|
|
|
|
CU_UNKNOWN_USER |
25
|
|
|
|
|
|
|
CU_SMTP_TIMEOUT |
26
|
|
|
|
|
|
|
CU_SMTP_UNREACHABLE |
27
|
|
|
|
|
|
|
CU_MAILBOX_FULL |
28
|
|
|
|
|
|
|
CU_TRY_AGAIN)]; |
29
|
|
|
|
|
|
|
push @EXPORT_OK, @{$EXPORT_TAGS{constants}}; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
$VERSION = '1.22'; |
32
|
|
|
|
|
|
|
|
33
|
5
|
|
|
5
|
|
27
|
use Carp; |
|
5
|
|
|
|
|
16
|
|
|
5
|
|
|
|
|
393
|
|
34
|
5
|
|
|
5
|
|
3603
|
use Net::DNS; |
|
5
|
|
|
|
|
457531
|
|
|
5
|
|
|
|
|
507
|
|
35
|
5
|
|
|
5
|
|
3236
|
use Net::SMTP; |
|
5
|
|
|
|
|
63111
|
|
|
5
|
|
|
|
|
264
|
|
36
|
5
|
|
|
5
|
|
38
|
use IO::Handle; |
|
5
|
|
|
|
|
7
|
|
|
5
|
|
|
|
|
224
|
|
37
|
|
|
|
|
|
|
|
38
|
5
|
|
|
|
|
1277
|
use vars qw($Skip_Network_Checks $Skip_SMTP_Checks |
39
|
|
|
|
|
|
|
$Skip_SYN $Net_DNS_Resolver |
40
|
|
|
|
|
|
|
$NXDOMAIN |
41
|
|
|
|
|
|
|
$Timeout $Treat_Timeout_As_Fail $Debug |
42
|
|
|
|
|
|
|
$Treat_Full_As_Fail |
43
|
|
|
|
|
|
|
$Treat_Grey_As_Fail |
44
|
5
|
|
|
5
|
|
22
|
$Sender_Addr $Helo_Domain $Last_Check); |
|
5
|
|
|
|
|
8
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# if it is true Mail::CheckUser doesn't make network checks |
47
|
|
|
|
|
|
|
$Skip_Network_Checks = 0; |
48
|
|
|
|
|
|
|
# if it is true Mail::CheckUser doesn't try to connect to mail |
49
|
|
|
|
|
|
|
# server to check if user is valid |
50
|
|
|
|
|
|
|
$Skip_SMTP_Checks = 0; |
51
|
|
|
|
|
|
|
# timeout in seconds for network checks |
52
|
|
|
|
|
|
|
$Timeout = 60; |
53
|
|
|
|
|
|
|
# if it is true the Net::Ping SYN/ACK check will be skipped |
54
|
|
|
|
|
|
|
$Skip_SYN = 0; |
55
|
|
|
|
|
|
|
# if it is true Mail::CheckUser treats timeouted checks as |
56
|
|
|
|
|
|
|
# failed checks |
57
|
|
|
|
|
|
|
$Treat_Timeout_As_Fail = 0; |
58
|
|
|
|
|
|
|
# if it is true Mail::CheckUser treats mailbox full message |
59
|
|
|
|
|
|
|
# as failed checks |
60
|
|
|
|
|
|
|
$Treat_Full_As_Fail = 0; |
61
|
|
|
|
|
|
|
# sender addr used in MAIL/RCPT check |
62
|
|
|
|
|
|
|
$Sender_Addr = 'check@user.com'; |
63
|
|
|
|
|
|
|
# sender domain used in HELO SMTP command - if undef lets |
64
|
|
|
|
|
|
|
# Net::SMTP use its default value |
65
|
|
|
|
|
|
|
$Helo_Domain = undef; |
66
|
|
|
|
|
|
|
# Default Net::DNS::Resolver override object |
67
|
|
|
|
|
|
|
$Net_DNS_Resolver = undef; |
68
|
|
|
|
|
|
|
# if true then enable debug mode |
69
|
|
|
|
|
|
|
$Debug = 0; |
70
|
|
|
|
|
|
|
# Wildcard gTLD always denote bogus domains |
71
|
|
|
|
|
|
|
# (http://www.imperialviolet.org/dnsfix.html) |
72
|
|
|
|
|
|
|
## gTLD Wildcard IPs |
73
|
|
|
|
|
|
|
$NXDOMAIN = { |
74
|
|
|
|
|
|
|
# com/net |
75
|
|
|
|
|
|
|
"64.94.110.11" => 1, # A |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# ac |
78
|
|
|
|
|
|
|
"194.205.62.122" => 1, # A |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# cc |
81
|
|
|
|
|
|
|
"206.253.214.102" => 1, # A |
82
|
|
|
|
|
|
|
"snubby.enic.cc" => 1, # MX |
83
|
|
|
|
|
|
|
"206.191.159.103" => 1, # MX |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# cx |
86
|
|
|
|
|
|
|
"219.88.106.80" => 1, # A |
87
|
|
|
|
|
|
|
"mail.nonregistered.nic.cx" => 1, # MX |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# mp |
90
|
|
|
|
|
|
|
"202.128.12.163" => 1, # A |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# museum |
93
|
|
|
|
|
|
|
"195.7.77.20" => 1, # A |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# nu |
96
|
|
|
|
|
|
|
"64.55.105.9" => 1, # A |
97
|
|
|
|
|
|
|
"212.181.91.6" => 1, # A |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# ph |
100
|
|
|
|
|
|
|
"203.119.4.6" => 1, # A |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# pw |
103
|
|
|
|
|
|
|
"216.98.141.250" => 1, # A |
104
|
|
|
|
|
|
|
"65.125.231.178" => 1, # A |
105
|
|
|
|
|
|
|
"wfb.dnsvr.com" => 1, # CNAME |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
# sh |
108
|
|
|
|
|
|
|
"194.205.62.62" => 1, # A |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# td |
111
|
|
|
|
|
|
|
"146.101.245.154" => 1, # A |
112
|
|
|
|
|
|
|
"www.nic.td" => 1, # CNAME |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# tk |
115
|
|
|
|
|
|
|
"195.20.32.83" => 1, # A |
116
|
|
|
|
|
|
|
"195.20.32.86" => 1, # A |
117
|
|
|
|
|
|
|
"nukumatau.taloha.com" => 1, # MX |
118
|
|
|
|
|
|
|
"195.20.32.99" => 1, # MX |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# tm |
121
|
|
|
|
|
|
|
"194.205.62.42" => 1, # A |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# tw |
124
|
|
|
|
|
|
|
"203.73.24.11" => 1, # A |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# ws |
127
|
|
|
|
|
|
|
"216.35.187.246" => 1, # A |
128
|
|
|
|
|
|
|
"mail.worldsite.ws" => 1, # MX |
129
|
|
|
|
|
|
|
"216.35.187.251" => 1, # MX |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
}; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# check_email EMAIL |
134
|
|
|
|
|
|
|
sub check_email( $ ); |
135
|
|
|
|
|
|
|
# last_check |
136
|
|
|
|
|
|
|
sub last_check( ); |
137
|
|
|
|
|
|
|
# check_hostname_syntax HOSTNAME |
138
|
|
|
|
|
|
|
sub check_hostname_syntax( $ ); |
139
|
|
|
|
|
|
|
# check_username_syntax USERNAME |
140
|
|
|
|
|
|
|
sub check_username_syntax( $ ); |
141
|
|
|
|
|
|
|
# check_network HOSTNAME, USERNAME |
142
|
|
|
|
|
|
|
sub check_network( $$ ); |
143
|
|
|
|
|
|
|
# check_user_on_host MSERVER, USERNAME, HOSTNAME, TIMEOUT |
144
|
|
|
|
|
|
|
sub check_user_on_host( $$$$ ); |
145
|
|
|
|
|
|
|
# _calc_timeout FULL_TIMEOUT START_TIME |
146
|
|
|
|
|
|
|
sub _calc_timeout( $$ ); |
147
|
|
|
|
|
|
|
# _pm_log LOG_STR |
148
|
|
|
|
|
|
|
sub _pm_log( $ ); |
149
|
|
|
|
|
|
|
# _result RESULT, REASON |
150
|
|
|
|
|
|
|
sub _result( $$ ); |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# check result codes |
153
|
5
|
|
|
5
|
|
32
|
use constant CU_OK => 0; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
407
|
|
154
|
5
|
|
|
5
|
|
21
|
use constant CU_BAD_SYNTAX => 1; |
|
5
|
|
|
|
|
7
|
|
|
5
|
|
|
|
|
242
|
|
155
|
5
|
|
|
5
|
|
62
|
use constant CU_UNKNOWN_DOMAIN => 2; |
|
5
|
|
|
|
|
8
|
|
|
5
|
|
|
|
|
215
|
|
156
|
5
|
|
|
5
|
|
20
|
use constant CU_DNS_TIMEOUT => 3; |
|
5
|
|
|
|
|
5
|
|
|
5
|
|
|
|
|
201
|
|
157
|
5
|
|
|
5
|
|
17
|
use constant CU_UNKNOWN_USER => 4; |
|
5
|
|
|
|
|
7
|
|
|
5
|
|
|
|
|
196
|
|
158
|
5
|
|
|
5
|
|
18
|
use constant CU_SMTP_TIMEOUT => 5; |
|
5
|
|
|
|
|
5
|
|
|
5
|
|
|
|
|
189
|
|
159
|
5
|
|
|
5
|
|
28
|
use constant CU_SMTP_UNREACHABLE => 6; |
|
5
|
|
|
|
|
7
|
|
|
5
|
|
|
|
|
199
|
|
160
|
5
|
|
|
5
|
|
18
|
use constant CU_MAILBOX_FULL => 7; |
|
5
|
|
|
|
|
7
|
|
|
5
|
|
|
|
|
204
|
|
161
|
5
|
|
|
5
|
|
18
|
use constant CU_TRY_AGAIN => 8; |
|
5
|
|
|
|
|
5
|
|
|
5
|
|
|
|
|
11972
|
|
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub check_email($) { |
164
|
53
|
|
|
53
|
1
|
44764
|
my($email) = @_; |
165
|
|
|
|
|
|
|
|
166
|
53
|
50
|
|
|
|
222
|
unless(defined $email) { |
167
|
0
|
|
|
|
|
0
|
croak __PACKAGE__ . "::check_email: \$email is undefined"; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
53
|
|
|
|
|
259
|
_pm_log '=' x 40; |
171
|
53
|
|
|
|
|
205
|
_pm_log "check_email: checking \"$email\""; |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# split email address on username and hostname |
174
|
53
|
|
|
|
|
399
|
my($username, $hostname) = $email =~ /^(.*)@(.*)$/; |
175
|
|
|
|
|
|
|
# return false if it impossible |
176
|
53
|
100
|
|
|
|
166
|
unless(defined $hostname) { |
177
|
2
|
|
|
|
|
6
|
return _result(CU_BAD_SYNTAX, 'bad address format: missing @'); |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
51
|
|
|
|
|
98
|
my $ok = 1; |
181
|
51
|
|
66
|
|
|
234
|
$ok &&= check_hostname_syntax $hostname; |
182
|
51
|
|
100
|
|
|
216
|
$ok &&= check_username_syntax $username; |
183
|
51
|
100
|
|
|
|
196
|
if($Skip_Network_Checks) { |
|
|
50
|
|
|
|
|
|
184
|
28
|
|
|
|
|
62
|
_pm_log "check_email: skipping network checks"; |
185
|
|
|
|
|
|
|
} elsif ($ok) { |
186
|
23
|
|
66
|
|
|
93
|
$ok &&= check_network $hostname, $username; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
51
|
|
|
|
|
901
|
return $ok; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub last_check() { |
193
|
6
|
|
|
6
|
1
|
86
|
return $Mail::CheckUser::Last_Check; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# build hostname regexp |
197
|
|
|
|
|
|
|
# NOTE: it doesn't strictly follow RFC822 |
198
|
|
|
|
|
|
|
# because of what registrars now allow. |
199
|
|
|
|
|
|
|
my $DOMAIN_RE = qr/(?:[\da-zA-Z]+ -+)* [\da-zA-Z]+/x; |
200
|
|
|
|
|
|
|
my $HOSTNAME_RE = qr/^ (?:$DOMAIN_RE \.)+ [a-zA-Z]+ $/xo; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub check_hostname_syntax($) { |
203
|
51
|
|
|
51
|
0
|
86
|
my($hostname) = @_; |
204
|
|
|
|
|
|
|
|
205
|
51
|
|
|
|
|
178
|
_pm_log "check_hostname_syntax: checking \"$hostname\""; |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# check if hostname syntax is correct |
208
|
51
|
100
|
|
|
|
574
|
if($hostname =~ $HOSTNAME_RE) { |
209
|
43
|
|
|
|
|
116
|
return _result(CU_OK, 'correct hostname syntax'); |
210
|
|
|
|
|
|
|
} else { |
211
|
8
|
|
|
|
|
16
|
return _result(CU_BAD_SYNTAX, 'bad hostname syntax'); |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# build username regexp |
216
|
|
|
|
|
|
|
# NOTE: it doesn't strictly follow RFC821 |
217
|
|
|
|
|
|
|
my $STRING_RE = ('[' . quotemeta(join '', |
218
|
|
|
|
|
|
|
grep(!/[<>()\[\]\\\.,;:\@"]/, # ["], UnBug Emacs |
219
|
|
|
|
|
|
|
map chr, 33 .. 126)) . ']'); |
220
|
|
|
|
|
|
|
my $USERNAME_RE = qr/^ (?:$STRING_RE+ \.)* $STRING_RE+ $/xo; |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub check_username_syntax($) { |
224
|
43
|
|
|
43
|
0
|
72
|
my($username) = @_; |
225
|
|
|
|
|
|
|
|
226
|
43
|
|
|
|
|
156
|
_pm_log "check_username_syntax: checking \"$username\""; |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# check if username syntax is correct |
229
|
43
|
100
|
|
|
|
309
|
if($username =~ $USERNAME_RE) { |
230
|
35
|
|
|
|
|
76
|
return _result(CU_OK, 'correct username syntax'); |
231
|
|
|
|
|
|
|
} else { |
232
|
8
|
|
|
|
|
32
|
return _result(CU_BAD_SYNTAX, 'bad username syntax'); |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub check_network($$) { |
237
|
23
|
|
|
23
|
0
|
44
|
my($hostname, $username) = @_; |
238
|
|
|
|
|
|
|
|
239
|
23
|
|
|
|
|
95
|
_pm_log "check_network: checking \"$username\" on \"$hostname\""; |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# list of mail servers for hostname |
242
|
23
|
|
|
|
|
50
|
my @mservers = (); |
243
|
|
|
|
|
|
|
|
244
|
23
|
|
|
|
|
46
|
my $timeout = $Timeout; |
245
|
23
|
|
|
|
|
39
|
my $start_time = time; |
246
|
|
|
|
|
|
|
|
247
|
23
|
|
33
|
|
|
333
|
my $resolver = $Mail::CheckUser::Net_DNS_Resolver || new Net::DNS::Resolver; |
248
|
23
|
|
|
|
|
2533
|
my $tout = _calc_timeout($timeout, $start_time); |
249
|
23
|
50
|
|
|
|
90
|
return _result(CU_DNS_TIMEOUT, 'DNS timeout') if $tout == 0; |
250
|
23
|
|
|
|
|
144
|
$resolver->udp_timeout($tout); |
251
|
|
|
|
|
|
|
|
252
|
23
|
|
|
|
|
390
|
my @mx = mx($resolver, "$hostname."); |
253
|
23
|
|
|
|
|
2596424
|
$tout = _calc_timeout($timeout, $start_time); |
254
|
23
|
50
|
|
|
|
121
|
return _result(CU_DNS_TIMEOUT, 'DNS timeout') if $tout == 0; |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# check result of query |
257
|
23
|
100
|
|
|
|
79
|
if(@mx) { |
258
|
|
|
|
|
|
|
# if MX record exists, |
259
|
|
|
|
|
|
|
# then it's already sorted by preference |
260
|
10
|
|
|
|
|
44
|
@mservers = map {$_->exchange} @mx; |
|
24
|
|
|
|
|
997
|
|
261
|
|
|
|
|
|
|
} else { |
262
|
|
|
|
|
|
|
# if there is no MX record try hostname as mail server |
263
|
13
|
|
|
|
|
36
|
my $tout = _calc_timeout($timeout, $start_time); |
264
|
13
|
50
|
|
|
|
61
|
return _result(CU_DNS_TIMEOUT, 'DNS timeout') if $tout == 0; |
265
|
13
|
|
|
|
|
95
|
$resolver->udp_timeout($tout); |
266
|
|
|
|
|
|
|
|
267
|
13
|
|
|
|
|
261
|
my $res = $resolver->search("$hostname.", 'A'); |
268
|
|
|
|
|
|
|
# check if timeout has happen |
269
|
13
|
|
|
|
|
385052
|
$tout = _calc_timeout($timeout, $start_time); |
270
|
13
|
50
|
|
|
|
50
|
return _result(CU_DNS_TIMEOUT, 'DNS timeout') if $tout == 0; |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# check result of query |
273
|
13
|
100
|
|
|
|
39
|
if($res) { |
274
|
1
|
|
|
|
|
3
|
@mservers = ($hostname); |
275
|
1
|
|
|
|
|
2
|
my $ip; |
276
|
1
|
|
|
|
|
5
|
foreach my $rr ($res->answer) { |
277
|
1
|
50
|
|
|
|
15
|
if ($rr->type eq "A") { |
|
|
0
|
|
|
|
|
|
278
|
1
|
|
|
|
|
13
|
$ip = $rr->address; |
279
|
1
|
|
|
|
|
10
|
last; |
280
|
|
|
|
|
|
|
} elsif ($rr->type eq "CNAME") { |
281
|
0
|
|
|
|
|
0
|
$ip = $rr->cname; |
282
|
|
|
|
|
|
|
} else { |
283
|
|
|
|
|
|
|
# Should never happen! |
284
|
0
|
|
|
|
|
0
|
$ip = ""; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
} |
287
|
1
|
|
|
|
|
5
|
_pm_log "check_network: \"$ip\" Wildcard gTLD check"; |
288
|
1
|
50
|
|
|
|
11
|
return _result(CU_UNKNOWN_DOMAIN, 'Wildcard gTLD') if $NXDOMAIN->{lc $ip}; |
289
|
|
|
|
|
|
|
} else { |
290
|
12
|
|
|
|
|
63
|
return _result(CU_UNKNOWN_DOMAIN, 'DNS failure: ' . $resolver->errorstring); |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
11
|
|
|
|
|
424
|
foreach my $mserver (@mservers) { |
295
|
25
|
|
|
|
|
117
|
_pm_log "check_network: \"$mserver\" Wildcard gTLD check"; |
296
|
25
|
50
|
|
|
|
188
|
return _result(CU_UNKNOWN_DOMAIN, 'Wildcard gTLD') if $NXDOMAIN->{lc $mserver}; |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
|
299
|
11
|
100
|
|
|
|
47
|
if($Skip_SMTP_Checks) { |
300
|
5
|
|
|
|
|
21
|
return _result(CU_OK, 'skipping SMTP checks'); |
301
|
|
|
|
|
|
|
} else { |
302
|
6
|
50
|
|
|
|
38
|
if ($Skip_SYN) { |
303
|
|
|
|
|
|
|
# Skip SYN/ACK check. |
304
|
|
|
|
|
|
|
# Just check user on each mail server one at a time. |
305
|
0
|
|
|
|
|
0
|
foreach my $mserver (@mservers) { |
306
|
0
|
|
|
|
|
0
|
my $tout = _calc_timeout($timeout, $start_time); |
307
|
0
|
0
|
|
|
|
0
|
if ($mserver !~ /^\d+\.\d+\.\d+\.\d+$/) { |
308
|
|
|
|
|
|
|
# Resolve it to an IP |
309
|
0
|
0
|
|
|
|
0
|
return _result(CU_DNS_TIMEOUT, 'DNS timeout') if $tout == 0; |
310
|
0
|
|
|
|
|
0
|
$resolver->udp_timeout($tout); |
311
|
0
|
0
|
|
|
|
0
|
if (my $ans = $resolver->query($mserver)) { |
312
|
0
|
|
|
|
|
0
|
foreach my $rr_a ($ans->answer) { |
313
|
0
|
0
|
|
|
|
0
|
if ($rr_a->type eq "A") { |
314
|
0
|
|
|
|
|
0
|
$mserver = $rr_a->address; |
315
|
0
|
|
|
|
|
0
|
last; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
} |
319
|
0
|
|
|
|
|
0
|
$tout = _calc_timeout($timeout, $start_time); |
320
|
|
|
|
|
|
|
} |
321
|
0
|
0
|
|
|
|
0
|
return _result(CU_SMTP_TIMEOUT, 'SMTP timeout') if $tout == 0; |
322
|
|
|
|
|
|
|
|
323
|
0
|
|
|
|
|
0
|
my $res = check_user_on_host $mserver, $username, $hostname, $tout; |
324
|
|
|
|
|
|
|
|
325
|
0
|
0
|
|
|
|
0
|
return 1 if $res == 1; |
326
|
0
|
0
|
|
|
|
0
|
return 0 if $res == 0; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
} else { |
329
|
|
|
|
|
|
|
# Determine which mail servers are on |
330
|
6
|
|
|
|
|
17
|
my $resolve = {}; |
331
|
6
|
|
|
|
|
17
|
my $tout = _calc_timeout($timeout, $start_time); |
332
|
6
|
|
|
|
|
19
|
foreach my $mserver (@mservers) { |
333
|
|
|
|
|
|
|
# All mservers need to be resolved to IPs before the SYN check |
334
|
13
|
50
|
|
|
|
170
|
if ($mserver =~ /^\d+\.\d+\.\d+\.\d+$/) { |
335
|
0
|
|
|
|
|
0
|
$resolve->{$mserver} = 1; |
336
|
|
|
|
|
|
|
} else { |
337
|
13
|
|
|
|
|
75
|
_pm_log "check_network: \"$mserver\" resolving"; |
338
|
13
|
50
|
|
|
|
47
|
return _result(CU_DNS_TIMEOUT, 'DNS timeout') if $tout == 0; |
339
|
13
|
|
|
|
|
62
|
$resolver->udp_timeout($tout); |
340
|
13
|
100
|
|
|
|
168
|
if (my $ans = $resolver->query($mserver)) { |
341
|
12
|
|
|
|
|
229882
|
foreach my $rr_a ($ans->answer) { |
342
|
12
|
50
|
|
|
|
167
|
if ($rr_a->type eq "A") { |
343
|
12
|
|
|
|
|
193
|
$mserver = $rr_a->address; |
344
|
12
|
|
|
|
|
176
|
$resolve->{$mserver} = 1; |
345
|
12
|
|
|
|
|
61
|
_pm_log "check_network: resolved to IP \"$mserver\""; |
346
|
12
|
|
|
|
|
30
|
last; |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
} else { |
350
|
1
|
|
|
|
|
12057
|
_pm_log "check_network: \"$mserver\" host not found!"; |
351
|
|
|
|
|
|
|
} |
352
|
13
|
|
|
|
|
87
|
$tout = _calc_timeout($timeout, $start_time); |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
6
|
|
|
|
|
1699
|
require Net::Ping; |
357
|
6
|
|
|
|
|
29587
|
import Net::Ping 2.24; |
358
|
|
|
|
|
|
|
# Use only three-fourths of the full timeout for lookups |
359
|
|
|
|
|
|
|
# in order to leave time to actually speak to the server. |
360
|
6
|
|
|
|
|
29
|
my $ping = Net::Ping->new("syn", _calc_timeout($timeout, $start_time) * 3 / 4 + 1); |
361
|
6
|
|
|
|
|
2199
|
$ping->{port_num} = getservbyname("smtp", "tcp"); |
362
|
6
|
|
|
|
|
53
|
$ping->tcp_service_check(1); |
363
|
6
|
|
|
|
|
94
|
foreach my $mserver (@mservers) { |
364
|
13
|
|
|
|
|
52
|
_pm_log "check_network: \"$mserver\" sending SYN..."; |
365
|
|
|
|
|
|
|
# untaint before passing to Net::Ping |
366
|
13
|
|
|
|
|
104
|
my ($tainted) = $mserver =~ /(\d+\.\d+\.\d+\.\d+)/; |
367
|
13
|
100
|
66
|
|
|
201
|
if ($tainted and $tainted eq $mserver and |
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
368
|
|
|
|
|
|
|
$resolve->{$tainted} and $ping->ping($tainted)) { |
369
|
12
|
|
|
|
|
3636
|
_pm_log "check_network: \"$tainted\" SYN packet sent."; |
370
|
|
|
|
|
|
|
} else { |
371
|
1
|
|
|
|
|
10
|
_pm_log "check_network: \"$mserver\" host not found!"; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
} |
374
|
6
|
|
|
|
|
19
|
foreach my $mserver (@mservers) { |
375
|
9
|
|
|
|
|
49
|
my $tout = _calc_timeout($timeout, $start_time); |
376
|
9
|
50
|
|
|
|
37
|
return _result(CU_SMTP_TIMEOUT, 'SMTP timeout') if $tout == 0; |
377
|
|
|
|
|
|
|
|
378
|
9
|
|
|
|
|
37
|
_pm_log "check_network: \"$mserver\" waiting for ACK"; |
379
|
9
|
100
|
|
|
|
50
|
if ($resolve->{$mserver}) { |
380
|
|
|
|
|
|
|
# untaint before passing to Net::Ping |
381
|
8
|
|
|
|
|
84
|
my($mserver) = $mserver =~ /(\d+\.\d+\.\d+\.\d+)/; |
382
|
8
|
50
|
|
|
|
56
|
if ($ping->ack($mserver)) { |
383
|
8
|
|
|
|
|
332569
|
_pm_log "check_network: \"$mserver\" ACK received."; |
384
|
|
|
|
|
|
|
# check user on this mail server |
385
|
8
|
|
|
|
|
47
|
my $res = check_user_on_host $mserver, $username, $hostname, $tout; |
386
|
|
|
|
|
|
|
|
387
|
8
|
100
|
|
|
|
1120
|
return 1 if $res == 1; |
388
|
7
|
100
|
|
|
|
135
|
return 0 if $res == 0; |
389
|
|
|
|
|
|
|
} else { |
390
|
0
|
|
0
|
|
|
0
|
_pm_log "check_network: \"$mserver\" no ACK received: [". |
391
|
|
|
|
|
|
|
($ping->nack($mserver) || "no SYN sent")."]"; |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
} else { |
394
|
1
|
|
|
|
|
6
|
_pm_log "check_network: skipping check_user_on_host \"$mserver\" since it did not resolve"; |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
|
399
|
2
|
|
|
|
|
62
|
return _result(CU_SMTP_UNREACHABLE, |
400
|
|
|
|
|
|
|
'Cannot connect SMTP servers: ' . |
401
|
|
|
|
|
|
|
join(', ', @mservers)); |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# it should be impossible to reach this statement |
405
|
0
|
|
|
|
|
0
|
die "Internal error"; |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
sub check_user_on_host($$$$) { |
409
|
8
|
|
|
8
|
0
|
33
|
my($mserver, $username, $hostname, $timeout) = @_; |
410
|
|
|
|
|
|
|
|
411
|
8
|
|
|
|
|
50
|
_pm_log "check_user_on_host: checking user \"$username\" on \"$mserver\""; |
412
|
|
|
|
|
|
|
|
413
|
8
|
|
|
|
|
22
|
my $start_time = time; |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# disable warnings because Net::SMTP can generate some on timeout |
416
|
|
|
|
|
|
|
# conditions |
417
|
8
|
|
|
|
|
73
|
local $^W = 0; |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# try to connect to mail server |
420
|
8
|
|
|
|
|
32
|
my $tout = _calc_timeout($timeout, $start_time); |
421
|
8
|
50
|
|
|
|
35
|
return _result(CU_SMTP_TIMEOUT, 'SMTP timeout') if $tout == 0; |
422
|
|
|
|
|
|
|
|
423
|
8
|
50
|
|
|
|
45
|
my @hello_params = defined $Helo_Domain ? (Hello => $Helo_Domain) : (); |
424
|
8
|
|
|
|
|
155
|
my $smtp = Net::SMTP->new($mserver, Timeout => $tout, @hello_params); |
425
|
8
|
50
|
|
|
|
4055624
|
unless(defined $smtp) { |
426
|
0
|
|
|
|
|
0
|
_pm_log "check_user_on_host: unable to connect to \"$mserver\""; |
427
|
0
|
|
|
|
|
0
|
return -1; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
# try to check if user is valid with MAIL/RCPT commands |
431
|
8
|
|
|
|
|
55
|
$tout = _calc_timeout($timeout, $start_time); |
432
|
8
|
100
|
|
|
|
38
|
return _result(CU_SMTP_TIMEOUT, 'SMTP timeout') if $tout == 0; |
433
|
7
|
|
|
|
|
75
|
$smtp->timeout($tout); |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
# send MAIL FROM command |
436
|
7
|
100
|
|
|
|
173
|
unless($smtp->mail($Sender_Addr)) { |
437
|
|
|
|
|
|
|
# something wrong? |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
# check for timeout |
440
|
4
|
50
|
|
|
|
444779
|
return _result(CU_SMTP_TIMEOUT, 'SMTP timeout') if $tout == 0; |
441
|
|
|
|
|
|
|
|
442
|
4
|
|
|
|
|
60
|
_pm_log "check_user_on_host: can't say MAIL - " . $smtp->message; |
443
|
4
|
|
|
|
|
54
|
return -1; |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
# send RCPT TO command |
447
|
3
|
50
|
|
|
|
438669
|
if($smtp->to("$username\@$hostname")) { |
448
|
|
|
|
|
|
|
# give server opportunity to exist gracefully by telling it QUIT |
449
|
0
|
|
|
|
|
0
|
my $tout = _calc_timeout($timeout, $start_time); |
450
|
0
|
0
|
|
|
|
0
|
if($tout) { |
451
|
0
|
|
|
|
|
0
|
$smtp->timeout($tout); |
452
|
0
|
|
|
|
|
0
|
$smtp->quit; |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
|
455
|
0
|
|
|
|
|
0
|
return _result(CU_OK, 'SMTP server accepts username'); |
456
|
|
|
|
|
|
|
} else { |
457
|
|
|
|
|
|
|
# check if verify returned error because of timeout |
458
|
3
|
|
|
|
|
665053
|
my $tout = _calc_timeout($timeout, $start_time); |
459
|
3
|
50
|
|
|
|
45
|
return _result(CU_SMTP_TIMEOUT, 'SMTP timeout') if $tout == 0; |
460
|
|
|
|
|
|
|
|
461
|
3
|
|
|
|
|
179
|
my $code = $smtp->code; |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
# give server opportunity to exist gracefully by telling it QUIT |
464
|
3
|
|
|
|
|
192
|
$smtp->timeout($tout); |
465
|
3
|
|
|
|
|
168
|
$smtp->quit; |
466
|
|
|
|
|
|
|
|
467
|
3
|
50
|
33
|
|
|
172920
|
if($code == 550 or $code == 551 or $code == 553) { |
|
|
0
|
33
|
|
|
|
|
|
|
0
|
|
|
|
|
|
468
|
3
|
|
|
|
|
122
|
return _result(CU_UNKNOWN_USER, 'no such user'); |
469
|
|
|
|
|
|
|
} elsif($code == 552) { |
470
|
0
|
|
|
|
|
0
|
return _result(CU_MAILBOX_FULL, 'mailbox full'); |
471
|
|
|
|
|
|
|
} elsif($code =~ /^4/) { |
472
|
0
|
|
|
|
|
0
|
return _result(CU_TRY_AGAIN, 'temporary delivery failure'); |
473
|
|
|
|
|
|
|
} else { |
474
|
0
|
|
|
|
|
0
|
_pm_log "check_user_on_host: unknown error in response"; |
475
|
0
|
|
|
|
|
0
|
return _result(CU_OK, 'unknown error in response'); |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
# it should be impossible to reach this statement |
481
|
0
|
|
|
|
|
0
|
die "Internal error"; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
sub _calc_timeout($$) { |
485
|
125
|
|
|
125
|
|
296
|
my($full_timeout, $start_time) = @_; |
486
|
|
|
|
|
|
|
|
487
|
125
|
|
|
|
|
256
|
my $now_time = time; |
488
|
125
|
|
|
|
|
282
|
my $passed_time = $now_time - $start_time; |
489
|
125
|
|
|
|
|
610
|
_pm_log "_calc_timeout: start - $start_time, now - $now_time"; |
490
|
125
|
|
|
|
|
489
|
_pm_log "_calc_timeout: timeout - $full_timeout, passed - $passed_time"; |
491
|
|
|
|
|
|
|
|
492
|
125
|
|
|
|
|
245
|
my $timeout = $full_timeout - $passed_time; |
493
|
|
|
|
|
|
|
|
494
|
125
|
50
|
|
|
|
372
|
if($timeout < 0) { |
495
|
0
|
|
|
|
|
0
|
return 0; |
496
|
|
|
|
|
|
|
} else { |
497
|
125
|
|
|
|
|
627
|
return $timeout; |
498
|
|
|
|
|
|
|
} |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
sub _pm_log($) { |
502
|
728
|
|
|
728
|
|
1101
|
my($log_str) = @_; |
503
|
|
|
|
|
|
|
|
504
|
728
|
50
|
|
|
|
1988
|
if($Debug) { |
505
|
0
|
|
|
|
|
0
|
print STDERR "$log_str\n"; |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
sub _result($$) { |
510
|
119
|
|
|
119
|
|
258
|
my($code, $reason) = @_; |
511
|
|
|
|
|
|
|
|
512
|
119
|
|
|
|
|
185
|
my $ok = 0; |
513
|
|
|
|
|
|
|
|
514
|
119
|
100
|
|
|
|
329
|
$ok = 1 if $code == CU_OK; |
515
|
119
|
100
|
|
|
|
248
|
$ok = 1 if $code == CU_SMTP_UNREACHABLE; |
516
|
119
|
50
|
33
|
|
|
364
|
$ok = 1 if $code == CU_MAILBOX_FULL and not $Treat_Full_As_Fail; |
517
|
119
|
50
|
33
|
|
|
312
|
$ok = 1 if $code == CU_DNS_TIMEOUT and not $Treat_Timeout_As_Fail; |
518
|
119
|
100
|
66
|
|
|
440
|
$ok = 1 if $code == CU_SMTP_TIMEOUT and not $Treat_Timeout_As_Fail; |
519
|
119
|
50
|
33
|
|
|
262
|
$ok = 1 if $code == CU_TRY_AGAIN and not $Treat_Grey_As_Fail; |
520
|
|
|
|
|
|
|
|
521
|
119
|
|
|
|
|
465
|
$Last_Check = { ok => $ok, |
522
|
|
|
|
|
|
|
code => $code, |
523
|
|
|
|
|
|
|
reason => $reason }; |
524
|
|
|
|
|
|
|
|
525
|
119
|
|
|
|
|
1464
|
my($sub) = (caller(1))[3] =~ /^.*::(.*)$/; |
526
|
|
|
|
|
|
|
|
527
|
119
|
100
|
|
|
|
675
|
_pm_log "$sub: check result is " . |
528
|
|
|
|
|
|
|
($ok ? 'ok' : 'not ok') . |
529
|
|
|
|
|
|
|
": [$code] $reason"; |
530
|
|
|
|
|
|
|
|
531
|
119
|
|
|
|
|
998
|
return $ok; |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
1; |
535
|
|
|
|
|
|
|
__END__ |