line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package HTTP::ProxyTest; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
6152
|
use 5.006; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
100
|
|
4
|
|
|
|
|
|
|
our $VERSION = '0.11'; |
5
|
|
|
|
|
|
|
# $Id: ProxyTest.pm,v 1.3 2011/08/01 21:03:09 gunnarh Exp $ |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 NAME |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
HTTP::ProxyTest - Reject an HTTP request if passed via an open proxy |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 SYNOPSIS |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use HTTP::ProxyTest; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
proxytest( |
16
|
|
|
|
|
|
|
-nmap => '/usr/local/bin/nmap', |
17
|
|
|
|
|
|
|
-whitelist => '/usr/local/etc/ProxyTest_whitelist', |
18
|
|
|
|
|
|
|
-log => '/var/log/open_proxy.log', |
19
|
|
|
|
|
|
|
); |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 DESCRIPTION |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
Robots that send comment spam are often hidden behind anonymous open |
24
|
|
|
|
|
|
|
proxy servers. You can use C to look for open proxies |
25
|
|
|
|
|
|
|
on-the-fly and prevent such spam robots from submitting their crap. |
26
|
|
|
|
|
|
|
The module is particularly useful if you don't want to bother your |
27
|
|
|
|
|
|
|
web site visitors with CAPTCHAs etc. |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
C tests certain ports of C that are |
30
|
|
|
|
|
|
|
often used for anonymous open proxies, and denies access if an open |
31
|
|
|
|
|
|
|
proxy is found, i.e. it responds with status "403 Forbidden" and |
32
|
|
|
|
|
|
|
exits. The module was designed to make use of the Nmap security |
33
|
|
|
|
|
|
|
scanner (L) in order to speed up things and/or |
34
|
|
|
|
|
|
|
increase the number of ports to be considered for testing. |
35
|
|
|
|
|
|
|
Consequently, if Nmap is currently not available to you, you are |
36
|
|
|
|
|
|
|
advised to download and install that program. |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
The strong point of C, compared to other similar CPAN |
39
|
|
|
|
|
|
|
modules (see L), is its speed. Since Nmap limits the number |
40
|
|
|
|
|
|
|
of ports to test, C can do on-the-fly testing fast |
41
|
|
|
|
|
|
|
enough to cover quite a few proxy port candidates, without causing any |
42
|
|
|
|
|
|
|
significant response delay. The same seems not to be true for other |
43
|
|
|
|
|
|
|
modules. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=head2 Arguments |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Below are the arguments that can be passed the B |
48
|
|
|
|
|
|
|
function, which by the way is the only function of C |
49
|
|
|
|
|
|
|
that you are supposed to call from outside the module. B |
50
|
|
|
|
|
|
|
takes hash style key=Evalue arguments (see L). |
51
|
|
|
|
|
|
|
All the arguments are optional. |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=over 4 |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=item B<-nmap> |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
Path to the nmap executable; no value by default. |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
If -nmap is set, C will test those -primary ports |
60
|
|
|
|
|
|
|
that Nmap reports to be either open or filtered, while it will only |
61
|
|
|
|
|
|
|
test those -secondary ports that Nmap reports to be open. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
If -nmap is not set, C will test all the -primary |
64
|
|
|
|
|
|
|
ports and skip the -secondary ports. |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=item B<-primary> |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
Reference to an array of ports where the risk of carrying an open |
69
|
|
|
|
|
|
|
proxy is not insignificant. Default value: |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
[ 80, 3128, 8080 ] |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=item B<-secondary> |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
Reference to an array of ports which are less likely, compared to |
76
|
|
|
|
|
|
|
the -primary ports, to carry an open proxy. Default value: |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
[ 808, 6588, 8000, 8088 ] |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=item B<-test_url> |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
Web address used for proxy testing; defaults to |
83
|
|
|
|
|
|
|
C<'http://gunnar.cc/proxy_test.txt'>, which is the address to a tiny |
84
|
|
|
|
|
|
|
text file on my own server. Even if that address works fine when I'm |
85
|
|
|
|
|
|
|
writing this, there is no guarantee that it will keep working for all |
86
|
|
|
|
|
|
|
time, so you are recommended to set -test_url to a resource that you |
87
|
|
|
|
|
|
|
control. Choose a URL to a tiny page on a reliable server which |
88
|
|
|
|
|
|
|
includes the status line C<200 OK> in the responses. |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=item B<-content_substr> |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
A string that shall be included in the content string of the response; |
93
|
|
|
|
|
|
|
defaults to C<'y4dWP:a7w'>. To prevent false positives, |
94
|
|
|
|
|
|
|
C will not report that a host carries an open proxy, |
95
|
|
|
|
|
|
|
unless it has confirmed an occurrence of -content_substr in the |
96
|
|
|
|
|
|
|
response content string. |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Obviously, if you set -test_url, you will most likely need to set |
99
|
|
|
|
|
|
|
-content_substr as well. |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=item B<-timeout> |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
When doing proxy testing, C expects to establish a |
104
|
|
|
|
|
|
|
server connection within -timeout seconds after a request, or else |
105
|
|
|
|
|
|
|
the request is aborted. Defaults to 4. |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=item B<-whitelist> |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Path to a DBM database with IP addresses of hosts that passed the |
110
|
|
|
|
|
|
|
proxy tests during the last week; no value by default. If you set |
111
|
|
|
|
|
|
|
-whitelist, C will maintain the database and skip |
112
|
|
|
|
|
|
|
testing for hosts in the 'whitelist'. |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=item B<-log> |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Path to a text file where information about requests from hosts with |
117
|
|
|
|
|
|
|
open proxies is logged; no value by default. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=item B<-log_maxbytes> |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
Maximum size in bytes of the -log file; defaults to C<1_000_000>. If |
122
|
|
|
|
|
|
|
-log is set, and when the max size is touched, C |
123
|
|
|
|
|
|
|
halves the file size by removing the oldest entries. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=back |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=head1 EXAMPLES |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=head2 Perl web apps |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
After having adapted the L code, you can simply insert it |
132
|
|
|
|
|
|
|
e.g. before any form generating or form data processing code portion |
133
|
|
|
|
|
|
|
of a Perl program. To shorten the code to be inserted in various |
134
|
|
|
|
|
|
|
programs, you can place a wrapper in one of the C<@INC> directories. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
# proxytest.pl |
137
|
|
|
|
|
|
|
use HTTP::ProxyTest; |
138
|
|
|
|
|
|
|
proxytest( |
139
|
|
|
|
|
|
|
-nmap => '/usr/local/bin/nmap', |
140
|
|
|
|
|
|
|
-whitelist => '/usr/local/etc/ProxyTest_whitelist', |
141
|
|
|
|
|
|
|
-log => '/var/log/open_proxy.log', |
142
|
|
|
|
|
|
|
); |
143
|
|
|
|
|
|
|
1; |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
Now you can invoke C by just saying: |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
require 'proxytest.pl'; |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head2 PHP web apps |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
This example of how to invoke C from PHP begins with |
152
|
|
|
|
|
|
|
this script, located in one of the PHP C directories: |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
// proxytest.php |
156
|
|
|
|
|
|
|
function proxytest() { |
157
|
|
|
|
|
|
|
$args = implode(' ', array( |
158
|
|
|
|
|
|
|
getenv('REMOTE_ADDR'), |
159
|
|
|
|
|
|
|
getenv('HTTP_HOST'), |
160
|
|
|
|
|
|
|
getenv('REQUEST_URI'), |
161
|
|
|
|
|
|
|
)); |
162
|
|
|
|
|
|
|
exec('/path/to/proxytest.pl ' . $args, $error); |
163
|
|
|
|
|
|
|
if ( count($error) ) { |
164
|
|
|
|
|
|
|
header('HTTP/1.0 403 Forbidden'); |
165
|
|
|
|
|
|
|
echo ( implode( "\n", array_slice($error, 3) ) ); |
166
|
|
|
|
|
|
|
exit; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
proxytest(); |
170
|
|
|
|
|
|
|
?> |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
Then we add some code to the wrapper and make it an executable Perl |
173
|
|
|
|
|
|
|
script. |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
#!/usr/bin/perl |
176
|
|
|
|
|
|
|
# proxytest.pl |
177
|
|
|
|
|
|
|
use HTTP::ProxyTest; |
178
|
|
|
|
|
|
|
if ( $ENV{_} and $ENV{_} eq '/path/to/proxytest.php' ) { |
179
|
|
|
|
|
|
|
@ENV{ qw/REMOTE_ADDR HTTP_HOST REQUEST_URI/ } = @ARGV; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
proxytest( |
182
|
|
|
|
|
|
|
-nmap => '/usr/local/bin/nmap', |
183
|
|
|
|
|
|
|
-whitelist => '/usr/local/etc/ProxyTest_whitelist', |
184
|
|
|
|
|
|
|
-log => '/var/log/open_proxy.log', |
185
|
|
|
|
|
|
|
); |
186
|
|
|
|
|
|
|
1; |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
Finally the single line call from a PHP program: |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
include 'proxytest.php'; |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=head1 DEPENDENCIES |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
This module is dependent on the L set of modules. |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
Also, even if it's possible to use C without access |
197
|
|
|
|
|
|
|
to the Nmap security scanner, we'd better consider Nmap to be a |
198
|
|
|
|
|
|
|
S<'soft dependency'>, a.k.a. strong recommendation. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=head1 CAVEAT |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
In case of C being invoked via a server wide wrapper, |
203
|
|
|
|
|
|
|
and the web server may be run as more than one user (e.g. because of |
204
|
|
|
|
|
|
|
Apache suEXEC), you should pay attention to the permissions of the |
205
|
|
|
|
|
|
|
DBM and log files. You may want to make sure that those files are |
206
|
|
|
|
|
|
|
'world writable'. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=head1 AUTHOR, COPYRIGHT AND LICENSE |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
Copyright (c) 2010-2011 Gunnar Hjalmarsson |
211
|
|
|
|
|
|
|
http://www.gunnar.cc/cgi-bin/contact.pl |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
This module is free software; you can redistribute it and/or modify it |
214
|
|
|
|
|
|
|
under the same terms as Perl itself. |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=head1 SEE ALSO |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
L, |
219
|
|
|
|
|
|
|
L |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=cut |
222
|
|
|
|
|
|
|
|
223
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
40
|
|
224
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
31
|
|
225
|
1
|
|
|
1
|
|
4
|
use Carp; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
63
|
|
226
|
1
|
|
|
1
|
|
1057
|
use LWP::UserAgent; |
|
1
|
|
|
|
|
73180
|
|
|
1
|
|
|
|
|
37
|
|
227
|
1
|
|
|
1
|
|
1197
|
use SDBM_File; |
|
1
|
|
|
|
|
4052
|
|
|
1
|
|
|
|
|
61
|
|
228
|
1
|
|
|
1
|
|
9
|
use Fcntl qw(:DEFAULT :flock); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
587
|
|
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
BEGIN { |
231
|
1
|
|
|
1
|
|
6
|
require Exporter; |
232
|
1
|
|
|
|
|
17
|
our @ISA = 'Exporter'; |
233
|
1
|
|
|
|
|
2110
|
our @EXPORT = 'proxytest'; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
local $Carp::CarpLevel = 2; |
237
|
|
|
|
|
|
|
local our $useragent; |
238
|
|
|
|
|
|
|
our $time = time; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub proxytest { |
241
|
1
|
50
|
|
1
|
0
|
154
|
my ($ip) = $ENV{REMOTE_ADDR} =~ /^(\d+(?:\.\d+){3})$/ or return; |
242
|
1
|
50
|
|
|
|
4
|
my $args = &arguments or return; |
243
|
1
|
|
|
|
|
6
|
my $path = $ENV{PATH}; |
244
|
1
|
|
|
|
|
7
|
$ENV{PATH} = ''; |
245
|
1
|
|
|
|
|
8
|
my $white = update_whitelist( $args->{whitelist} ); |
246
|
1
|
50
|
|
|
|
5
|
TEST: { |
247
|
1
|
|
|
|
|
4
|
last TEST if $white->{$ip}; |
248
|
|
|
|
|
|
|
|
249
|
1
|
|
|
|
|
6
|
my $ports = portselect($ip, $args); |
250
|
1
|
|
|
|
|
5
|
foreach my $port ( @$ports ) { |
251
|
3
|
|
|
|
|
53
|
$useragent->proxy('http', "http://$ip:$port"); |
252
|
3
|
|
|
|
|
415
|
my $res = $useragent->get( $args->{test_url} ); |
253
|
3
|
50
|
33
|
|
|
6325
|
if ( $res->is_success and |
254
|
|
|
|
|
|
|
index( $res->content, $args->{content_substr} ) >= 0 ) { |
255
|
0
|
|
|
|
|
0
|
caught($ip, $port, $args); |
256
|
0
|
|
|
|
|
0
|
untie %$white; |
257
|
0
|
|
|
|
|
0
|
exit; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
1
|
|
|
|
|
17
|
$white->{$ip} = $time; |
262
|
|
|
|
|
|
|
} |
263
|
1
|
|
|
|
|
32
|
untie %$white; |
264
|
1
|
|
|
|
|
13
|
$ENV{PATH} = $path; # don't interfere with rest of program |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub arguments { |
268
|
1
|
|
|
1
|
0
|
13
|
my %defaults = ( |
269
|
|
|
|
|
|
|
primary => [ 80, 3128, 8080 ], |
270
|
|
|
|
|
|
|
secondary => [ 808, 6588, 8000, 8088 ], |
271
|
|
|
|
|
|
|
test_url => 'http://gunnar.cc/proxy_test.txt', |
272
|
|
|
|
|
|
|
content_substr => 'y4dWP:a7w', |
273
|
|
|
|
|
|
|
timeout => 4, |
274
|
|
|
|
|
|
|
log_maxbytes => 1_000_000, |
275
|
|
|
|
|
|
|
); |
276
|
1
|
|
|
|
|
13
|
my %valid_keys = ( |
277
|
|
|
|
|
|
|
-nmap => 'nmap', |
278
|
|
|
|
|
|
|
-primary => 'primary', |
279
|
|
|
|
|
|
|
-secondary => 'secondary', |
280
|
|
|
|
|
|
|
-test_url => 'test_url', |
281
|
|
|
|
|
|
|
-content_substr => 'content_substr', |
282
|
|
|
|
|
|
|
-timeout => 'timeout', |
283
|
|
|
|
|
|
|
-whitelist => 'whitelist', |
284
|
|
|
|
|
|
|
-log => 'log', |
285
|
|
|
|
|
|
|
-log_maxbytes => 'log_maxbytes', |
286
|
|
|
|
|
|
|
); |
287
|
|
|
|
|
|
|
|
288
|
1
|
50
|
|
|
|
5
|
@_ % 2 == 0 or croak 'key=>value pairs are expected'; |
289
|
|
|
|
|
|
|
|
290
|
1
|
|
|
|
|
3
|
my %args; |
291
|
1
|
|
|
|
|
5
|
while ( my $arg = shift ) { |
292
|
0
|
|
|
|
|
0
|
my $key = lc $arg; |
293
|
0
|
0
|
|
|
|
0
|
$valid_keys{$key} or croak "Unknown argument key '$key'"; |
294
|
0
|
|
|
|
|
0
|
$args{ $valid_keys{$key} } = shift; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
1
|
50
|
|
|
|
5
|
if ( $args{nmap} ) { |
298
|
0
|
0
|
|
|
|
0
|
-f $args{nmap} or croak "File '$args{nmap}' does not exist"; |
299
|
0
|
0
|
|
|
|
0
|
-x $args{nmap} or croak "'$args{nmap}' is not an executable file"; |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
1
|
|
|
|
|
2
|
for ('whitelist', 'log') { |
303
|
2
|
50
|
|
|
|
8
|
PATHCHECKS: { |
304
|
2
|
|
|
|
|
2
|
last PATHCHECKS unless $args{$_}; |
305
|
0
|
0
|
|
|
|
0
|
my $file = $_ eq 'whitelist' ? $args{$_}.'.pag' : $args{$_}; |
306
|
0
|
0
|
|
|
|
0
|
if ( -f $file ) { |
307
|
0
|
0
|
0
|
|
|
0
|
last PATHCHECKS if -r $file and -w _; |
308
|
0
|
|
|
|
|
0
|
croak "Argument -$_: The user this script runs as ", |
309
|
|
|
|
|
|
|
"does not have write access to '$file'"; |
310
|
|
|
|
|
|
|
} |
311
|
0
|
|
|
|
|
0
|
require File::Basename; |
312
|
0
|
|
|
|
|
0
|
my $dir = ( File::Basename::fileparse($file) )[1]; |
313
|
0
|
0
|
|
|
|
0
|
if ( -d $dir ) { |
314
|
0
|
0
|
0
|
|
|
0
|
last PATHCHECKS if -r $dir and -w _ and -x _; |
|
|
|
0
|
|
|
|
|
315
|
0
|
|
|
|
|
0
|
croak "Argument -$_: The user this script runs as ", |
316
|
|
|
|
|
|
|
"does not have write access to '$dir'"; |
317
|
|
|
|
|
|
|
} |
318
|
0
|
|
|
|
|
0
|
croak "Argument -$_: Can't find any directory '$dir'"; |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
1
|
|
|
|
|
3
|
for ('primary', 'secondary') { |
323
|
2
|
50
|
|
|
|
6
|
if ( exists $args{$_} ) { |
324
|
0
|
0
|
|
|
|
0
|
ref($args{$_}) eq 'ARRAY' or croak "Argument -$_ shall be an arrayref"; |
325
|
0
|
|
0
|
|
|
0
|
my $err = grep /\D/ || $_ < 0 || $_ > 65535, @{ $args{$_} }; |
|
0
|
|
|
|
|
0
|
|
326
|
0
|
0
|
|
|
|
0
|
$err == 0 or croak "Argument -$_: $err elements are not valid port numbers"; |
327
|
|
|
|
|
|
|
} else { |
328
|
2
|
|
|
|
|
7
|
$args{$_} = $defaults{$_}; |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
} |
331
|
1
|
50
|
33
|
|
|
6
|
$args{primary}->[0] or $args{secondary}->[0] or |
332
|
|
|
|
|
|
|
croak 'There should be at least one port to test'; |
333
|
1
|
50
|
33
|
|
|
11
|
unless ( $args{nmap} or $args{primary}->[0] ) { |
334
|
0
|
|
|
|
|
0
|
croak 'Argument -primary may not refer to an empty list when no Nmap scanning is done'; |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
1
|
|
|
|
|
3
|
for ('timeout', 'log_maxbytes') { |
338
|
2
|
50
|
|
|
|
5
|
if ( $args{$_} ) { |
339
|
0
|
0
|
|
|
|
0
|
$args{$_} =~ /^\d+$/ or croak "Argument -$_ shall be a positive integer"; |
340
|
|
|
|
|
|
|
} else { |
341
|
2
|
|
|
|
|
6
|
$args{$_} = $defaults{$_}; |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
|
345
|
1
|
|
|
|
|
6
|
for ('test_url', 'content_substr') { |
346
|
2
|
|
33
|
|
|
13
|
$args{$_} ||= $defaults{$_}; |
347
|
|
|
|
|
|
|
} |
348
|
1
|
|
|
|
|
15
|
$useragent = LWP::UserAgent->new( |
349
|
|
|
|
|
|
|
timeout => $args{timeout}, |
350
|
|
|
|
|
|
|
agent => "HTTP::ProxyTest/$VERSION", |
351
|
|
|
|
|
|
|
requests_redirectable => [], |
352
|
|
|
|
|
|
|
); |
353
|
1
|
|
|
|
|
14233
|
my $res = $useragent->get( $args{test_url} ); |
354
|
1
|
50
|
|
|
|
226046
|
unless ( $res->is_success ) { |
355
|
|
|
|
|
|
|
# no fatal error, since a temporary glitch |
356
|
|
|
|
|
|
|
# might be the cause of the failure |
357
|
0
|
|
|
|
|
0
|
carp 'Argument -test_url: Response status ', $res->status_line; |
358
|
0
|
|
|
|
|
0
|
return undef; |
359
|
|
|
|
|
|
|
} |
360
|
1
|
50
|
|
|
|
24
|
unless ( index( $res->content, $args{content_substr} ) >= 0 ) { |
361
|
0
|
|
|
|
|
0
|
croak 'Argument -content_substr: The string ', |
362
|
|
|
|
|
|
|
"'$args{content_substr}' not found in the source of $args{test_url}"; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
1
|
|
|
|
|
50
|
\%args |
366
|
|
|
|
|
|
|
} |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
sub update_whitelist { |
369
|
1
|
|
|
1
|
0
|
4
|
my $whitelist = shift; |
370
|
1
|
50
|
|
|
|
6
|
return {} unless $whitelist; |
371
|
|
|
|
|
|
|
|
372
|
0
|
0
|
|
|
|
0
|
tie my %white, 'SDBM_File', $whitelist, O_CREAT|O_RDWR, 0666 or die $!; |
373
|
0
|
|
|
|
|
0
|
my @oldies = grep $white{$_} < $time - 604800, keys %white; |
374
|
0
|
|
|
|
|
0
|
delete @white{ @oldies }; |
375
|
0
|
|
|
|
|
0
|
\%white |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
sub portselect { |
379
|
1
|
|
|
1
|
0
|
3
|
my ($ip, $args) = @_; |
380
|
1
|
50
|
|
|
|
6
|
return $args->{primary} unless $args->{nmap}; |
381
|
|
|
|
|
|
|
|
382
|
0
|
|
|
|
|
|
my (%count, @open, @filtered); |
383
|
0
|
0
|
|
|
|
|
my $ports = join ',', map { $count{$_}++ ? () : $_ } |
|
0
|
|
|
|
|
|
|
384
|
0
|
|
|
|
|
|
@{ $args->{primary} }, @{ $args->{secondary} }; |
|
0
|
|
|
|
|
|
|
385
|
0
|
|
|
|
|
|
my $nmap_result = qx( $args->{nmap} -PN -p $ports $ip ); |
386
|
0
|
0
|
0
|
|
|
|
croak 'Nmap scan failed' if !$nmap_result or $?; |
387
|
0
|
|
|
|
|
|
while ( $nmap_result =~ m,^(\d+)/tcp\s+(open|filtered)\b,gm ) { |
388
|
0
|
|
|
|
|
|
my ($port, $state) = ($1, $2); |
389
|
0
|
0
|
|
|
|
|
if ( $state eq 'open' ) { |
|
0
|
0
|
|
|
|
|
|
390
|
0
|
|
|
|
|
|
push @open, $port; |
391
|
|
|
|
|
|
|
} elsif ( grep $_ eq $port, @{ $args->{primary} } ) { |
392
|
0
|
|
|
|
|
|
push @filtered, $port; |
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
} |
395
|
0
|
|
|
|
|
|
[ @open, @filtered ] |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
sub caught { |
399
|
0
|
|
|
0
|
0
|
|
my ($ip, $port, $args) = @_; |
400
|
0
|
|
0
|
|
|
|
my $host = gethostbyaddr( pack('C4', split /\./, $ip), 2 ) || "IP $ip"; |
401
|
0
|
|
|
|
|
|
print "Status: 403 Forbidden\n", |
402
|
|
|
|
|
|
|
"Content-type: text/html; charset=UTF-8\n\n"; |
403
|
0
|
|
|
|
|
|
print "403 Forbidden\n", |
404
|
|
|
|
|
|
|
"403 Forbidden\nThe host you are using ($host) ", |
405
|
|
|
|
|
|
|
"appears to carry an open proxy on port $port.\n", |
406
|
|
|
|
|
|
|
"\n"; |
407
|
0
|
0
|
|
|
|
|
return unless $args->{log}; |
408
|
|
|
|
|
|
|
|
409
|
0
|
0
|
|
|
|
|
open my $log, '+>>', $args->{log} or die $!; |
410
|
0
|
|
|
|
|
|
flock $log, LOCK_EX; |
411
|
0
|
0
|
|
|
|
|
print $log "Date: ", scalar localtime $time, "\n", |
412
|
|
|
|
|
|
|
"URL: ", ( lc substr($ENV{REQUEST_URI}, 0, 4) eq 'http' ? |
413
|
|
|
|
|
|
|
'' : "http://$ENV{HTTP_HOST}" ), "$ENV{REQUEST_URI}\n", |
414
|
|
|
|
|
|
|
"IP: $ip\n"; |
415
|
0
|
0
|
|
|
|
|
print $log "Host name: $host\n" unless substr($host, 3) eq $ip; |
416
|
0
|
|
|
|
|
|
print $log "Port: $port\n\n"; |
417
|
|
|
|
|
|
|
|
418
|
0
|
|
|
|
|
|
my $oldfh = select $log; $|++; select $oldfh; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
419
|
0
|
0
|
|
|
|
|
return unless -s $log > $args->{log_maxbytes}; |
420
|
|
|
|
|
|
|
|
421
|
0
|
|
|
|
|
|
seek $log, $args->{log_maxbytes} / 2, 0; |
422
|
0
|
|
|
|
|
|
my $latest = do { local $/; <$log> }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
423
|
0
|
|
|
|
|
|
$latest =~ s/.+?\n\n//s; |
424
|
0
|
|
|
|
|
|
seek $log, 0, 0; |
425
|
0
|
|
|
|
|
|
truncate $log, 0; |
426
|
0
|
|
|
|
|
|
print $log $latest; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
1; |
430
|
|
|
|
|
|
|
|