From dc106e2975597350c2056a2c207a131d701fc392 Mon Sep 17 00:00:00 2001 From: Marc Hagen Date: Wed, 23 Nov 2022 11:47:51 +0100 Subject: [PATCH 01/26] Adding const used throughout the project --- LookingGlass.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/LookingGlass.php b/LookingGlass.php index 5cb8c9f..35193bb 100644 --- a/LookingGlass.php +++ b/LookingGlass.php @@ -1,4 +1,4 @@ - Date: Wed, 23 Nov 2022 11:48:14 +0100 Subject: [PATCH 02/26] Use consts --- LookingGlass.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/LookingGlass.php b/LookingGlass.php index 35193bb..49962e5 100644 --- a/LookingGlass.php +++ b/LookingGlass.php @@ -111,17 +111,20 @@ class LookingGlass if (!substr_count($host, '.')) { return ''; } + if (filter_var('https://' . $host, FILTER_VALIDATE_URL)) { if ($host = parse_url('https://' . $host, PHP_URL_HOST)) { - if ($type == 'ipv4' && isset(dns_get_record($host, DNS_A)[0]['ip'])) { + if ($type === self::IPV4 && isset(dns_get_record($host, DNS_A)[0]['ip'])) { return $host; } - if ($type == 'ipv6' && isset(dns_get_record($host, DNS_AAAA)[0]['ipv6'])) { + if ($type === self::IPV6 && isset(dns_get_record($host, DNS_AAAA)[0]['ipv6'])) { return $host; } + return ''; } } + return ''; } @@ -134,6 +137,10 @@ class LookingGlass */ public static function detectIpAddress(): string { + if (php_sapi_name() === 'cli') { + return '127.0.0.1'; + } + if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && filter_var($_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP)) { return $_SERVER['HTTP_X_FORWARDED_FOR']; } else { From f6a470e8f8c4b09575dabc45a92876ccb6bd477b Mon Sep 17 00:00:00 2001 From: Marc Hagen Date: Wed, 23 Nov 2022 11:48:40 +0100 Subject: [PATCH 03/26] Add live MTR to LookingGlass --- LookingGlass.php | 223 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 206 insertions(+), 17 deletions(-) diff --git a/LookingGlass.php b/LookingGlass.php index 49962e5..131be53 100644 --- a/LookingGlass.php +++ b/LookingGlass.php @@ -180,7 +180,7 @@ class LookingGlass */ public static function mtr(string $host): bool { - return self::procExecute('mtr -4 --report --report-wide', $host); + return self::procExecute('mtr --raw -n -4 -c ' . self::MTR_COUNT, $host); } /** @@ -191,7 +191,7 @@ class LookingGlass */ public static function mtr6(string $host): bool { - return self::procExecute('mtr -6 --report --report-wide', $host); + return self::procExecute('mtr --raw -n -6 -c ' . self::MTR_COUNT, $host); } /** @@ -232,11 +232,11 @@ class LookingGlass private static function procExecute(string $cmd, string $host, int $failCount = 2): bool { // define output pipes - $spec = array( - 0 => array("pipe", "r"), - 1 => array("pipe", "w"), - 2 => array("pipe", "w") - ); + $spec = [ + 0 => ['pipe', 'r'], + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'] + ]; // sanitize + remove single quotes $host = str_replace('\'', '', filter_var($host, FILTER_SANITIZE_URL)); @@ -251,16 +251,17 @@ class LookingGlass // check for mtr/traceroute if (strpos($cmd, 'mtr') !== false) { $type = 'mtr'; + $parser = new Parser(); } elseif (strpos($cmd, 'traceroute') !== false) { $type = 'traceroute'; } else { $type = ''; } - $fail = 0; - $match = 0; + $fail = 0; + $match = 0; $traceCount = 0; - $lastFail = 'start'; + $lastFail = 'start'; // iterate stdout while (($str = fgets($pipes[1], 4096)) != null) { // check for output buffer @@ -273,12 +274,14 @@ class LookingGlass // correct output for mtr if ($type === 'mtr') { - if ($match < 10 && preg_match('/^[0-9]\. /', $str, $string)) { - $str = preg_replace('/^[0-9]\. /', '  ' . $string[0], $str); - $match++; - } else { - $str = preg_replace('/^[0-9]{2}\. /', ' ' . substr($str, 0, 4), $str); - } + // correct output for mtr + $parser->update($str); + echo '---' . PHP_EOL . $parser->__toString() . PHP_EOL . str_pad('', 4096) . PHP_EOL; + + // flush output buffering + @ob_flush(); + flush(); + continue; } // correct output for traceroute elseif ($type === 'traceroute') { @@ -319,7 +322,7 @@ class LookingGlass } $status = proc_get_status($process); - if ($status['running'] == true) { + if ($status['running']) { // close pipes that are still open foreach ($pipes as $pipe) { fclose($pipe); @@ -341,3 +344,189 @@ class LookingGlass return true; } } + +class Hop +{ + /** @var int */ + public $idx; + /** @var string */ + public $asn = ''; + /** @var float */ + public $avg = 0.0; + /** @var int */ + public $loss = 0; + /** @var float */ + public $stdev = 0.0; + /** @var int */ + public $sent = 0; + /** @var int */ + public $recieved = 0; + /** @var float */ + public $last = 0.0; + /** @var float */ + public $best = 0.0; + /** @var float */ + public $worst = 0.0; + + /** @var string[] */ + public $ips = []; + /** @var string[] */ + public $hosts = []; + /** @var float[] */ + public $timings = []; + +} + +class RawHop +{ + /** @var string */ + public $dataType; + /** @var int */ + public $idx; + /** @var string */ + public $value; +} + +class Parser +{ + /** @var Hop[] */ + protected $hopsCollection = []; + /** @var int */ + private $hopCount = 0; + /** @var int */ + private $outputWidth = 38; + + public function __construct() + { + putenv('RES_OPTIONS=retrans:1 retry:1 timeout:1 attempts:1'); + } + + public function __toString(): string + { + $str = ''; + foreach ($this->hopsCollection as $index => $hop) { + $host = $hop->hosts[0] ?? $hop->ips[0] ?? '???'; + + if (strlen($host) > $this->outputWidth) { + $this->outputWidth = strlen($host); + } + + $hop->recieved = count($hop->timings); + if (count($hop->timings)) { + $hop->last = $hop->timings[count($hop->timings) - 1]; + $hop->best = $hop->timings[0]; + $hop->worst = $hop->timings[0]; + $hop->avg = array_sum($hop->timings) / count($hop->timings); + } + + if (count($hop->timings) > 1) { + $hop->stdev = $this->stDev($hop->timings); + } + + foreach ($hop->timings as $time) { + + if ($hop->best > $time) { + $hop->best = $time; + } + + if ($hop->worst < $time) { + $hop->worst = $time; + } + } + + $hop->loss = $hop->sent ? (100 * ($hop->sent - $hop->recieved)) / $hop->sent : 100; + + $str = sprintf( + "%s%2d.|-- %s%3d.0%% %3d %5.1f %5.1f %5.1f %5.1f %5.1f\n", + $str, + $index, + str_pad($host, $this->outputWidth + 3, ' ', STR_PAD_RIGHT), + $hop->loss, + $hop->sent, + $hop->last, + $hop->avg, + $hop->best, + $hop->worst, + $hop->stdev + ); + } + + return sprintf(" Host%sLoss%% Snt Last Avg Best Wrst StDev\n%s", str_pad('', $this->outputWidth + 7, ' ', STR_PAD_RIGHT), $str); + } + + private function stDev(array $array): float + { + $sdSquare = function ($x, $mean) { + return pow($x - $mean, 2); + }; + + // square root of sum of squares devided by N-1 + return sqrt(array_sum(array_map($sdSquare, $array, array_fill(0, count($array), (array_sum($array) / count($array))))) / (count($array) - 1)); + } + + public function update($rawMtrInput) + { + //Store each line of output in rawhop structure + $things = explode(' ', $rawMtrInput); + + if (count($things) !== 3 && (count($things) !== 4 && $things[0] === 'p')) { + return; + } + + $rawHop = new RawHop(); + $rawHop->dataType = $things[0]; + $rawHop->idx = (int)$things[1]; + $rawHop->value = $things[2]; + + if ($this->hopCount < $rawHop->idx + 1) { + $this->hopCount = $rawHop->idx + 1; + } + + if (!isset($this->hopsCollection[$rawHop->idx])) { + $this->hopsCollection[$rawHop->idx] = new Hop(); + } + + $hop = $this->hopsCollection[$rawHop->idx]; + $hop->idx = $rawHop->idx; + switch ($rawHop->dataType) { + case 'h': + $hop->ips[] = $rawHop->value; + $hop->hosts[] = gethostbyaddr($rawHop->value) ? : null; + break; + case 'd': + //Not entirely sure if multiple IPs. Better use -n in mtr and resolve later in summarize. + //out.Hops[data.idx].Host = append(out.Hops[data.idx].Host, data.value) + break; + case 'p': + $hop->sent++; + $hop->timings[] = (float)$rawHop->value / 1000; + break; + } + + $this->hopsCollection[$rawHop->idx] = $hop; + + $this->filterLastDupeHop(); + } + + // Function to calculate standard deviation (uses sd_square) + + private function filterLastDupeHop() + { + // filter dupe last hop + $finalIdx = 0; + $previousIp = ''; + + foreach ($this->hopsCollection as $key => $hop) { + if (count($hop->ips) && $hop->ips[0] !== $previousIp) { + $previousIp = $hop->ips[0]; + $finalIdx = $key + 1; + } + } + + unset($this->hopsCollection[$finalIdx]); + + usort($this->hopsCollection, function ($a, $b) { + return $a->idx - $b->idx; + }); + } +} From caafc380ea3e1faaf21345861c35b27a73713244 Mon Sep 17 00:00:00 2001 From: Marc Hagen Date: Wed, 23 Nov 2022 11:48:54 +0100 Subject: [PATCH 04/26] Better session checking, and use consts --- backend.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/backend.php b/backend.php index 9c149ec..ff8c10d 100644 --- a/backend.php +++ b/backend.php @@ -21,8 +21,13 @@ use Hybula\LookingGlass; LookingGlass::validateConfig(); LookingGlass::startSession(); -if ($_SESSION['TARGET'] && $_SESSION['METHOD'] && isset($_SESSION['BACKEND'])) { - unset($_SESSION['BACKEND']); +if (isset($_SESSION[LookingGlass::SESSION_TARGET_HOST]) && + isset($_SESSION[LookingGlass::SESSION_TARGET_METHOD]) && + isset($_SESSION[LookingGlass::SESSION_CALL_BACKEND]) +) { + unset($_SESSION[LookingGlass::SESSION_CALL_BACKEND]); + + switch ($_SESSION['METHOD']) { case 'ping': LookingGlass::ping($_SESSION['TARGET']); From 5be59c4e35b9108b7ce3adf8c64099706e622d11 Mon Sep 17 00:00:00 2001 From: Marc Hagen Date: Wed, 23 Nov 2022 11:50:57 +0100 Subject: [PATCH 05/26] Use consts --- index.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/index.php b/index.php index e776a8e..e3b699e 100644 --- a/index.php +++ b/index.php @@ -42,7 +42,7 @@ if (!empty($_POST)) { if (in_array($_POST['backendMethod'], ['ping', 'mtr', 'traceroute'])) { if (!LookingGlass::isValidIpv4($_POST['targetHost'])) { - $targetHost = LookingGlass::isValidHost($_POST['targetHost'], 'ipv4'); + $targetHost = LookingGlass::isValidHost($_POST['targetHost'], LookingGlass::IPV4); if (!$targetHost) { $errorMessage = 'No valid IPv4 provided.'; break; @@ -50,9 +50,10 @@ if (!empty($_POST)) { $_SESSION['TARGET'] = $targetHost; } } + if (in_array($_POST['backendMethod'], ['ping6', 'mtr6', 'traceroute6'])) { if (!LookingGlass::isValidIpv6($_POST['targetHost'])) { - $targetHost = LookingGlass::isValidHost($_POST['targetHost'], 'ipv6'); + $targetHost = LookingGlass::isValidHost($_POST['targetHost'], LookingGlass::IPV4); if (!$targetHost) { $errorMessage = 'No valid IPv6 provided.'; break; @@ -60,6 +61,7 @@ if (!empty($_POST)) { $_SESSION['TARGET'] = $targetHost; } } + $_SESSION['TERMS'] = true; $_SESSION['BACKEND'] = true; break; From b3098f80133725ada6554dcf0697b4b7c67e4502 Mon Sep 17 00:00:00 2001 From: Marc Hagen Date: Wed, 23 Nov 2022 11:51:58 +0100 Subject: [PATCH 06/26] Replace deprecated document.execCommand with navigator.clipboard --- index.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/index.php b/index.php index e3b699e..2af90b7 100644 --- a/index.php +++ b/index.php @@ -289,13 +289,12 @@ if (LG_BLOCK_CUSTOM) { + From 3c84c973bc53d2e4642d159817654e7f297857bf Mon Sep 17 00:00:00 2001 From: Marc Hagen Date: Wed, 23 Nov 2022 13:07:49 +0100 Subject: [PATCH 21/26] Tabs > spaces --- index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.php b/index.php index 0a59a42..682b563 100644 --- a/index.php +++ b/index.php @@ -212,8 +212,8 @@ $templateData['csrfToken'] = $_SESSION[LookingGlass::SESSION_CSRF] = bin2hex(ran - - + +