123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- #!/usr/bin/perl
-
- use strict;
- use Data::Dumper;
- use warnings;
-
-
- my %conf = (
- avail => 10,
- spare => 3,
- ipt_chain => "PREROUTING",
- ipt_rule => "-p tcp -m tcp --dport 777 -m statistic --mode random --probability %s -j REDIRECT --to-ports %s",
- ipt_table => 'nat',
- socks_offset => 9000,
- path => '/opt/tor',
- dns_check => [
- 'www.microsoft.com',
- 'www.google.com',
- ],
- );
-
- my @var;
-
-
- run();
-
-
- sub run {
- check_processes();
- check_dns();
- check_download();
-
- sleep(10);
- }
-
- sub check_processes {
- my $inuse=0;
- my @spares;
-
- 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 ( $inuse < $conf{avail} ) {
- print "missing live processes. looking for spares.\n";
-
- for(my $x=0; $x<($conf{avail}-$inuse; $x++) {
- my $new = pop @spares;
- if ( defined $new ) {
- print "moving spare to prod.\n";
- tor_prod($id);
- }
- }
- }
-
- if ( @spares < $conf{spares} ) {
- print "missing spare processes, starting new ones.\n";
-
- for(my $x=0; $x< @spares-$conf{spares}; $x++) {
- tor_new();
- }
- }
- }
-
- sub check_dns {
-
- my($id) = @_;
-
- 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 ! $?;
- }
-
- 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}++;
- }
- elsif ( $s eq 'AVAILABLE' && ! $ok ) {
- $var[$id]{state}++;
- }
- elsif ( $s eq 'TEMPORARY_UNAVAILABLE' ) {
- $var[$id}{state}++
-
- if ( ! $ok ) {
- }
-
- }
-
- sub tor_new {
- my $next;
-
- # try to recycle a position
- for(my $id=0; defined $var[$id]; $id++) {
- if ( $state[$var[$id]{state}] eq 'KILLED' ) {
- $next = $id;
- last;
- }
- }
-
- # alternatively, create a new position
- if ( !defined $next ) {
- $next = @var;
- }
-
- $var[$next] = {
- state => 0,
- port => $conf{port_offset} + $next,
- pid => undef,
- };
-
- # create a config file and the required files and directories
- my $datadir = "$conf{path}/var/lib/$id";
- 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",
- );
-
- system("mkdir -p $datadir");
- system("chmod 700 $datadir");
- system("chown debian-tor:debian-tor $datadir");
-
- foreach my $f ( keys $files ) {
- system("touch $files{$f}");
- system("chown debian-tor:debian-tor $files{$f}");
- }
-
- open(C,">$files{config}");
- print C <<EOF;
- DataDirectory $datadir
- PidFile $files{pidfile}
- RunAsDaemon 0
- User debian-tor
-
- ControlSocket $files{ctrl} GroupWritable RelaxDirModeCheck
- ControlSocketsGroupWritable 1
- SocksPort unix:$files{socks} WorldWritable
- SocksPort $var[$id]{port}
-
- CookieAuthentication 1
- CookieAuthFileGroupReadable 1
- CookieAuthFile $files{cookie}
-
- BandwidthRate 10MB
-
- Log notice syslog
-
- EOF
- close(C);
-
- system("cd $conf{dir} && tor -f $files{config}");
-
- }
-
- sub tor_prod {
- my($id) = @_;
-
- # move process from spare to prod
-
- $var[$id]{state}++;
- ipt_add($var[$id]{port});
- }
-
- sub ipt_add {
- my($port) = @_;
-
- system("iptables -t $conf{ipt_table} -A $conf{ipt_chain} " . sprintf($conf{ipt_rule}, "1.0", $port));
-
- ipt_recalc();
- }
-
- sub ipt_del {
- 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");
- }
- }
- close(IPT);
-
- ipt_recalc();
- }
-
- sub ipt_recalc {
- 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 ];
- }
- }
- 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));
- }
-
- }
|