From e17b16030df585c7ceb69721629138dc827fa599 Mon Sep 17 00:00:00 2001 From: D4708 Date: Sun, 30 May 2021 01:33:36 +0300 Subject: [PATCH] initial commit --- config-default.php | 35 +++++++++ curl/curl.php | 95 ++++++++++++++++++++++++ curl/geoplugin.php | 25 +++++++ curl/peer.php | 31 ++++++++ curl/torproject.php | 32 ++++++++ model/ip.php | 176 ++++++++++++++++++++++++++++++++++++++++++++ model/log.php | 20 +++++ model/model.php | 16 ++++ peer.php | 119 ++++++++++++++++++++++++++++++ tor.php | 37 ++++++++++ 10 files changed, 586 insertions(+) create mode 100644 config-default.php create mode 100644 curl/curl.php create mode 100644 curl/geoplugin.php create mode 100644 curl/peer.php create mode 100644 curl/torproject.php create mode 100644 model/ip.php create mode 100644 model/log.php create mode 100644 model/model.php create mode 100644 peer.php create mode 100644 tor.php diff --git a/config-default.php b/config-default.php new file mode 100644 index 0000000..76bc4ac --- /dev/null +++ b/config-default.php @@ -0,0 +1,35 @@ +_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; + } +} diff --git a/curl/geoplugin.php b/curl/geoplugin.php new file mode 100644 index 0000000..992fb34 --- /dev/null +++ b/curl/geoplugin.php @@ -0,0 +1,25 @@ +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; + } +} diff --git a/curl/peer.php b/curl/peer.php new file mode 100644 index 0000000..c48ab80 --- /dev/null +++ b/curl/peer.php @@ -0,0 +1,31 @@ +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; + } +} diff --git a/curl/torproject.php b/curl/torproject.php new file mode 100644 index 0000000..d77ae5c --- /dev/null +++ b/curl/torproject.php @@ -0,0 +1,32 @@ +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; + } +} diff --git a/model/ip.php b/model/ip.php new file mode 100644 index 0000000..8328192 --- /dev/null +++ b/model/ip.php @@ -0,0 +1,176 @@ +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; + } + } +} diff --git a/model/log.php b/model/log.php new file mode 100644 index 0000000..17265ef --- /dev/null +++ b/model/log.php @@ -0,0 +1,20 @@ +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; + } + } +} diff --git a/model/model.php b/model/model.php new file mode 100644 index 0000000..1d41126 --- /dev/null +++ b/model/model.php @@ -0,0 +1,16 @@ +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()); + } + } +} diff --git a/peer.php b/peer.php new file mode 100644 index 0000000..a99775d --- /dev/null +++ b/peer.php @@ -0,0 +1,119 @@ +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)); +} diff --git a/tor.php b/tor.php new file mode 100644 index 0000000..18105e6 --- /dev/null +++ b/tor.php @@ -0,0 +1,37 @@ +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')); +}