Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /home/zhenxiangba/zhenxiangba.com/public_html/phproxy-improved-master/index.php on line 456
#!/usr/local/bin/perl
# $Id: http-proxy.pl,v 1.2 2003/05/17 14:31:02 68user Exp $
require 5.000;
use strict;
use IO::Socket;
use IO::Select;
# 接続する proxy。直接接続なら $real_proxy=''; とすること。
my $real_proxy = 'proxy.hoge.co.jp:8080';
# $real_proxy を経由しないホスト名。正規表現で記述。
my @noproxy_hosts = qw(localhost hoge\.co\.jp);
# proxy 用ポート番号
my $listen_port = 8080;
my $sock_waiting = IO::Socket::INET->new(Listen => SOMAXCONN,
LocalPort => $listen_port,
Proto => 'tcp',
Reuse => 1,
# 自サーバ以外からの接続を許すなら LocalAddr をコメントアウトする。
LocalAddr => 'localhost',
);
my $selecter = IO::Select->new;
$selecter->add($sock_waiting);
my %conn_table;
my %conn_table_rev;
my %request;
#-------------------
# SIGPIPE シグナルを受けても終了しないようにする。例えば、web サーバからの
# レスポンスをブラウザに渡そうと print したが、それより前にブラウザの中止ボタンが
# 押されていた、というときに SIGPIPE が飛んでくる。
$SIG{PIPE} = sub {
print "Caught SIGPIPE. But continue..\n";
};
while (1){
print "Begin select\n";
my ($clients) = IO::Select->select($selecter, undef, undef, undef);
if ( @$clients == 0 ){
next;
}
foreach my $sock (@$clients){
print " socket_descripter=", $sock->fileno, "\n";
if ( $sock == $sock_waiting ){
# ブラウザからの新しい接続あり
my $new_sock = $sock_waiting->accept;
$selecter->add($new_sock);
printf " New client comes (%d).\n", $new_sock->fileno;
} else {
if ( $conn_table{$sock} ){
# web サーバからのデータがあるか、web サーバとのコネクション切断
my $buf;
my $read_len = sysread($sock, $buf, 10000);
if ( $buf ){
# web サーバからのデータあり
print " FROM Web server. Read OK ($read_len bytes).\n";
my $fn = $conn_table{$sock};
select($fn); $|=1; select(STDOUT);
print $fn $buf;
} else {
# web サーバとのコネクション切断
print " FROM Web server. Finished.\n";
printf " close(%d)\n", $conn_table{$sock}->fileno;
&remove_contable($conn_table{$sock});
shutdown($conn_table{$sock}, 2);
shutdown($sock, 2);
$conn_table{$sock} = undef;
}
} else {
# ブラウザからのデータあり
my $buf;
sysread($sock, $buf, 10000);
if ( $buf ){
print " FROM Browser. Read OK.\n";
$request{$sock} .= $buf;
if ( $request{$sock} =~ m/\r\n\r\n|\n\n/ ){
# 空行がある=完全なリクエストが送られてきた
# POST のときには、あと Content-length の分だけ
# 読まなければいけないが、未実装。
if ( $request{$sock} =~ m/^(POST|GET) / ){
my ($host, $port, $new_req) = &parse_request($request{$sock});
printf "MAKE SOCKET TO $host:$port\n";
my $sock_connect = IO::Socket::INET->new(PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp',
);
if ( ! $sock_connect ){
select($sock); $|=1; select(STDOUT);
print $sock "HTTP/1.0 500 cannot resolve.\r\n";
print $sock "\r\n";
print $sock "\r\n";
print $sock "\r\n";
print $sock "$host:$port cannot resolve.\r\n";
print $sock "\r\n";
print $sock "\r\n";
&remove_contable($sock);
close($sock);
next;
}
$conn_table{$sock_connect} = $sock;
$conn_table_rev{$sock} = $sock_connect;
select($sock_connect); $|=1; select(STDOUT);
print $sock_connect $new_req;
$new_req =~ s/^/ /gm;
print " -- request start--\n";
print $new_req;
print " -- request end--\n";
$selecter->add($sock_connect);
}
} else {
# 完全なリクエストが届かなかった (次回続きが送られてくることを期待)
print "Wait continue request\n";
}
} else {
# ブラウザからのコネクション切断。
print " FROM Browser. Finished.\n";
&remove_contable($sock);
}
}
}
}
}
exit;
#---------------------------------
# コネクションが切断された場合、select 対象から外す。
sub remove_contable($){
my ($sock) = @_;
print " delete ", $sock->fileno, "\n";
$conn_table_rev{$sock} = undef;
# %conn_table も undef しないとメモリリークすると思うが、
# 一回につき数十KB くらいなので、とりあえず放置。
$selecter->remove($sock);
$request{$sock} = undef;
}
#---------------------------------
# ヘッダの書き換え
sub override_header($\$) {
my ($header, $ref_value) = @_;
if ( $header =~ m/^User-Agent$/i ){
$$ref_value .= '';
}
}
#---------------------------------
# リクエストを解析し、実際の接続先やポート番号などを求める。
#
# 引数: HTTP リクエスト
# 戻り値: 接続先ホスト名、接続先ポート番号、解析済リクエスト
sub parse_request($){
my ($req) = @_;
my $host;
my $port;
my $new_req = '';
my $is_header = 1;
foreach ( split(/\r\n|\n/, $req, -1) ){
if ( m|^([A-Z]+) [a-zA-Z]+://(.*?)/(.*?) (HTTP/\d\.\d)$|i ){
my $proto_ver;
my $path;
my $method;
($method, $host, $path, $proto_ver) = ($1, $2, $3, $4);
if ( &is_use_proxy($host) ){
$new_req = "$_\r\n";
($host, $port) = $real_proxy =~ m/^(.*):(\d+)$/;
} else {
if ( $host =~ s/:(\d+)$// ){
$port = $1;
} else {
$port = 80;
}
$new_req = "$method /$path $proto_ver\r\n";
}
} elsif ( $is_header ){
if ( m/^(.*?):\s*(.*)$/s ){
my ($header, $value) = ($1, $2);
&override_header($header, \$value);
$new_req .= "$header: $value\r\n";
} elsif ( $_ eq '' ){
# ヘッダおしまい
$new_req .= "\r\n";
$is_header = 0;
}
} elsif ( ! $is_header ){
$new_req .= "$_";
}
}
return ($host, $port, $new_req);
}
#----------------------------------------------
# 外部 proxy を経由するかどうか調べる。
#
# 引数: ホスト名
# 戻り値: 1…proxy を経由する
# 0…proxy を経由しない
sub is_use_proxy($){
my ($host) = @_;
foreach ( @noproxy_hosts ){
if ( $host =~ m/.*$_.*/ ){
return 0;
}
}
if ( $real_proxy ){
return 1;
} else {
return 0;
}
}