Browse Source

first working version

master
root 6 years ago
parent
commit
de2004f092
1 changed files with 230 additions and 67 deletions
  1. 230
    67
      bin/tor_controller.pl

+ 230
- 67
bin/tor_controller.pl View File

#!/usr/bin/perl #!/usr/bin/perl


use strict; use strict;
use POSIX ":sys_wait_h";
use Data::Dumper; use Data::Dumper;
use warnings; use warnings;




my %conf = ( my %conf = (
avail => 10,
spare => 3,
avail => 2,
spare => 2,
ipt_chain => "PREROUTING", ipt_chain => "PREROUTING",
ipt_rule => "-p tcp -m tcp --dport 777 -m statistic --mode random --probability %s -j REDIRECT --to-ports %s", ipt_rule => "-p tcp -m tcp --dport 777 -m statistic --mode random --probability %s -j REDIRECT --to-ports %s",
ipt_table => 'nat', ipt_table => 'nat',
'www.microsoft.com', 'www.microsoft.com',
'www.google.com', 'www.google.com',
], ],
proto => {
ipv4 => 1,
ipv6 => 1,
},
loopinterval => 60,
); );


my @var;
# states: startup, ready, problem, dying, killed
# modes: spare, live


my @var;


run();
ipt_clean();
while(1) { run(); }




sub run { sub run {

check_processes(); check_processes();
check_dns();
check_download();
show_state();

sleep($conf{loopinterval});
}

sub show_state {


sleep(10);
my $s = ipt_conn();

printf("======================================\n");
printf("%3s %-7s %-5s %4s %4s\n", "id", "state", "mode", "ipv4", "ipv6");
for(my $id=0; defined $var[$id]; $id++) {
printf("%3s %-7s %-5s %4s %4s\n",
$id,
$var[$id]{state},
$var[$id]{mode},
exists $s->{$id}{ipv4} ? $s->{$id}{ipv4} : 0,
exists $s->{$id}{ipv6} ? $s->{$id}{ipv6} : 0,
);
}
printf("======================================\n");
} }


