#!/usr/bin/perl
#
#
#  DATA root:18dLSAXgB10:0:r:4
#
#

use Socket;

while(1) {
   if (($server_pid = fork()) == 0) {
      $SIG{USR1} = \&success;
      $SIG{USR2} = \&next_pool;
      $SIG{PIPE}  = \&stop_pipe;
      $SIG{CHLD}  = \&stop_child;
      my $server_port = 1234;
      my $pass_string = 'cazz0';
      my $status_file = '.status';
      my $serial = 0;
 
      socket(SERVER, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
      setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1);
      my $my_addr = sockaddr_in($server_port, INADDR_ANY);
      bind(SERVER, $my_addr);
      listen(SERVER, SOMAXCONN);
      write_status("0:0:0:waiting:$serial");

      while(accept(CLIENT, SERVER)) {
 
         select CLIENT; $| = 1;
         select SERVER; $| = 1;
         select STDOUT; $| = 1;
         $ayt_serial = 0;
         send CLIENT, "OK\n", 0;
         recv CLIENT, $buff, 512, 0;
         chop($buff);
         chop($buff);
         if ($buff =~  /$pass_string/i) {
            send CLIENT, "OK\n", 0;
            recv CLIENT, $buff, 512, 0;
            chop($buff);
            chop($buff);
            while ($buff !~ /^EXIT/i) {
               if ($buff =~ /^DATA/i) {
                  if ($crack_status) {
                     send CLIENT, "\n\nERROR   PROZESS LAEUFT BEREITS\n\n", 0;
                  } else {
		     my @splitter;
	             write_status("0:0:0:waiting:$serial");
      	             @splitter = split(/\s+?/, $buff);
                     ($username, $password, $chars, $pool, $stellen) = split(/:/, $splitter[1], 5);
 
	             if (($username eq "") ||  ($password eq "") || ($chars !~ /0|1|2/) || ($pool eq "") || ($stellen !~ /4|5|6|7|8/)) {
                        send CLIENT, "ERROR  FALSCHE EINGABE\n\n", 0;
                     } else {
                        $crack_status = 1;
                        send CLIENT, "OK\tvorgang laeuft\n\n", 0;
#
# wenn maschine zu langsam crackt, und der status abgefragt wird,
# wuerde immer noch das ...:0:waiting .. im status stehen, drum wird waiting auf
# continue geaendert
#
			write_status("0:0:continue:$serial");
			my @container;
                        $container[0] = "abcdefghijklmnopqrstuvwxyz-_.";
		        $container[1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-_.";
		        $container[2] = "1234567890abcdefghijklmnopqrstuvwxyz-_.";
		        $container[3] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ-_.";
		        $container[4] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.";
		        $container[5] = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.";
	  	        $pool_content = $container[$chars];
                        @characters = split(/ */, $pool_content);
#
#  hier den crack prozess abforken, damit allenfalls
#  ein exit gelesen und abgebrochen werden kann.
#                 
                        if (($pid = fork()) == 0) {
#
#  status-variablen initialisieren
#
		           open(STATUS, "$status_file") || die "$status_file: $!\n";
                           chomp($entry = <STATUS>);
                           $serial = (split(/:/,$entry))[4];
                           close(STATUS);
                           local $ayt_serial = 0;
                           local $crack_status = 0;
                           local $cracks = 0;
                           local $cracks_per_second = 0;
		           local $total_cracks = 0;
	  	           local $start_time = time;
                           local $stellen = 4 if $stellen < 4;


                           if ($stellen == 4) {
		              foreach $1 (@characters) {
                                 foreach $2 (@characters) {
                                    foreach $3 (@characters) {
                                       $string = $pool . $1 . $2 . $3;
                                       crack($string, $password);
                                    }
                                 }   
                              } 
	  	           }
                           if ($stellen == 5) {
		              foreach $1 (@characters) {
                                 foreach $2 (@characters) {
                                    foreach $3 (@characters) {
                                       foreach $4 (@characters) {
                                          $string = $pool . $1 . $2 . $3 . $4;
                                          crack($string, $password);
                                       }
                                    }
                                 }   
                              } 
		           }
                           if ($stellen == 6) {
		              foreach $1 (@characters) {
                                 foreach $2 (@characters) {
                                    foreach $3 (@characters) {
                                       foreach $4 (@characters) {
                                          foreach $5 (@characters) {
                                             $string = $pool . $1 . $2 . $3 . $4 . $5;
                                             crack($string, $password);
                                          }
                                       }
                                    }
                                 }   
                              } 
		           }
                           if ($stellen == 7) {
		              foreach $1 (@characters) {
                                 foreach $2 (@characters) {
                                    foreach $3 (@characters) {
                                       foreach $4 (@characters) {
                                          foreach $5 (@characters) {
                                             foreach $6 (@characters) {
                                                $string = $pool . $1 . $2 . $3 . $4 . $5 . $6;
                                                crack($string, $password);
                                             }
                                          }
                                       }
                                    }
                                 }   
                              } 
		           }
                           if ($stellen == 8) {
		              foreach $1 (@characters) {
                                 foreach $2 (@characters) {
                                    foreach $3 (@characters) {
                                       foreach $4 (@characters) {
                                          foreach $5 (@characters) {
                                             foreach $6 (@characters) {
                                                foreach $7 (@characters) {
                                                   $string = $pool . $1 . $2 . $3 . $4 . $5 . $6 . $7;
                                                   crack($string, $password);
                                                }
                                             }
                                          }
                                       }
                                    }
			         }   
                              } 
		           }

                           sub crack {
                              my($word) = shift;
                              my($pw)   = shift;
                              my($salt) = substr($pw,0, 2);
                              my($compare) = crypt($word, $salt);
                              $cracks++;
#
# teil gecrackt, client mitteilen, dass erfolgreich 
# beendet werden kann und abbrechen
#
                              if ($pw eq $compare) {
			         $time = time - $start_time;
                                 $total_cracks = $total_cracks + $cracks;
			         $total_cracks = 1 if $total_cracks == 0;
		  	         $time = 1 if $time == 0;
			         $average_cracks = int $total_cracks / $time;
	                         write_status("$time:$total_cracks:$average_cracks:cracked:$username:$string");
			         kill('USR1', getppid);
			         exit;
                              }   

#
#  filehandle fuer das status file oeffnen.
#  koennte theoretisch auch mit einem separaten
#  socket erledigt werden, ich will aber die ganze
#  arbeit und kommunikation der client-seite ueber-
#  lassen.
#

			      if ($cracks == 5000) {
			
#
# alte serialnummer um 1 erhoehen. da bei einem fork
# der wert offenbar nicht beibehalten werden kann, wird
# dieser ins status file geschrieben
#

		     
		 	         $serial++;
		 	         $time = time - $start_time;
			         $time = 1 if $time == 0;
			         $total_cracks = $total_cracks + $cracks;
			         $average_cracks = int $total_cracks / $time;
			         $cracks = 0;
			         write_status("$time:$total_cracks:$average_cracks:continue:$serial");
 
		    	      }
                           }   # end von sub crack

#
# wenn fertig, parent mitteilen, dass der status 
# auf null gesetzt wird, status in status file reinschmeissen
# und child process beenden 
#

		           $serial++;
		           $time = time - $start_time;
		           $total_cracks = $total_cracks + $cracks;
		           $average_cracks = int $total_cracks / $time;
		           write_status("$time:$total_cracks:$average_cracks:next:$serial");
#	    	           print "$time:$total_cracks:$average_cracks:next:$serial\n";
		           kill('USR2', getppid);
		           exit;
                        }
                     }
  	          }
               } elsif ($buff =~/^STAT/i) {
                  if(open(STATUS, $status_file)) {
                     $raw_status = <STATUS>;
 	             chomp($raw_status);
                     close(STATUS);
	  	     send CLIENT, "OK  $raw_status\n", 0;
		  } else {
                     send CLIENT, "ERROR  STATUS FILE KONNTE NICHT GEOEFFNET WERDEN\n", 0;
                  }
               } elsif ($buff =~ /^AYT/i) {
	          send CLIENT, "OK $ayt_serial  ", 0;
	          $ayt_serial++
   	       } else {
                  send CLIENT, "\n\nERROR  BEFEHLE: DATA, STAT, AYT, EXIT\n\n", 0;
               }
               recv CLIENT, $buff, 512, 0;
               chop($buff);
               chop($buff);
            }
            $crack_status = 0;
            if ($pid) {
               kill('TERM', $pid);
            }
       
            chomp($entry = <STATUS>);
            $serial = (split(/:/,$entry))[4];
            write_status("0:0:0:waiting:$serial");
            send CLIENT, "OK\n", 0;

      
         } else {
            send CLIENT, "ERROR  FALSCHES PASSWORT\n\n", 0;
         }
      }

      sub success {
         $SIG{USR1} = \&success;
         $crack_status = 0;
         $ret_child = waitpid($pid,0);
      }

      sub next_pool {
         $SIG{USR2} = \&next_pool;
         $crack_status = 0;
         $ret_child = waitpid($pid,0);
      }

      sub write_status {
         my $string = shift;
         open(STATUS, ">$status_file") || die "$status_file: $!\n";
         print STATUS $string;
         close(STATUS);
      }

      sub stop_pipe {
         $SIG{PIPE} = \&stop_pipe;
         if ($pid) {
            kill('TERM', $pid);
         }
         $ret_child = waitpid($pid,0);
	 kill('TERM', $$);
      }
      
      sub stop_child {
         $SIG{CHILD} = \&stop_child;
         $ret_child = waitpid($pid,0);
      }

   } else {
      $ret_child = waitpid($server_pid,0);
      sleep(2);
   }   
}

