208 lines
7.1 KiB
PHP
208 lines
7.1 KiB
PHP
<?php
|
|
|
|
function torctl(array $commands): array {
|
|
$sock = @stream_socket_client("unix:///run/tor/control", $errno, $errstr, 2.0);
|
|
if (!$sock) { throw new Exception("control connect failed: $errstr"); }
|
|
stream_set_timeout($sock, 2);
|
|
$cookieRaw = @file_get_contents("/run/tor/control.authcookie");
|
|
if ($cookieRaw === false) { fclose($sock); throw new Exception("cookie read failed"); }
|
|
$cookie = bin2hex($cookieRaw);
|
|
fwrite($sock, "AUTHENTICATE $cookie\r\n");
|
|
$resp = fgets($sock);
|
|
if ($resp === false || strpos($resp, "250") !== 0) { fclose($sock); throw new Exception("auth failed: $resp"); }
|
|
|
|
$out = [];
|
|
foreach ($commands as $c) {
|
|
fwrite($sock, rtrim($c)."\r\n");
|
|
$buf = "";
|
|
while (($line = fgets($sock)) !== false) {
|
|
$buf .= $line;
|
|
if (rtrim($line) === "250 OK") break;
|
|
}
|
|
foreach (explode("\n", trim($buf)) as $ln) {
|
|
$ln = trim($ln);
|
|
if (strpos($ln, "250-") === 0) $ln = substr($ln, 4);
|
|
else if (strpos($ln, "250 ") === 0) $ln = substr($ln, 4);
|
|
else continue;
|
|
if (strpos($ln, "=") !== false) {
|
|
[$k,$v] = explode("=", $ln, 2);
|
|
$out[trim($k)] = trim($v);
|
|
} else {
|
|
$out[] = $ln;
|
|
}
|
|
}
|
|
}
|
|
fclose($sock);
|
|
return $out;
|
|
}
|
|
|
|
function torpanel_conf_path(): string { return "/etc/tor/torrc.d/99-torpanel.conf"; }
|
|
|
|
|
|
function _sanitize_nickname(string $nick): string {
|
|
$nick = preg_replace('/[^A-Za-z0-9_-]/', '', $nick);
|
|
if ($nick === '') $nick = 'RaspberryRelay';
|
|
return substr($nick, 0, 19);
|
|
}
|
|
|
|
function _mbps_to_kb(int $mbps): int {
|
|
$mbps = max(1, min($mbps, 1000000));
|
|
return (int)max(1, round($mbps * 125));
|
|
}
|
|
function _kb_to_mbps(float $kb): int {
|
|
return (int)max(1, round($kb / 125));
|
|
}
|
|
|
|
function _rate_to_kb(string $val): int {
|
|
if (preg_match('/^\s*(\d+(?:\.\d+)?)\s*(KB|MB)?\s*$/i', $val, $m)) {
|
|
$n = (float)$m[1];
|
|
$u = isset($m[2]) ? strtoupper($m[2]) : 'KB';
|
|
if ($u === 'MB') return (int)max(1, round($n * 1000));
|
|
return (int)max(1, round($n));
|
|
}
|
|
return 625; // ~5 Mbps
|
|
}
|
|
|
|
function read_torpanel_conf(): array {
|
|
$path = torpanel_conf_path();
|
|
$cfg = [
|
|
"Nickname" => "RaspberryRelay",
|
|
"ContactInfo" => "contact@admin.com",
|
|
"ORPort" => "9001",
|
|
"BandwidthRate" => "625 KB",
|
|
"BandwidthBurst" => "1250 KB",
|
|
"AccountingMax" => "100 GB",
|
|
"AccountingStart" => "month 1 00:00",
|
|
"SocksPort" => "0",
|
|
"TransPort" => "0",
|
|
"ExitRelay" => "0",
|
|
"ControlPort" => "0",
|
|
"ControlSocket" => "/run/tor/control",
|
|
"CookieAuthentication" => "1",
|
|
"CookieAuthFileGroupReadable" => "1",
|
|
];
|
|
if (!is_readable($path)) return $cfg;
|
|
|
|
foreach (@file($path) ?: [] as $line) {
|
|
if (preg_match('/^\s*(Nickname|ContactInfo|ORPort|BandwidthRate|BandwidthBurst|AccountingMax|AccountingStart|SocksPort|TransPort|ExitRelay|ControlPort|ControlSocket|CookieAuthentication|CookieAuthFileGroupReadable)\s+(.+?)\s*$/', $line, $m)) {
|
|
$cfg[$m[1]] = trim($m[2]);
|
|
}
|
|
}
|
|
return $cfg;
|
|
}
|
|
|
|
function torrc_to_ui(array $parsed): array {
|
|
$rate_kb = _rate_to_kb((string)($parsed['BandwidthRate'] ?? '625 KB'));
|
|
$burst_kb = _rate_to_kb((string)($parsed['BandwidthBurst'] ?? max(1, $rate_kb * 2)));
|
|
|
|
$cap_gb = 100;
|
|
if (preg_match('/^\s*(\d+(?:\.\d+)?)\s*GB\b/i', (string)($parsed['AccountingMax'] ?? ''), $m)) {
|
|
$cap_gb = (int)max(1, round((float)$m[1]));
|
|
}
|
|
|
|
$acc_day = 1;
|
|
if (preg_match('/month\s+(\d+)\s+\d+:\d+/i', (string)($parsed['AccountingStart'] ?? ''), $m)) {
|
|
$acc_day = max(1, min(28, (int)$m[1]));
|
|
}
|
|
|
|
return [
|
|
'nickname' => (string)($parsed['Nickname'] ?? 'RaspberryRelay'),
|
|
'contact' => (string)($parsed['ContactInfo'] ?? 'contact@admin.com'),
|
|
'orport' => (int) ($parsed['ORPort'] ?? 9001),
|
|
'rate_mbps' => _kb_to_mbps($rate_kb),
|
|
'burst_mbps' => _kb_to_mbps($burst_kb),
|
|
'cap_gb' => (int)$cap_gb,
|
|
'acc_day' => (int)$acc_day,
|
|
];
|
|
}
|
|
|
|
|
|
function write_torpanel_conf(array $in): bool {
|
|
$nick = isset($in['nickname']) ? (string)$in['nickname'] :
|
|
(isset($in['Nickname']) ? (string)$in['Nickname'] : 'RaspberryRelay');
|
|
$nick = _sanitize_nickname($nick);
|
|
|
|
$contact = isset($in['contact']) ? (string)$in['contact'] :
|
|
(isset($in['ContactInfo']) ? (string)$in['ContactInfo'] : 'contact@admin.com');
|
|
$contact = substr(preg_replace('/[\x00-\x1F\x7F]/', '', trim($contact)), 0, 200);
|
|
|
|
$orport = (int)($in['orport'] ?? ($in['ORPort'] ?? 9001));
|
|
if ($orport < 1 || $orport > 65535) $orport = 9001;
|
|
|
|
if (isset($in['rate_mbps'])) { $rate_kb = _mbps_to_kb((int)$in['rate_mbps']); }
|
|
else { $rate_kb = _rate_to_kb((string)($in['BandwidthRate'] ?? '625 KB')); }
|
|
|
|
if (isset($in['burst_mbps'])) { $burst_kb = _mbps_to_kb((int)$in['burst_mbps']); }
|
|
else { $burst_kb = _rate_to_kb((string)($in['BandwidthBurst'] ?? max(1, $rate_kb*2) . ' KB')); }
|
|
|
|
if ($burst_kb < $rate_kb) $burst_kb = $rate_kb;
|
|
|
|
$cap_gb = (int)($in['cap_gb'] ?? 0);
|
|
if ($cap_gb <= 0 && isset($in['AccountingMax']) && preg_match('/^\s*(\d+(?:\.\d+)?)\s*GB\b/i', (string)$in['AccountingMax'], $m)) {
|
|
$cap_gb = (int)max(1, round((float)$m[1]));
|
|
}
|
|
if ($cap_gb < 1) $cap_gb = 100;
|
|
|
|
$acc_day = (int)($in['acc_day'] ?? 0);
|
|
if ($acc_day <= 0 && isset($in['AccountingStart']) && preg_match('/month\s+(\d+)\s+\d+:\d+/i', (string)$in['AccountingStart'], $m)) {
|
|
$acc_day = (int)$m[1];
|
|
}
|
|
if ($acc_day < 1 || $acc_day > 28) $acc_day = 1;
|
|
|
|
$out = <<<EOC
|
|
## --- Managed by TorPanel ---
|
|
SocksPort 0
|
|
TransPort 0
|
|
ORPort {$orport}
|
|
ExitRelay 0
|
|
ExitPolicy reject *:*
|
|
Nickname {$nick}
|
|
ContactInfo {$contact}
|
|
BandwidthRate {$rate_kb} KB
|
|
BandwidthBurst {$burst_kb} KB
|
|
AccountingMax {$cap_gb} GB
|
|
AccountingStart month {$acc_day} 00:00
|
|
ControlPort 0
|
|
ControlSocket /run/tor/control
|
|
CookieAuthentication 1
|
|
CookieAuthFileGroupReadable 1
|
|
# --- End TorPanel block ---
|
|
EOC;
|
|
|
|
$path = torpanel_conf_path();
|
|
$dir = dirname($path);
|
|
|
|
if (file_exists($path) && is_writable($path)) {
|
|
if (file_put_contents($path, $out, LOCK_EX) !== false) {
|
|
@chgrp($path, 'www-data'); @chmod($path, 0664);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (is_dir($dir) && is_writable($dir)) {
|
|
$tmp = $path . '.tmp';
|
|
if (file_put_contents($tmp, $out) !== false) {
|
|
@chgrp($tmp, 'www-data'); @chmod($tmp, 0664);
|
|
@rename($tmp, $path);
|
|
@chgrp($path, 'www-data'); @chmod($path, 0664);
|
|
return true;
|
|
}
|
|
@is_file($tmp) && @unlink($tmp);
|
|
}
|
|
|
|
if (file_put_contents($path, $out) !== false) {
|
|
@chgrp($path, 'www-data'); @chmod($path, 0664);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function config_apply(array $in): bool {
|
|
$ok = write_torpanel_conf($in);
|
|
if (!$ok) return false;
|
|
@exec('sudo /bin/systemctl reload tor 2>/dev/null');
|
|
return true;
|
|
}
|