D4708
4 years ago
10 changed files with 586 additions and 0 deletions
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
<?php |
||||
|
||||
// Debug |
||||
ini_set('display_errors', '1'); |
||||
ini_set('display_startup_errors', '1'); |
||||
error_reporting(E_ALL); |
||||
|
||||
// Twister |
||||
define('TWISTER_PROTOCOL', 'http'); |
||||
define('TWISTER_HOST', 'localhost'); |
||||
define('TWISTER_PORT', 28332); |
||||
define('TWISTER_USERNAME', ''); |
||||
define('TWISTER_PASSWORD', ''); |
||||
|
||||
// Geoplugin |
||||
define('GEOPLUGIN_PROTOCOL', 'http'); |
||||
define('GEOPLUGIN_HOST', 'www.geoplugin.net'); |
||||
define('GEOPLUGIN_PORT', 80); |
||||
|
||||
// Torproject |
||||
define('TORPROJECT_PROTOCOL', 'https'); |
||||
define('TORPROJECT_HOST', 'check.torproject.org'); |
||||
define('TORPROJECT_PORT', 443); |
||||
|
||||
// DB |
||||
define('DB_HOSTNAME', 'localhost'); |
||||
define('DB_PORT', '3306'); |
||||
define('DB_DATABASE', ''); |
||||
define('DB_USERNAME', ''); |
||||
define('DB_PASSWORD', ''); |
||||
|
||||
// Options |
||||
define('EMAIL_ONLINE_PEERS', false); // email address|false |
||||
define('EMAIL_OFFLINE_PEERS', false); // email address|false |
||||
define('EMAIL_NEW_PEERS', false); // email address|false |
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
<?php |
||||
|
||||
class Curl { |
||||
|
||||
private $_curl; |
||||
private $_protocol; |
||||
private $_host; |
||||
private $_port; |
||||
|
||||
public function __construct($protocol, $host, $port, $username = false, $password = false) { |
||||
|
||||
$this->_protocol = $protocol; |
||||
$this->_host = $host; |
||||
$this->_port = $port; |
||||
|
||||
$this->_curl = curl_init(); |
||||
|
||||
|
||||
if ($username && $password) { |
||||
$headers = [ |
||||
'Content-Type: application/json', |
||||
sprintf('Authorization: Basic %s', base64_encode($username . ':' . $password)), |
||||
]; |
||||
} else { |
||||
$headers = [ |
||||
'Content-Type: application/json', |
||||
]; |
||||
} |
||||
|
||||
curl_setopt_array($this->_curl, [CURLOPT_RETURNTRANSFER => true, |
||||
CURLOPT_FOLLOWLOCATION => true, |
||||
CURLOPT_FRESH_CONNECT => true, |
||||
//CURLOPT_VERBOSE => true, |
||||
CURLOPT_HTTPHEADER => $headers, |
||||
]); |
||||
} |
||||
|
||||
public function __destruct() { |
||||
curl_close($this->_curl); |
||||
} |
||||
|
||||
public function sanitize($string, $lowercase = false) { |
||||
|
||||
// Lowercase |
||||
if ($lowercase) { |
||||
$string = mb_strtolower($string, 'UTF-8'); |
||||
} |
||||
|
||||
return $string; |
||||
} |
||||
|
||||
protected function prepare($uri, $method, $timeout = 30, array $postfields = [], $verify_ssl = true, $verify_host = true) { |
||||
|
||||
curl_setopt($this->_curl, CURLOPT_URL, $this->_protocol . '://' . $this->_host . ':' . $this->_port . '/' . $uri); |
||||
curl_setopt($this->_curl, CURLOPT_CONNECTTIMEOUT, $timeout); |
||||
curl_setopt($this->_curl, CURLOPT_TIMEOUT, $timeout); |
||||
|
||||
if ($verify_ssl === false) { |
||||
curl_setopt($this->_curl, CURLOPT_SSL_VERIFYPEER, false); |
||||
} |
||||
|
||||
if ($verify_host === false) { |
||||
curl_setopt($this->_curl, CURLOPT_SSL_VERIFYHOST, false); |
||||
} |
||||
|
||||
if ($method) { |
||||
curl_setopt($this->_curl, CURLOPT_CUSTOMREQUEST, $method); |
||||
} |
||||
|
||||
if ($method == 'POST' && $postfields) { |
||||
curl_setopt($this->_curl, CURLOPT_POSTFIELDS, json_encode($postfields)); |
||||
} |
||||
} |
||||
|
||||
protected function execute($json = true) { |
||||
|
||||
$response = curl_exec($this->_curl); |
||||
$errorNumber = curl_errno($this->_curl); |
||||
$errorText = curl_error($this->_curl); |
||||
|
||||
if ($errorNumber > 0) { |
||||
return false; |
||||
} |
||||
|
||||
if ($response) { |
||||
if ($json) { |
||||
return json_decode($response, true); |
||||
} else { |
||||
return $response; |
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
} |
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
<?php |
||||
|
||||
class CurlGeoPlugin extends Curl { |
||||
|
||||
public function getLocation($ip) { |
||||
|
||||
$this->prepare('json.gp?ip=' . $ip, 'GET'); |
||||
|
||||
if ($response = $this->execute()) { |
||||
|
||||
switch (false) { |
||||
case isset($response['geoplugin_city']): |
||||
case isset($response['geoplugin_countryCode']): |
||||
case isset($response['geoplugin_latitude']): |
||||
case isset($response['geoplugin_longitude']): |
||||
|
||||
return false; |
||||
} |
||||
|
||||
return $response; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
} |
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
<?php |
||||
|
||||
class CurlPeer extends Curl { |
||||
|
||||
public function getAll() { |
||||
|
||||
$this->prepare('', 'POST', 30, ['jsonrpc' => '2.0', |
||||
'method' => 'getpeerinfo', |
||||
'params' => [], |
||||
'id' => 1], false, false); |
||||
|
||||
if ($response = $this->execute()) { |
||||
|
||||
if (isset($response['result'])) { |
||||
|
||||
$peers = []; |
||||
foreach ($response['result'] as $peer) { |
||||
|
||||
# @TODO validate |
||||
if (isset($peer['addr'])) { |
||||
$peers[] = $peer; |
||||
} |
||||
} |
||||
|
||||
return $peers; |
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
} |
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
<?php |
||||
|
||||
class CurlTorProject extends Curl { |
||||
|
||||
public function getExitNodes() { |
||||
|
||||
$this->prepare('torbulkexitlist', 'GET'); |
||||
|
||||
if ($response = $this->execute(false)) { |
||||
|
||||
$list = explode("\n", $response); |
||||
|
||||
if (count($list)) { |
||||
|
||||
$sanitized = []; |
||||
foreach ($list as $ip) { |
||||
|
||||
$ip = $this->sanitize($ip); |
||||
|
||||
if ($ip) { |
||||
$sanitized[] = $ip; |
||||
} |
||||
|
||||
} |
||||
|
||||
return $sanitized; |
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
} |
@ -0,0 +1,176 @@
@@ -0,0 +1,176 @@
|
||||
<?php |
||||
|
||||
class ModelIp extends Model { |
||||
|
||||
public function exists($address) { |
||||
|
||||
try { |
||||
|
||||
$query = $this->db->prepare('SELECT `ipId` FROM `ip` WHERE `address` = ? LIMIT 1'); |
||||
|
||||
$query->execute([$address]); |
||||
|
||||
return $query->rowCount() ? $query->fetch()['ipId'] : false; |
||||
|
||||
} catch (PDOException $e) { |
||||
trigger_error($e->getMessage()); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function getIps() { |
||||
|
||||
try { |
||||
|
||||
$query = $this->db->prepare('SELECT `ipId`, `address` FROM `ip`'); |
||||
|
||||
$query->execute(); |
||||
|
||||
return $query->rowCount() ? $query->fetchAll() : []; |
||||
|
||||
} catch (PDOException $e) { |
||||
trigger_error($e->getMessage()); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function getIsOnlineIps() { |
||||
|
||||
try { |
||||
|
||||
$query = $this->db->prepare('SELECT `address` FROM `ip` WHERE `isOnline` = "1"'); |
||||
|
||||
$query->execute(); |
||||
|
||||
return $query->rowCount() ? $query->fetchAll() : []; |
||||
|
||||
} catch (PDOException $e) { |
||||
trigger_error($e->getMessage()); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function getIsOfflineIps() { |
||||
|
||||
try { |
||||
|
||||
$query = $this->db->prepare('SELECT `address` FROM `ip` WHERE `isOnline` = "0"'); |
||||
|
||||
$query->execute(); |
||||
|
||||
return $query->rowCount() ? $query->fetchAll() : []; |
||||
|
||||
} catch (PDOException $e) { |
||||
trigger_error($e->getMessage()); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function add($address, $port) { |
||||
|
||||
try { |
||||
|
||||
$query = $this->db->prepare('INSERT INTO `ip` SET `address` = ?, `port` = ?, `isOnline` = "0", `isTOR` = "0"'); |
||||
|
||||
$query->execute([$address, $port]); |
||||
|
||||
return $this->db->lastInsertId(); |
||||
|
||||
} catch (PDOException $e) { |
||||
trigger_error($e->getMessage()); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function addOnline($ipId, $startingHeight, $timeConnection, $timeLastSend, $timeLastReceive) { |
||||
|
||||
try { |
||||
|
||||
$query = $this->db->prepare('INSERT INTO `ipOnline` SET `ipId` = ?, |
||||
`startingHeight` = ?, |
||||
`timeConnection` = ?, |
||||
`timeLastSend` = ?, |
||||
`timeLastReceive` = ?, |
||||
|
||||
`timeAdded` = UNIX_TIMESTAMP()'); |
||||
|
||||
$query->execute([$ipId, $startingHeight, $timeConnection, $timeLastSend, $timeLastReceive]); |
||||
|
||||
return $this->db->lastInsertId(); |
||||
|
||||
} catch (PDOException $e) { |
||||
trigger_error($e->getMessage()); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function updateGeoData($ipId, $countryCode, $city, $latitude, $longitude) { |
||||
|
||||
try { |
||||
|
||||
$query = $this->db->prepare('UPDATE `ip` SET `countryCode` = ?, |
||||
`city` = ?, |
||||
`latitude` = ?, |
||||
`longitude` = ? |
||||
|
||||
WHERE `ipId` = ? |
||||
LIMIT 1'); |
||||
|
||||
$query->execute([$countryCode, $city, $latitude, $longitude, $ipId]); |
||||
|
||||
return $query->rowCount(); |
||||
|
||||
} catch (PDOException $e) { |
||||
trigger_error($e->getMessage()); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function resetIsOnline() { |
||||
|
||||
try { |
||||
|
||||
$query = $this->db->prepare('UPDATE `ip` SET `isOnline` = "0"'); |
||||
|
||||
$query->execute(); |
||||
|
||||
return $query->rowCount(); |
||||
|
||||
} catch (PDOException $e) { |
||||
trigger_error($e->getMessage()); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function setIsOnline($ipId) { |
||||
|
||||
try { |
||||
|
||||
$query = $this->db->prepare('UPDATE `ip` SET `isOnline` = "1" WHERE `ipId` = ? LIMIT 1'); |
||||
|
||||
$query->execute([$ipId]); |
||||
|
||||
return $query->rowCount(); |
||||
|
||||
} catch (PDOException $e) { |
||||
trigger_error($e->getMessage()); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function setIsTOR($ipId) { |
||||
|
||||
try { |
||||
|
||||
$query = $this->db->prepare('UPDATE `ip` SET `isTOR` = "1" WHERE `ipId` = ? LIMIT 1'); |
||||
|
||||
$query->execute([$ipId]); |
||||
|
||||
return $query->rowCount(); |
||||
|
||||
} catch (PDOException $e) { |
||||
trigger_error($e->getMessage()); |
||||
return false; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
<?php |
||||
|
||||
class ModelLog extends Model { |
||||
|
||||
public function add($message) { |
||||
|
||||
try { |
||||
|
||||
$query = $this->db->prepare('INSERT INTO `log` SET `message` = ?, `timeAdded` = UNIX_TIMESTAMP()'); |
||||
|
||||
$query->execute([$message]); |
||||
|
||||
return $this->db->lastInsertId(); |
||||
|
||||
} catch (PDOException $e) { |
||||
trigger_error($e->getMessage()); |
||||
return false; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
|
||||
<?php |
||||
|
||||
class Model { |
||||
|
||||
protected $db; |
||||
|
||||
public function __construct($database, $hostname, $port, $user, $password) { |
||||
try { |
||||
$this->db = new PDO('mysql:dbname=' . $database . ';host=' . $hostname . ';port=' . $port . ';charset=utf8', $user, $password, [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8']); |
||||
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
||||
$this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); |
||||
} catch(PDOException $e) { |
||||
trigger_error($e->getMessage()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,119 @@
@@ -0,0 +1,119 @@
|
||||
<?php |
||||
|
||||
require(__DIR__ . '/config.php'); |
||||
|
||||
require(__DIR__ . '/curl/curl.php'); |
||||
require(__DIR__ . '/curl/peer.php'); |
||||
require(__DIR__ . '/curl/geoplugin.php'); |
||||
|
||||
require(__DIR__ . '/model/model.php'); |
||||
require(__DIR__ . '/model/ip.php'); |
||||
require(__DIR__ . '/model/log.php'); |
||||
|
||||
$curlPeer = new CurlPeer(TWISTER_PROTOCOL, TWISTER_HOST, TWISTER_PORT, TWISTER_USERNAME, TWISTER_PASSWORD); |
||||
$curlGeoPlugin = new CurlGeoPlugin(GEOPLUGIN_PROTOCOL, GEOPLUGIN_HOST, GEOPLUGIN_PORT); |
||||
|
||||
$modelIp = new ModelIp(DB_DATABASE, DB_HOSTNAME, DB_PORT, DB_USERNAME, DB_PASSWORD); |
||||
$modelLog = new ModelLog(DB_DATABASE, DB_HOSTNAME, DB_PORT, DB_USERNAME, DB_PASSWORD); |
||||
|
||||
$isOnlinePeers = []; |
||||
$isOfflinePeers = []; |
||||
|
||||
$toOnlinePeers = []; |
||||
$toOfflinePeers = []; |
||||
$newPeers = []; |
||||
$onlinePeers = []; |
||||
|
||||
// Get online peers list |
||||
foreach ($modelIp->getIsOnlineIps() as $isOnlinePeer) { |
||||
$isOnlinePeers[] = $isOnlinePeer['address']; |
||||
} |
||||
|
||||
// Get offline peers list |
||||
foreach ($modelIp->getIsOfflineIps() as $isOfflinePeer) { |
||||
$isOfflinePeers[] = $isOfflinePeer['address']; |
||||
} |
||||
|
||||
// Reset peers online |
||||
$modelIp->resetIsOnline(); |
||||
|
||||
// Get current peers |
||||
if ($peers = $curlPeer->getAll()) { |
||||
|
||||
foreach ($peers as $peer) { |
||||
|
||||
if (isset($peer['addr'])) { |
||||
|
||||
// Parse response |
||||
if (false !== preg_match('/(.*):(\d+)$/', $peer['addr'], $matches)) { |
||||
|
||||
if (isset($matches[1]) && isset($matches[2])) { |
||||
|
||||
// IP exist |
||||
if (!$ipId = $modelIp->exists($matches[1])) { |
||||
|
||||
// Save IP |
||||
$ipId = $modelIp->add($matches[1], $matches[2]); |
||||
|
||||
// Get geo info |
||||
if ($location = $curlGeoPlugin->getLocation($matches[1])) { |
||||
|
||||
$modelIp->updateGeoData($ipId, |
||||
$location['geoplugin_countryCode'], |
||||
$location['geoplugin_city'], |
||||
$location['geoplugin_latitude'], |
||||
$location['geoplugin_longitude']); |
||||
} else { |
||||
$modelLog->add(_('Could not receive geolocation details')); |
||||
} |
||||
|
||||
$newPeers[] = $matches[1]; |
||||
} |
||||
|
||||
// Peer switching online |
||||
if (in_array($matches[1], $isOfflinePeers)) { |
||||
$toOnlinePeers[] = $matches[1]; |
||||
} |
||||
|
||||
// Add online peers to registry |
||||
$onlinePeers[] = $matches[1]; |
||||
|
||||
// Set peer as online |
||||
$modelIp->setIsOnline($ipId); |
||||
|
||||
// Update online time |
||||
$modelIp->addOnline($ipId, |
||||
$peer['startingheight'], |
||||
$peer['conntime'], |
||||
$peer['lastsend'], |
||||
$peer['lastrecv']); |
||||
} else { |
||||
$modelLog->add(_('Could not extract peer address or port')); |
||||
} |
||||
} else { |
||||
$modelLog->add(_('Could not parse peer address')); |
||||
} |
||||
} else { |
||||
$modelLog->add(_('Could not parse RPC response')); |
||||
} |
||||
} |
||||
} else { |
||||
$modelLog->add(_('Could not connect to twister peer')); |
||||
} |
||||
|
||||
// Alert if peer(s) going to offline |
||||
$toOfflinePeers = array_diff($isOnlinePeers, $onlinePeers); |
||||
|
||||
if (EMAIL_OFFLINE_PEERS && $toOfflinePeers) { |
||||
mail(EMAIL_OFFLINE_PEERS, sprintf(_('Peer(s) switched to offline')), implode("\r\n", $toOfflinePeers)); |
||||
} |
||||
|
||||
// Alert if peer(s) going to online |
||||
if (EMAIL_ONLINE_PEERS && $toOnlinePeers) { |
||||
mail(EMAIL_ONLINE_PEERS, sprintf(_('Peer(s) switched to online')), implode("\r\n", $toOnlinePeers)); |
||||
} |
||||
|
||||
// Alert if new peer(s) available |
||||
if (EMAIL_NEW_PEERS && $newPeers) { |
||||
mail(EMAIL_NEW_PEERS, sprintf(_('New peer(s) added')), implode("\r\n", $newPeers)); |
||||
} |
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
<?php |
||||
|
||||
require(__DIR__ . '/config.php'); |
||||
|
||||
require(__DIR__ . '/curl/curl.php'); |
||||
require(__DIR__ . '/curl/torproject.php'); |
||||
|
||||
require(__DIR__ . '/model/model.php'); |
||||
require(__DIR__ . '/model/ip.php'); |
||||
require(__DIR__ . '/model/log.php'); |
||||
|
||||
$curlTorProject = new CurlTorProject(TORPROJECT_PROTOCOL, TORPROJECT_HOST, TORPROJECT_PORT); |
||||
|
||||
$modelIp = new ModelIp(DB_DATABASE, DB_HOSTNAME, DB_PORT, DB_USERNAME, DB_PASSWORD); |
||||
$modelLog = new ModelLog(DB_DATABASE, DB_HOSTNAME, DB_PORT, DB_USERNAME, DB_PASSWORD); |
||||
|
||||
$exitNodes = []; |
||||
|
||||
// Get TOR registry |
||||
if ($torProjectExitNodes = $curlTorProject->getExitNodes()) { |
||||
|
||||
foreach ($torProjectExitNodes as $exitNode) { |
||||
$exitNodes[] = $exitNode; |
||||
} |
||||
|
||||
// Get IPs |
||||
foreach ($modelIp->getIps() as $ip) { |
||||
|
||||
if (in_array($ip['address'], $exitNodes)) { |
||||
$modelIp->setIsTOR($ip['ipId']); |
||||
} |
||||
} |
||||
|
||||
} else { |
||||
|
||||
$modelLog->add(_('Could not parse TorProject response')); |
||||
} |
Loading…
Reference in new issue