* @version 3.0.0
* @copyright Copyright (c) 2009 - 2017 Cracker Tracker Team
* @license GNU GPL 3.0 or any newer version
* @link http://www.shipsimu.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Function to aquire a database link
function aquireCrackerTrackerDatabaseLink () {
// Is the link up?
if ((!isCrackerTrackerDatabaseLinkUp()) && (!empty($GLOBALS['ctracker_host'])) && (!empty($GLOBALS['ctracker_dbname'])) && (!empty($GLOBALS['ctracker_user']))) {
// Then connect to the database
$GLOBALS['ctracker_link'] = mysqli_connect($GLOBALS['ctracker_host'], $GLOBALS['ctracker_user'], $GLOBALS['ctracker_password'], $GLOBALS['ctracker_dbname']) or crackerTrackerDatabaseError(__FUNCTION__, __LINE__);
// Check on connection and config table
if (!isCrackerTrackerDatabaseLinkUp()) {
// Connect didn't work
crackerTrackerDatabaseError(__FUNCTION__, __LINE__);
} elseif (isCrackerTrackerTableCreated('ctracker_config')) {
// Load the config
crackerTrackerLoadConfig();
}
} else {
// Init fake config
crackerTrackerInitFakeConfig();
}
}
// Inits a fake configurtation
function crackerTrackerInitFakeConfig () {
// Set the array
$GLOBALS['ctracker_config'] = [
'ctracker_alert_user' => 'Y',
];
}
// Checks if the link is up
function isCrackerTrackerDatabaseLinkUp () {
// Is the instance at least set?
if (isset($GLOBALS['ctracker_link'])) {
// Debug message
//* DEBUG: */ error_log('isset='.intval(isset($GLOBALS['ctracker_link'])) . ',is_object=' . intval(is_object($GLOBALS['ctracker_link'])) . ',mysqli_connect_errno=' . mysqli_connect_errno());
} else {
// Not set!
//* DEBUG: */ error_log('ctracker_link not set.');
}
return ((isset($GLOBALS['ctracker_link'])) && (is_object($GLOBALS['ctracker_link'])) && (mysqli_connect_errno() == 0));
}
// Database error detected
function crackerTrackerDatabaseError (string $file, int $line) {
// Should we debug?
if (isCrackerTrackerDebug()) {
// Output error
print 'Function : ' . $file . '
' . PHP_EOL;
print 'Line : ' . $line . '
' . PHP_EOL;
if (isset($GLOBALS['ctracker_link']) && $GLOBALS['ctracker_link'] !== false) {
print 'MySQL error : ' . mysqli_error($GLOBALS['ctracker_link']) . '
' . PHP_EOL;
} else {
print 'No MySQLi available.
' . PHP_EOL;
}
if (isset($GLOBALS['ctracker_last_sql'])) {
print 'Last SQL : '. $GLOBALS['ctracker_last_sql'] . '
' . PHP_EOL;
} else {
print 'No last SQL command available.
' . PHP_EOL;
}
}
// Currently only die here
crackerTrackerDie();
}
// Closes a maybe open database link
function crackerTrackerCloseDatabaseLink () {
// The link should be up here
if (!isCrackerTrackerDatabaseLinkUp()) {
// Throw exception
throw new BadFunctionCallException('Link is not up.');
}
// Did it work?
if (!mysqli_close($GLOBALS['ctracker_link'])) {
// Attempt has failed
crackerTrackerDatabaseError(__FUNCTION__, __LINE__);
}
}
// Inserts given array, if IP/check_get combination was not found
function crackerTrackerInsertArray (string $table, array $rowData) {
// Is it found?
if (!isCrackerTrackerEntryFound($rowData)) {
// Reset insert id
$GLOBALS['ctracker_last_insert_id'] = false;
// Run it
runCrackerTrackerSql(sprintf("INSERT INTO `%s` (`%s`) VALUES(%s)",
$table,
implode('`,`', array_keys($rowData)),
implode_secure($rowData)
), __FUNCTION__, __LINE__);
// Remember the last insert id
$GLOBALS['ctracker_last_insert_id'] = mysqli_insert_id($GLOBALS['ctracker_link']) or crackerTrackerDatabaseError(__FUNCTION__, __LINE__);
} else {
// Only update the entry
updateCrackerTrackerEntry($rowData);
}
}
// Updates a given entry by just counting it up
function updateCrackerTrackerEntry (array $rowData, string $countColumn = 'count') {
// The link should be up here
if (!isCrackerTrackerDatabaseLinkUp()) {
// Throw exception
throw new BadFunctionCallException('Link is not up.');
}
// Run the SQL and check if we have one line
runCrackerTrackerSql(sprintf("UPDATE `ctracker_data` SET `%s`=`%s`+1 WHERE (`remote_addr`='%s' AND `proxy_addr`='%s') LIMIT 1",
$countColumn,
$countColumn,
crackerTrackerEscapeString($rowData['remote_addr']),
crackerTrackerEscapeString($rowData['proxy_addr'])
), __FUNCTION__, __LINE__);
}
// Checks if an entry with IP/check_get/domain combination is there
function isCrackerTrackerEntryFound (array $rowData) {
// The link should be up here
if (!isCrackerTrackerDatabaseLinkUp()) {
// Throw exception
throw new BadFunctionCallException('Link is not up.');
}
// Run the SQL and check if we have one line
$result = runCrackerTrackerSql(sprintf("SELECT `id` FROM `ctracker_data` WHERE (`remote_addr`='%s' OR `proxy_addr`='%s') AND `check_get` = '%s' AND `server_name`='%s' LIMIT 1'",
crackerTrackerEscapeString($rowData['remote_addr']),
crackerTrackerEscapeString($rowData['proxy_addr']),
crackerTrackerEscapeString($rowData['check_get']),
crackerTrackerEscapeString($rowData['server_name'])
), __FUNCTION__, __LINE__);
// Check count of rows
return (mysqli_num_rows($result) == 1);
}
// Escapes the string
function crackerTrackerEscapeString (string $string) {
// Is the link up?
if (!isCrackerTrackerDatabaseLinkUp()) {
// Then we cant use mysqli_real_escape_string!
$string = htmlentities($string, ENT_QUOTES);
} elseif (function_exists('mysqli_real_escape_string')) {
// Use mysqli_real_escape_string()
$string = mysqli_real_escape_string($GLOBALS['ctracker_link'], $string);
} else {
// Use fall-back (bad!)
$string = htmlentities($string, ENT_QUOTES);
}
// Return the secured string
return $string;
}
// Runs an SQL query and checks for errors
function runCrackerTrackerSql (string $sqlString, string $function, int $line) {
// Is the link up?
if (!isCrackerTrackerDatabaseLinkUp()) {
// Abort here
crackerTrackerDie();
}
// Remember last SQL
$GLOBALS['ctracker_last_sql'] = $sqlString;
// Run the query
$GLOBALS['ctracker_last_result'] = mysqli_query($GLOBALS['ctracker_link'], $sqlString) or crackerTrackerDatabaseError($function, $line);
// And return it
return $GLOBALS['ctracker_last_result'];
}
// Checks wether a table was found
function isCrackerTrackerTableCreated (string $table) {
// Default is not found
$found = false;
// Run the query
$result = runCrackerTrackerSql('SHOW TABLES', __FUNCTION__, __LINE__);
// Is our table there?
while (list($tab) = mysqli_fetch_row($result)) {
// Debug message
//* NOISY-DEBUG: */ error_log('tab=' . $tab);
// Is the table there?
if ($tab == $table) {
// Okay, found. So abort
$found = true;
break;
}
}
// Free result
freeCrackerTrackerResult($result);
// Return result
return $found;
}
// Creates the given table with columns
function crackerTrackerCreateTable (string $table, array $columns, array $keys) {
// Begin the SQL
$sqlString = 'CREATE TABLE IF NOT EXISTS `' . $table . '` (';
// Add table name as first column
$sqlString .= '`' . $table . '` BIGINT ( 20 ) UNSIGNED NOT NULL AUTO_INCREMENT, ';
// Add all columns
foreach ($columns as $column=>$type) {
// Add this entry
$sqlString .= '`' . $column . '` ' . $type . ', ';
}
// Add table name as primary key
$sqlString .= 'PRIMARY KEY (`' . $table . '`), ';
// Add keys
foreach ($keys as $key => $type) {
// Add this entry
$sqlString .= '' . $type . ' (`' . $key . '`), ';
}
// Finish SQL
$sqlString = substr($sqlString, 0, -2) . ') TYPE=InnoDB';
// And run it
runCrackerTrackerSql($sqlString);
}
// Inits a table by inserting
function crackerTrackerInitTable (string $table) {
// Prepare SQL and run it
runCrackerTrackerSql(sprintf("INSERT INTO `%s` (`%s`) VALUES (NULL)'",
$table,
$table
));
}
// Updates the database scheme automatically
function crackerTrackerUpdateDatabaseScheme () {
// Is a link there?
if (!isCrackerTrackerDatabaseLinkUp()) {
// Abort here silently
return;
}
// Is the main config table there?
if (!isCrackerTrackerTableCreated('ctracker_config')) {
// Then do it for us
crackerTrackerCreateTable('ctracker_config', [
'ctracker_db_version' => 'BIGINT ( 20 ) UNSIGNED NOT NULL DEFAULT 0',
'ctracker_min_sleep' => 'SMALLINT ( 5 ) UNSIGNED NOT NULL DEFAULT 10',
'ctracker_max_sleep' => 'SMALLINT ( 5 ) UNSIGNED NOT NULL DEFAULT 30',
'ctracker_alert_user' => "ENUM('Y','N') NOT NULL DEFAULT 'Y'",
'ctracker_language' => "CHAR ( 2) NOT NULL DEFAULT 'en'"
], [
]);
// Init that table
crackerTrackerInitTable('ctracker_config');
}
// Init update array here
crackerTrackerInitUpdates();
// Run any SQL updates recursively
while (isset($GLOBALS['ctracker_updates'][getCrackerTrackerConfig('ctracker_db_version')])) {
// Run that updates
runCrackerTrackerUpdates(getCrackerTrackerConfig('ctracker_db_version'));
// Update config
runCrackerTrackerSql('UPDATE `ctracker_config` SET `ctracker_db_version`=`ctracker_db_version`+1 WHERE `ctracker_config`=1 LIMIT 1', __FUNCTION__, __LINE__);
// And count it up in the config array
$GLOBALS['ctracker_config']['ctracker_db_version']++;
}
}
// Load the configuration
function crackerTrackerLoadConfig () {
// Construct SQL command and run it
$result = runCrackerTrackerSql('SELECT * FROM `ctracker_config` WHERE `ctracker_config`=1 LIMIT 1', __FUNCTION__, __LINE__);
// And get it
$GLOBALS['ctracker_config'] = mysqli_fetch_array($result);
// Free result
freeCrackerTrackerResult($result);
}
// Getter for config
function getCrackerTrackerConfig (string $entry) {
// Is the config entry there?
if (!isset($GLOBALS['ctracker_config'][$entry])) {
// Then better die here, else we may have an endless loop
if (isCrackerTrackerDebug()) {
// Nicer message in debug mode
die('Configuration entry ' . $entry . ' missing!');
} else {
// die() on production systems
die();
}
}
// Return it
return $GLOBALS['ctracker_config'][$entry];
}
// Did the current IP already generated blocked attempts?
function isCrackerTrackerIpSuspicious () {
// Skip this silently if we have not config
if (!isCrackerTrackerDatabaseLinkUp()) {
// Skip this step silently, all is not suspicious
return false;
}
// Check if an entry is there
$result = runCrackerTrackerSql("SELECT COUNT(`id`) AS `cnt` FROM `ctracker_data` USE INDEX (`remote_proxy_last`) WHERE `remote_addr`='" . determineCrackerTrackerRealRemoteAddress() . "' OR `proxy_addr`='" . getenv('REMOTE_ADDR') . "' LIMIT 1", __FUNCTION__, __LINE__);
// Get row count
list($rows) = mysqli_fetch_row($result);
// Is there one entry?
$found = ($rows > 0);
// And again?
if ($found === true) {
// Yes, one is found, then load it
$result = runCrackerTrackerSql("SELECT SQL_SMALL_RESULT * FROM `ctracker_data` USE INDEX (`remote_proxy_last`) WHERE `remote_addr`='" . determineCrackerTrackerRealRemoteAddress() . "' OR `proxy_addr`='" . getenv('REMOTE_ADDR') . "' ORDER BY `last_attempt` DESC LIMIT 1", __FUNCTION__, __LINE__);
// Cache the entry
$GLOBALS['ctracker_last_suspicious_entry'] = mysqli_fetch_array($result);
}
// Free result
freeCrackerTrackerResult($result);
// Return the result
return $found;
}
// Does the current IP have a ticket?
function ifCrackerTrackerIpHasTicket () {
// We only give one ticket per IP!
$result = runCrackerTrackerSql("SELECT * FROM `ctracker_ticket` WHERE `ctracker_ticket_remote_addr`='" . determineCrackerTrackerRealRemoteAddress() . "' OR `ctracker_ticket_proxy_addr`='" . getenv('REMOTE_ADDR') . "' LIMIT 1", __FUNCTION__, __LINE__);
// Do we have a ticket?
$found = (mysqli_num_rows($result) == 1);
// And again?
if ($found === true) {
// Cache the ticket data
$GLOBALS['ctracker_last_ticket'] = mysqli_fetch_array($result);
}
// Free result
freeCrackerTrackerResult($result);
// Return the result
return $found;
}
// Adds a ticket based on given (mostly $_POST) data
function addCrackerTrackerTicket (array $data) {
// Prepare the array
$GLOBALS['ctracker_last_ticket'] = [
'ctracker_ticket_remote_addr' => determineCrackerTrackerRealRemoteAddress(),
'ctracker_ticket_proxy_addr' => getenv('REMOTE_ADDR'),
'ctracker_ticket_user_agent' => crackerTrackerUserAgent(),
'ctracker_ticket_name' => crackerTrackerSecureString($data['name']),
'ctracker_ticket_email' => crackerTrackerSecureString($data['email']),
'ctracker_ticket_comment' => crackerTrackerSecureString($data['comment'])
];
// Insert it
crackerTrackerInsertArray('ctracker_ticket', $GLOBALS['ctracker_last_ticket']);
// Is there an entry?
if ((isset($GLOBALS['ctracker_last_insert_id'])) && ($GLOBALS['ctracker_last_insert_id'] > 0)) {
// All fine, so prepare the link between ticket<->data
$data = [
'ctracker_ticket_id' => $GLOBALS['ctracker_last_insert_id'],
'ctracker_data_id' => $GLOBALS['ctracker_last_suspicious_entry']['id']
];
// And insert it as well
crackerTrackerInsertArray('ctracker_ticket_data', $data);
// Add ticket id again
$GLOBALS['ctracker_ticket'] = $data['ctracker_ticket_id'];
// Merge all data for emails
$GLOBALS['ctracker_last_ticket'] = array_merge($GLOBALS['ctracker_last_ticket'], $data);
// Is this also there?
if ((isset($GLOBALS['ctracker_last_insert_id'])) && ($GLOBALS['ctracker_last_insert_id'] > 0)) {
// All fine, so display "thank you page"
crackerTrackerLoadTemplate('add_ticket_thanks');
} else {
// Did not insert
crackerTrackerDie();
}
} else {
// Did not insert
crackerTrackerDie();
}
}
// Frees given result instance
function freeCrackerTrackerResult (mysqli_result $result) {
// Free result
$result->free();
}