sub check_processes { sub check_processes {
my @spares; my @spares;


for(my $id=0; defined $var[$id]; $id++) { for(my $id=0; defined $var[$id]; $id++) {
if ( $var[$id]{state} eq "ready" ) {
if ( $var[$id]{mode} eq "spare" ) {
push @spares, $id;
}
elsif ( $var[$id]{mode} eq "live" ) {
$inuse++;
}
if ( $var[$id]{pid} eq waitpid($var[$id]{pid}, WNOHANG) ) {
print "child $var[$id]{pid} died. resetting instance $id\n";
ipt_del($id);
$var[$id]{pid}=undef;
$var[$id]{state}='killed';
$var[$id]{mode}='none';
}
}

for(my $id=0; defined $var[$id]; $id++) {
if ( $var[$id]{state} =~ /^(startup|ready|problem)$/ ) {
check_dns($id);
} }
} }


for(my $id=0; defined $var[$id]; $id++) {
if ( $var[$id]{state} eq "ready" && $var[$id]{mode} eq 'live' ) {
$inuse++;
}

if ( $var[$id]{state} =~ /^(startup|ready|problem)$/ && $var[$id]{mode} eq 'spare' ) {
push @spares, $id;
}

}

print "found " . scalar(@spares) . " spare clients and $inuse live clients\n";

if ( $inuse < $conf{avail} ) { if ( $inuse < $conf{avail} ) {
print "missing live processes. looking for spares.\n"; print "missing live processes. looking for spares.\n";


for(my $x=0; $x<($conf{avail}-$inuse; $x++) {
for(my $x=0; $x<($conf{avail}-$inuse); $x++) {
my $new = pop @spares; my $new = pop @spares;
if ( defined $new ) { if ( defined $new ) {
print "moving spare to prod.\n";
tor_prod($id);
if ( $var[$new]{state} eq 'ready' ) {
print "moving spare to prod.\n";
tor_prod($new);
}
else {
push @spares, $new;
}
} }
} }
} }


if ( @spares < $conf{spares} ) {
if ( scalar(@spares) < $conf{spare} ) {
print "missing spare processes, starting new ones.\n"; print "missing spare processes, starting new ones.\n";


for(my $x=0; $x< @spares-$conf{spares}; $x++) {
for(my $x=0; $x < ($conf{spare}-scalar(@spares)); $x++) {
tor_new(); tor_new();
} }
} }
my($id) = @_; my($id) = @_;


my $ok = 0; my $ok = 0;
foreach my $hostname ( @{$conf{dns_check} ) {
system("tor-resolve $hostname 127.0.0.1:$var[$id]{port} 2>/dev/null 2>&1");
$ok++ if ! $?;
foreach my $hostname ( @{$conf{dns_check}} ) {
print "Checking DNS ($hostname) for $id ";
system("timeout 5 tor-resolve $hostname 127.0.0.1:$var[$id]{port} >/dev/null 2>&1");
if ( $? ) {
print "ERROR\n";
}
else {
print "OK\n";
$ok++;
}
} }


switch(get_state($id)) {
case "STARTUP" { set_state("SPARE") if $ok; }
case "SPARE" { set_state("PROBLEM") if !$ok; }
}
if ( $s eq 'DNS_CHECK' && $ok ) {
$var[$id]{state}++;
if ( $var[$id]{state} eq 'startup' ) {
if ( $ok ) {
$var[$id]{state} = 'ready';
}
else {
$var[$id]{state} = 'problem';
}
} }
elsif ( $s eq 'AVAILABLE' && ! $ok ) {
$var[$id]{state}++;
elsif ( $var[$id]{state} eq 'ready' && !$ok ) {
$var[$id]{state} = 'problem';
} }
elsif ( $s eq 'TEMPORARY_UNAVAILABLE' ) {
$var[$id}{state}++

if ( ! $ok ) {
elsif ( $var[$id]{state} eq 'problem' ) {
if ( $ok ) {
$var[$id]{state} = 'ready';
}
else {
$var[$id]{state} = 'dying';
ipt_del($id);
}
} }

} }


sub tor_new { sub tor_new {
my $next; my $next;


print "initiating a new tor client\n";

# try to recycle a position # try to recycle a position
for(my $id=0; defined $var[$id]; $id++) { for(my $id=0; defined $var[$id]; $id++) {
if ( $state[$var[$id]{state}] eq 'KILLED' ) {
if ( $var[$id]{state} eq 'killed' ) {
print "recovering id $id\n";
$next = $id; $next = $id;
last; last;
} }
# alternatively, create a new position # alternatively, create a new position
if ( !defined $next ) { if ( !defined $next ) {
$next = @var; $next = @var;
print "new id $next\n";
} }


$var[$next] = { $var[$next] = {
state => 0,
port => $conf{port_offset} + $next,
state => "startup",
mode => "spare",
port => $conf{socks_offset} + $next,
pid => undef, pid => undef,
}; };


print "creating configuration file and dependencies\n";

# create a config file and the required files and directories # create a config file and the required files and directories
my $datadir = "$conf{path}/var/lib/$id";
my $datadir = "$conf{path}/var/lib/$next";
my %files = ( my %files = (
config => "$conf{path}/etc/$id.conf",
pidfile => "$conf{path}/run/$id.pid",
socket => "$conf{path}/run/$id.socks",
ctrl => "$conf{path}/run/$id.ctrl",
cookie => "$conf{path}/run/$id.cookie",
config => "$conf{path}/etc/$next.conf",
pidfile => "$conf{path}/run/$next.pid",
socket => "$conf{path}/run/$next.socks",
ctrl => "$conf{path}/run/$next.ctrl",
cookie => "$conf{path}/run/$next.cookie",
); );


system("mkdir -p $datadir"); system("mkdir -p $datadir");
system("chmod 700 $datadir"); system("chmod 700 $datadir");
system("chown debian-tor:debian-tor $datadir"); system("chown debian-tor:debian-tor $datadir");


foreach my $f ( keys $files ) {
foreach my $f ( keys %files ) {
system("touch $files{$f}"); system("touch $files{$f}");
system("chown debian-tor:debian-tor $files{$f}"); system("chown debian-tor:debian-tor $files{$f}");
} }


ControlSocket $files{ctrl} GroupWritable RelaxDirModeCheck ControlSocket $files{ctrl} GroupWritable RelaxDirModeCheck
ControlSocketsGroupWritable 1 ControlSocketsGroupWritable 1
SocksPort unix:$files{socks} WorldWritable
SocksPort $var[$id]{port}
#SocksPort unix:$files{socket} WorldWritable
SocksPort 0.0.0.0:$var[$next]{port}
SocksPort [::]:$var[$next]{port}


CookieAuthentication 1 CookieAuthentication 1
CookieAuthFileGroupReadable 1 CookieAuthFileGroupReadable 1
EOF EOF
close(C); close(C);


system("cd $conf{dir} && tor -f $files{config}");
print "forking...\n";
my $pid = fork();


if ( !defined $pid ) {
die "Failed to fork, something has gone badly wrong!\n";
}
elsif ( $pid ) {
print "tor client pid $pid\n";
$var[$next]{pid} = $pid;
}
else {
chdir $conf{path};
exec("tor -f $files{config} >/dev/null");
die "something weng really wrong, failed to exec tor";
exit;
}
} }


sub tor_prod { sub tor_prod {


# move process from spare to prod # move process from spare to prod


$var[$id]{state}++;
$var[$id]{mode} = 'live';
ipt_add($var[$id]{port}); ipt_add($var[$id]{port});
} }


sub ipt_add { sub ipt_add {
my($port) = @_; my($port) = @_;


system("iptables -t $conf{ipt_table} -A $conf{ipt_chain} " . sprintf($conf{ipt_rule}, "1.0", $port));
system("iptables -t $conf{ipt_table} -A $conf{ipt_chain} " . sprintf($conf{ipt_rule}, "1.0", $port)) if $conf{proto}{ipv4};
system("ip6tables -t $conf{ipt_table} -A $conf{ipt_chain} " . sprintf($conf{ipt_rule}, "1.0", $port)) if $conf{proto}{ipv6};


ipt_recalc(); ipt_recalc();
} }
sub ipt_del { sub ipt_del {
my($port) = @_; my($port) = @_;


open(IPT,"iptables -t $conf{ipt_table} -L $conf{ipt_chain} -n --line-numbers |");
while(<IPT>) {
chomp;
if ( /^(\d+) .*mode random probability .* redir ports $port$/ ) {
system("iptables -t $conf{ipt_table} -D $conf{ipt_chain} $1");
if ( $conf{proto}{ipv4} ) {
open(IPT,"iptables -t $conf{ipt_table} -L $conf{ipt_chain} -n --line-numbers |");
while(<IPT>) {
chomp;
if ( /^(\d+) .*mode random probability .* redir ports $port$/ ) {
system("iptables -t $conf{ipt_table} -D $conf{ipt_chain} $1");
}
}
close(IPT);
}

if ( $conf{proto}{ipv6} ) {
open(IPT,"ip6tables -t $conf{ipt_table} -L $conf{ipt_chain} -n --line-numbers |");
while(<IPT>) {
chomp;
if ( /^(\d+) .*mode random probability .* redir ports $port$/ ) {
system("ip6tables -t $conf{ipt_table} -D $conf{ipt_chain} $1");
}
} }
close(IPT);
} }
close(IPT);


ipt_recalc(); ipt_recalc();
} }


sub ipt_clean {
system("iptables -t $conf{ipt_table} -F $conf{ipt_chain}") if $conf{proto}{ipv4};
system("ip6tables -t $conf{ipt_table} -F $conf{ipt_chain}") if $conf{proto}{ipv6};
}

sub ipt_recalc { sub ipt_recalc {
my @rules; my @rules;


open(IPT,"iptables -t $conf{ipt_table} -L $conf{ipt_chain} -n --line-numbers |");
while(<IPT>) {
chomp;
if ( /^(\d+) .*mode random probability .* redir ports (\d+)$/ ) {
push @rules, [ $1, $2 ];
if ( $conf{proto}{ipv4} ) {
open(IPT,"iptables -t $conf{ipt_table} -L $conf{ipt_chain} -n --line-numbers |");
while(<IPT>) {
chomp;
if ( /^(\d+) .*mode random probability .* redir ports (\d+)$/ ) {
push @rules, [ $1, $2 ];
}
}
close(IPT);

my $nums = @rules;

for(my $num = 0; defined $rules[$num]; $num++) {
my($rulenum,$port)=@{$rules[$num]};
my $prob = 1 / ($nums - $num);
system("iptables -t $conf{ipt_table} -R $conf{ipt_chain} $rulenum " . sprintf($conf{ipt_rule}, $prob, $port));
} }
} }
close(IPT);


my $nums = @rules;
if ( $conf{proto}{ipv6} ) {
open(IPT,"ip6tables -t $conf{ipt_table} -L $conf{ipt_chain} -n --line-numbers |");
while(<IPT>) {
chomp;
if ( /^(\d+) .*mode random probability .* redir ports (\d+)$/ ) {
push @rules, [ $1, $2 ];
}
}
close(IPT);

my $nums = @rules;


for(my $num = 0; defined $rules[$num]; $num++) {
my($rulenum,$port)=@{$rules[$num]};
for(my $num = 0; defined $rules[$num]; $num++) {
my($rulenum,$port)=@{$rules[$num]};
my $prob = 1 / ($nums - $num);
system("ip6tables -t $conf{ipt_table} -R $conf{ipt_chain} $rulenum " . sprintf($conf{ipt_rule}, $prob, $port));
}
}

}


my $prob = 1 / ($nums - $num);
sub ipt_conn {


system("iptables -t $conf{ipt_table} -R $conf{ipt_chain} $rulenum " . sprintf($conf{ipt_rule}, $prob, $port));
my %s;

if ( $conf{proto}{ipv4} ) {
open(IPT,"conntrack -L -f ipv4 2>/dev/null |");
while(<IPT>) {
chomp;
if ( / dport=777 .* sport=(\d+) / ) {
my $port = $1;
if ( $port >= $conf{socks_offset} ) {
my $id = $port - $conf{socks_offset};
$s{$id}{ipv4}++;
}
}
}
close(IPT);
}

if ( $conf{proto}{ipv6} ) {
open(IPT,"conntrack -L -f ipv6 2>/dev/null |");
while(<IPT>) {
chomp;
if ( / dport=777 .* sport=(\d+) / ) {
my $port = $1;
if ( $port >= $conf{socks_offset} ) {
my $id = $port - $conf{socks_offset};
$s{$id}{ipv6}++;
}
}
}
close(IPT);
} }


return \%s;
} }

Loading…
Cancel
Save