lookingglass/index.php

343 lines
16 KiB
PHP
Raw Normal View History

<?php declare(strict_types=1);
2022-01-15 18:40:44 +00:00
/**
* Hybula Looking Glass
*
* Provides UI and input for the looking glass backend.
*
* @copyright 2022 Hybula B.V.
* @license Mozilla Public License 2.0
* @version 0.1
* @since File available since release 0.1
* @link https://github.com/hybula/lookingglass
*/
require __DIR__.'/bootstrap.php';
2022-01-15 18:40:44 +00:00
use Hybula\LookingGlass;
2022-11-23 11:50:50 +00:00
$errorMessage = null;
2022-01-15 18:40:44 +00:00
if (!empty($_POST)) {
2022-11-23 11:50:50 +00:00
if (!isset($_POST['csrfToken']) || !isset($_SESSION[LookingGlass::SESSION_CSRF]) || ($_POST['csrfToken'] !== $_SESSION[LookingGlass::SESSION_CSRF])) {
exitErrorMessage('Missing or incorrect CSRF token.');
}
if (!isset($_POST['submitForm']) || !isset($_POST['backendMethod']) || !isset($_POST['targetHost'])) {
exitErrorMessage('Unsupported POST received.');
}
2022-01-15 18:40:44 +00:00
2022-11-23 11:50:50 +00:00
if (!in_array($_POST['backendMethod'], LG_METHODS)) {
exitErrorMessage('Unsupported backend method.');
}
2022-11-23 10:50:57 +00:00
2022-11-23 11:50:50 +00:00
$_SESSION[LookingGlass::SESSION_TARGET_METHOD] = $_POST['backendMethod'];
$_SESSION[LookingGlass::SESSION_TARGET_HOST] = $_POST['targetHost'];
if (!isset($_POST['checkTerms']) && LG_TERMS) {
exitErrorMessage('You must agree with the Terms of Service.');
}
$targetHost = $_POST['targetHost'];
if (in_array($_POST['backendMethod'], ['ping', 'mtr', 'traceroute'])) {
if (!LookingGlass::isValidIpv4($_POST['targetHost']) &&
!$targetHost = LookingGlass::isValidHost($_POST['targetHost'], LookingGlass::IPV4)
) {
exitErrorMessage('No valid IPv4 provided.');
}
}
2022-11-23 10:50:57 +00:00
2022-11-23 11:50:50 +00:00
if (in_array($_POST['backendMethod'], ['ping6', 'mtr6', 'traceroute6'])) {
if (!LookingGlass::isValidIpv6($_POST['targetHost']) &&
2022-11-23 11:50:50 +00:00
!$targetHost = LookingGlass::isValidHost($_POST['targetHost'],LookingGlass::IPV6)
) {
exitErrorMessage('No valid IPv6 provided.');
2022-01-15 18:40:44 +00:00
}
2022-11-23 11:50:50 +00:00
}
$_SESSION[LookingGlass::SESSION_TARGET_HOST] = $targetHost;
$_SESSION[LookingGlass::SESSION_TOS_CHECKED] = true;
$_SESSION[LookingGlass::SESSION_CALL_BACKEND] = true;
exitNormal();
}
$templateData['session_target'] = $_SESSION[LookingGlass::SESSION_TARGET_HOST] ?? '';
$templateData['session_method'] = $_SESSION[LookingGlass::SESSION_TARGET_METHOD] ?? '';
$templateData['session_call_backend'] = $_SESSION[LookingGlass::SESSION_CALL_BACKEND] ?? false;
$templateData['session_tos_checked'] = isset($_SESSION[LookingGlass::SESSION_TOS_CHECKED]) ? ' checked' : '';
if (isset($_SESSION[LookingGlass::SESSION_ERROR_MESSAGE])) {
$templateData['error_message'] = $_SESSION[LookingGlass::SESSION_ERROR_MESSAGE];
unset($_SESSION[LookingGlass::SESSION_ERROR_MESSAGE]);
2022-01-15 18:40:44 +00:00
}
if (LG_BLOCK_CUSTOM) {
include LG_CUSTOM_PHP;
ob_start();
include LG_CUSTOM_HTML;
$templateData['custom_html'] = ob_get_clean();
2022-01-15 18:40:44 +00:00
}
$templateData['csrfToken'] = $_SESSION[LookingGlass::SESSION_CSRF] = bin2hex(random_bytes(12));
2022-01-15 18:40:44 +00:00
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<meta content="" name="description">
<meta content="Hybula" name="author">
<title><?php echo $templateData['title'] ?></title>
2022-11-23 20:49:05 +00:00
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
<?php if ($templateData['custom_css']) { echo '<link href="'.$templateData['custom_css'].'" rel="stylesheet">'; } ?>
2022-11-23 21:05:42 +00:00
<?php if ($templateData['custom_head']) { echo $templateData['custom_head']; } ?>
2022-01-15 18:40:44 +00:00
</head>
<body>
<div class="col-lg-6 mx-auto p-3 py-md-5">
<header class="d-flex align-items-center pb-3 mb-5 border-bottom">
<div class="col-8">
<a class="d-flex align-items-center text-dark text-decoration-none" href="<?php echo $templateData['logo_url'] ?>" target="_blank">
<?php echo $templateData['logo_data'] ?>
2022-01-15 18:40:44 +00:00
</a>
</div>
<div class="col-4 float-end">
<select class="form-select" onchange="window.location = this.options[this.selectedIndex].value">
<option selected><?php echo $templateData['current_location'] ?></option>
<?php foreach ($templateData['locations'] as $location => $link): ?>
<option value="<?php echo $link ?>"><?php echo $location ?></option>
<?php endforeach ?>
2022-01-15 18:40:44 +00:00
</select>
</div>
</header>
<main>
<?php if (LG_BLOCK_NETWORK): ?>
2022-01-15 18:40:44 +00:00
<div class="row mb-5">
<div class="card shadow-lg">
<div class="card-body p-3">
<h1 class="fs-4 card-title mb-4">Network</h1>
<div class="row mb-3">
<div class="col-md-7">
<label class="mb-2 text-muted">Location</label>
<div class="input-group mb-3">
<input type="text" class="form-control" value="<?php echo $templateData['current_location'] ?>" onfocus="this.select()" readonly="">
<a class="btn btn-outline-secondary" href="https://www.openstreetmap.org/search?query=<?php echo urlencode($templateData['maps_query']); ?>" target="_blank">Map</a>
<?php if (!empty($templateData['locations'])): ?>
2022-01-15 18:40:44 +00:00
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">Locations</button>
<ul class="dropdown-menu dropdown-menu-end">
<?php foreach ($templateData['locations'] as $location => $link): ?>
<li><a class="dropdown-item" href="<?php echo $link ?>"><?php echo $location ?></a></li>
<?php endforeach ?>
2022-01-15 18:40:44 +00:00
</ul>
<?php endif ?>
2022-01-15 18:40:44 +00:00
</div>
</div>
<div class="col-md-5">
<label class="mb-2 text-muted">Facility</label>
<div class="input-group">
<input type="text" class="form-control" value="<?php echo $templateData['facility'] ?>" onfocus="this.select()" readonly="">
<a href="<?php echo $templateData['facility_url'] ?>" class="btn btn-outline-secondary" target="_blank">PeeringDB</a>
2022-01-15 18:40:44 +00:00
</div>
</div>
</div>
<div class="row mb-3">
<div class="col-md-3">
<label class="mb-2 text-muted">Test IPv4</label>
<div class="input-group">
<input type="text" class="form-control" value="<?php echo $templateData['ipv4'] ?>" onfocus="this.select()" readonly="">
<button class="btn btn-outline-secondary" onclick="copyToClipboard('<?php echo $templateData['ipv4'] ?>', this)">Copy</button>
2022-01-15 18:40:44 +00:00
</div>
</div>
<div class="col-md-5">
<label class="mb-2 text-muted">Test IPv6</label>
<div class="input-group">
<input type="text" class="form-control" value="<?php echo $templateData['ipv6'] ?>" onfocus="this.select()" readonly="">
<button class="btn btn-outline-secondary" onclick="copyToClipboard('<?php echo $templateData['ipv6'] ?>', this)">Copy</button>
2022-01-15 18:40:44 +00:00
</div>
</div>
<div class="col-md-4">
<label class="mb-2 text-muted">Your IP</label>
<div class="input-group">
<input type="text" class="form-control" value="<?php echo $templateData['user_ip'] ?>" onfocus="this.select()" readonly="">
<button class="btn btn-outline-secondary" onclick="copyToClipboard('<?php echo $templateData['user_ip'] ?>', this)">Copy</button>
2022-01-15 18:40:44 +00:00
</div>
</div>
</div>
</div>
</div>
</div>
<?php endif ?>
2022-01-15 18:40:44 +00:00
<?php if (LG_BLOCK_LOOKINGGLAS): ?>
2022-01-15 18:40:44 +00:00
<div class="row pb-5">
<div class="card shadow-lg">
<div class="card-body p-3">
<h1 class="fs-4 card-title mb-4">Looking Glass</h1>
<form method="POST" action="/" autocomplete="off">
<input type="hidden" name="csrfToken" value="<?php echo $templateData['csrfToken'] ?>">
2022-01-15 18:40:44 +00:00
<div class="row">
<div class="col-md-7 mb-3">
<div class="input-group">
<span class="input-group-text" id="basic-addon1">Target</span>
<input type="text" class="form-control" placeholder="IP address or host..." name="targetHost" value="<?php echo $templateData['session_target'] ?>" required="">
2022-01-15 18:40:44 +00:00
</div>
</div>
<div class="col-md-5 mb-3">
<div class="input-group">
<label class="input-group-text">Method</label>
<select class="form-select" name="backendMethod" id="backendMethod">
<?php foreach ($templateData['methods'] as $method): ?>
<option value="<?php echo $method ?>"<?php if($templateData['session_method'] === $method): ?> selected<?php endif ?>><?php echo $method ?></option>
<?php endforeach ?>
2022-01-15 18:40:44 +00:00
</select>
</div>
</div>
</div>
<div class="d-flex align-items-center">
<?php if ($templateData['tos']): ?>
2022-01-15 18:40:44 +00:00
<div class="form-check">
<input type="checkbox" id="checkTerms" name="checkTerms" class="form-check-input"<?php echo $templateData['session_tos_checked'] ?>>
<label for="checkTerms" class="form-check-label">I agree with the <a href="<?php echo $templateData['tos'] ?>" target="_blank">Terms of Use</a></label>
2022-01-15 18:40:44 +00:00
</div>
<?php endif ?>
2022-01-15 18:40:44 +00:00
<button type="submit" class="btn btn-primary ms-auto" id="executeButton" name="submitForm">
Execute
</button>
</div>
<?php if ($templateData['error_message']): ?>
2022-11-23 12:07:49 +00:00
<div class="alert alert-danger mt-3" role="alert"><?php echo $templateData['error_message'] ?></div>
<?php endif ?>
2022-01-15 18:40:44 +00:00
<div class="card card-body bg-light mt-4" style="display: none;" id="outputCard">
<pre id="outputContent" style="overflow: hidden; white-space: pre; word-wrap: normal;"></pre>
</div>
</form>
</div>
</div>
</div>
<?php endif ?>
2022-01-15 18:40:44 +00:00
<?php if (LG_BLOCK_SPEEDTEST): ?>
2022-01-15 18:40:44 +00:00
<div class="row pb-5">
<div class="card shadow-lg">
<div class="card-body p-3">
<h1 class="fs-4 card-title mb-4">Speedtest</h1>
<?php if ($templateData['speedtest_iperf']): ?>
2022-01-15 18:40:44 +00:00
<div class="row mb-3">
<div class="col-md-6">
<label class="mb-2 text-muted"><?php echo $templateData['speedtest_incoming_label'] ?></label>
<p><code><?php echo $templateData['speedtest_incoming_cmd']; ?></code></p>
<button class="btn btn-sm btn-outline-secondary" onclick="copyToClipboard('<?php echo $templateData['speedtest_incoming_cmd'] ?>', this)">Copy</button>
2022-01-15 18:40:44 +00:00
</div>
<div class="col-md-6">
<label class="mb-2 text-muted"><?php echo $templateData['speedtest_outgoing_label'] ?></label>
<p><code><?php echo $templateData['speedtest_outgoing_cmd'] ?></code></p>
<button class="btn btn-sm btn-outline-secondary" onclick="copyToClipboard('<?php echo $templateData['speedtest_outgoing_cmd'] ?>', this)">Copy</button>
2022-01-15 18:40:44 +00:00
</div>
</div>
<?php endif ?>
2022-01-15 18:40:44 +00:00
<div class="row">
<label class="mb-2 text-muted">Test Files</label>
<div class="btn-group input-group mb-3">
<?php foreach ($templateData['speedtest_files'] as $file => $link): ?>
<a href="<?php echo $link ?>" class="btn btn-outline-secondary"><?php echo $file ?></a>
<?php endforeach ?>
2022-01-15 18:40:44 +00:00
</div>
</div>
</div>
</div>
</div>
<?php endif ?>
2022-01-15 18:40:44 +00:00
<?php echo $templateData['custom_html'] ?>
2022-01-15 18:40:44 +00:00
</main>
<footer class="pt-3 mt-5 my-5 text-muted border-top">
Powered by <a href="https://github.com/hybula/lookingglass" target="_blank">Hybula Looking Glass</a>
2022-04-19 12:14:16 +00:00
<a href="https://github.com/hybula/lookingglass" target="_blank" class="float-end"><img src="https://img.shields.io/github/stars/hybula/lookingglass?style=social" alt="GitHub"></a>
2022-01-15 18:40:44 +00:00
</footer>
</div>
2022-11-23 12:00:07 +00:00
<?php if ($templateData['session_call_backend']): ?>
2022-01-15 18:40:44 +00:00
<script type="text/javascript">
2022-11-23 12:05:48 +00:00
(function () {
const outputContent = document.getElementById('outputContent')
const executeButton = document.getElementById('executeButton')
const outputCard = document.getElementById('outputCard')
2022-11-23 11:04:10 +00:00
2022-11-23 12:05:48 +00:00
executeButton.innerText = 'Executing...'
executeButton.disabled = true
2022-11-23 11:04:10 +00:00
2022-11-23 12:05:48 +00:00
outputCard.style.display = 'inherit'
2022-11-23 11:04:10 +00:00
2022-11-23 12:05:48 +00:00
fetch('/backend.php')
.then(async (response) => {
// response.body is a ReadableStream
const reader = response.body.getReader()
const decoder = new TextDecoder()
2022-11-23 11:04:10 +00:00
2022-11-23 12:05:48 +00:00
for await (const chunk of readChunks(reader)) {
const text = decoder.decode(chunk)
2022-11-23 11:04:10 +00:00
<?php if(in_array($_SESSION[LookingGlass::SESSION_TARGET_METHOD], [LookingGlass::METHOD_MTR, LookingGlass::METHOD_MTR6])): ?>
let splittedText = text.split('---')
2022-11-23 12:05:48 +00:00
if (!splittedText[1]) {
continue
}
outputContent.innerHTML = splittedText[1].trim()
2022-11-23 11:04:10 +00:00
<?php else: ?>
2022-11-23 12:05:48 +00:00
outputContent.innerHTML = outputContent.innerHTML + text.trim().replace(/<br \/> +/g, '<br />')
2022-11-23 11:04:10 +00:00
<?php endif ?>
2022-11-23 12:05:48 +00:00
}
})
.finally(() => {
executeButton.innerText = 'Execute'
executeButton.disabled = false
console.log('Backend ready!')
})
})()
// readChunks() reads from the provided reader and yields the results into an async iterable
function readChunks(reader) {
return {
async* [Symbol.asyncIterator]() {
let readResult = await reader.read()
while (!readResult.done) {
yield readResult.value
readResult = await reader.read()
}
},
}
}
2022-01-15 18:40:44 +00:00
</script>
2022-11-23 11:04:10 +00:00
<?php endif ?>
2022-01-15 18:40:44 +00:00
<script type="text/javascript">
async function copyToClipboard(text, button) {
2022-11-23 12:05:48 +00:00
if (!navigator || !navigator.clipboard || !navigator.clipboard.writeText) {
return Promise.reject('The Clipboard API is not available.')
}
2022-11-23 12:05:48 +00:00
button.innerHTML = 'Copied!'
await navigator.clipboard.writeText(text)
await new Promise(r => setTimeout(r, 2000))
button.innerHTML = 'Copy'
2022-01-15 18:40:44 +00:00
}
</script>
2022-11-23 20:49:05 +00:00
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
2022-01-15 18:40:44 +00:00
</body>
</html>