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