1
0

functions.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. <?php
  2. defined('CONFIG') or die('Direct access not allowed');
  3. function canRequestStatus($server) {
  4. if (!isset($_SESSION['last_request_time'][$server['name']])) { return true; }
  5. if (time() - $_SESSION['last_request_time'][$server['name']] >= REQUEST_INTERVAL) { return true; }
  6. return false;
  7. }
  8. function updateLastRequestTime($server) {
  9. $_SESSION['last_request_time'][$server['name']] = time();
  10. }
  11. function openvpnManagementCommand($server, $command) {
  12. if (empty($server['host']) || empty($server['port']) || empty($server['password'])) { return false; }
  13. $mgmt_host = $server['host'];
  14. $mgmt_port = $server['port'];
  15. $mgmt_pass = $server['password'];
  16. $timeout = 5;
  17. $socket = @fsockopen($mgmt_host, $mgmt_port, $errno, $errstr, $timeout);
  18. if (!$socket) {
  19. error_log("OpenVPN management connection failed to {$server['name']}: $errstr ($errno)");
  20. return false;
  21. }
  22. stream_set_timeout($socket, $timeout);
  23. try {
  24. // Читаем приветственное сообщение
  25. $welcome = '';
  26. while (!feof($socket)) {
  27. $line = fgets($socket);
  28. if ($line === false) break;
  29. $welcome .= $line;
  30. if (strpos($welcome, 'ENTER PASSWORD:') !== false) break;
  31. }
  32. // Отправляем пароль
  33. if (@fwrite($socket, "$mgmt_pass\n") === false) {
  34. throw new Exception("Failed to send password");
  35. }
  36. // Ждем подтверждения аутентификации
  37. $authResponse = '';
  38. while (!feof($socket)) {
  39. $line = fgets($socket);
  40. if ($line === false) break;
  41. $authResponse .= $line;
  42. if (strpos($authResponse, 'SUCCESS:') !== false || strpos($authResponse, '>INFO:') !== false) break;
  43. }
  44. // Отправляем команду
  45. if (@fwrite($socket, "$command\n") === false) {
  46. throw new Exception("Failed to send command");
  47. }
  48. // Читаем ответ
  49. $response = '';
  50. $expectedEnd = strpos($command, 'status') !== false ? "END\r\n" : ">";
  51. while (!feof($socket)) {
  52. $line = fgets($socket);
  53. if ($line === false) break;
  54. $response .= $line;
  55. if (strpos($response, $expectedEnd) !== false) break;
  56. }
  57. return $response;
  58. } catch (Exception $e) {
  59. error_log("OpenVPN management error ({$server['name']}): " . $e->getMessage());
  60. return false;
  61. } finally {
  62. @fwrite($socket, "quit\n");
  63. @fclose($socket);
  64. }
  65. }
  66. function getOpenVPNStatus($server) {
  67. // Проверяем, можно ли делать запрос
  68. if (!canRequestStatus($server)) {
  69. // Возвращаем кэшированные данные или пустой массив
  70. return $_SESSION['cached_status'][$server['name']] ?? [];
  71. }
  72. // Обновляем время последнего запроса
  73. updateLastRequestTime($server);
  74. $response = openvpnManagementCommand($server, "status 2");
  75. if (!$response) return $_SESSION['cached_status'][$server['name']] ?? [];
  76. $banned = getBannedClients($server);
  77. $clients = [];
  78. $lines = explode("\n", $response);
  79. $in_client_list = false;
  80. foreach ($lines as $line) {
  81. $line = trim($line);
  82. if (strpos($line, 'HEADER,CLIENT_LIST') === 0) {
  83. $in_client_list = true;
  84. continue;
  85. }
  86. if (strpos($line, 'HEADER,ROUTING_TABLE') === 0) {
  87. $in_client_list = false;
  88. continue;
  89. }
  90. //CLIENT_LIST,Common Name,Real Address,Virtual Address,Virtual IPv6 Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username,Client ID,Peer ID,Data Channel Cipher
  91. if ($in_client_list && strpos($line, 'CLIENT_LIST') === 0) {
  92. $parts = explode(',', $line);
  93. if (count($parts) >= 9) {
  94. $clients[] = [
  95. 'name' => $parts[1],
  96. 'real_ip' => $parts[2],
  97. 'virtual_ip' => $parts[3],
  98. 'bytes_received' => formatBytes($parts[5]),
  99. 'bytes_sent' => formatBytes($parts[6]),
  100. 'connected_since' => $parts[7],
  101. 'username' => $parts[8] ?? $parts[1],
  102. 'cipher' => end($parts),
  103. 'banned' => isset($banned[$parts[1]]),
  104. ];
  105. }
  106. }
  107. }
  108. // Кэшируем результат
  109. $_SESSION['cached_status'][$server['name']] = $clients;
  110. return $clients;
  111. }
  112. function get_servers_crt($cert_index) {
  113. // Проверка входных параметров
  114. if (empty($cert_index) || !is_string($cert_index)) {
  115. return false;
  116. }
  117. // Проверка существования исполняемого файла
  118. if (empty(SHOW_SERVERS_CRT) || !file_exists(SHOW_SERVERS_CRT) || !is_executable(SHOW_SERVERS_CRT)) {
  119. error_log('SHOW_SERVERS_CRT is not configured properly', 0);
  120. return false;
  121. }
  122. $command = sprintf(
  123. 'sudo %s %s 2>&1',
  124. escapeshellcmd(SHOW_SERVERS_CRT),
  125. escapeshellarg($cert_index)
  126. );
  127. exec($command, $cert_content, $return_var);
  128. if ($return_var !== 0) {
  129. error_log(sprintf(
  130. 'Command failed: %s (return code: %d, output: %s)',
  131. $command,
  132. $return_var,
  133. implode("\n", $cert_content)
  134. ), 0);
  135. return false;
  136. }
  137. if (empty($cert_content)) {
  138. error_log('Empty certificate content for file: '.$cert_index, 0);
  139. return false;
  140. }
  141. $result = array_fill_keys($cert_content, true);
  142. return $result;
  143. }
  144. function getBannedClients($server) {
  145. // Проверка входных параметров
  146. if (empty($server["ccd"]) || !is_string($server["ccd"])) {
  147. return [];
  148. }
  149. // Проверка существования исполняемого файла
  150. if (empty(SHOW_BANNED) || !file_exists(SHOW_BANNED) || !is_executable(SHOW_BANNED)) {
  151. error_log('SHOW_BANNED is not configured properly', 0);
  152. return [];
  153. }
  154. $command = sprintf(
  155. 'sudo %s %s 2>&1',
  156. escapeshellcmd(SHOW_BANNED),
  157. escapeshellarg($server["ccd"])
  158. );
  159. exec($command, $banned_content, $return_var);
  160. if ($return_var !== 0) {
  161. error_log(sprintf(
  162. 'Command failed: %s (return code: %d)',
  163. $command,
  164. $return_var,
  165. ), 0);
  166. return [];
  167. }
  168. if (empty($banned_content)) { return []; }
  169. $result = array_fill_keys($banned_content, true);
  170. return $result;
  171. }
  172. function getClientIPsCCD($server) {
  173. // Проверка входных параметров
  174. if (empty($server["ccd"]) || !is_string($server["ccd"])) {
  175. return [];
  176. }
  177. // Проверка существования исполняемого файла
  178. if (empty(GET_IPS_FROM_CCD) || !file_exists(GET_IPS_FROM_CCD) || !is_executable(GET_IPS_FROM_CCD)) {
  179. error_log('SHOW_BANNED is not configured properly', 0);
  180. return [];
  181. }
  182. $command = sprintf(
  183. 'sudo %s %s 2>&1',
  184. escapeshellcmd(GET_IPS_FROM_CCD),
  185. escapeshellarg($server["ccd"])
  186. );
  187. exec($command, $ccd_content, $return_var);
  188. if ($return_var !== 0) {
  189. error_log(sprintf(
  190. 'Command failed: %s (return code: %d)',
  191. $command,
  192. $return_var,
  193. ), 0);
  194. return [];
  195. }
  196. if (empty($ccd_content)) { return []; }
  197. $result=[];
  198. foreach ($ccd_content as $line) {
  199. if (empty($line)) { continue; }
  200. list($login, $ip) = explode(' ', trim($line), 2);
  201. $result[$login] = $ip;
  202. }
  203. return $result;
  204. }
  205. function getClientIPsIPP($server) {
  206. // Проверка входных параметров
  207. if (empty($server["ipp_file"]) || !is_string($server["ipp_file"])) {
  208. return [];
  209. }
  210. // Проверка существования исполняемого файла
  211. if (empty(GET_IPS_FROM_IPP) || !file_exists(GET_IPS_FROM_IPP) || !is_executable(GET_IPS_FROM_IPP)) {
  212. error_log('SHOW_BANNED is not configured properly', 0);
  213. return [];
  214. }
  215. $command = sprintf(
  216. 'sudo %s %s 2>&1',
  217. escapeshellcmd(GET_IPS_FROM_IPP),
  218. escapeshellarg($server["ipp_file"])
  219. );
  220. exec($command, $ipp_content, $return_var);
  221. if ($return_var !== 0) {
  222. error_log(sprintf(
  223. 'Command failed: %s (return code: %d)',
  224. $command,
  225. $return_var,
  226. ), 0);
  227. return [];
  228. }
  229. if (empty($ipp_content)) { return []; }
  230. $result=[];
  231. foreach ($ipp_content as $line) {
  232. if (empty($line)) { continue; }
  233. list($login, $ip) = explode(',', trim($line), 2);
  234. $result[$login] = $ip;
  235. }
  236. return $result;
  237. }
  238. function getAccountList($server) {
  239. $accounts = [];
  240. $banned = getBannedClients($server);
  241. // Получаем список из index.txt (неотозванные сертификаты)
  242. if (!empty($server['cert_index']) && !empty(SHOW_PKI_INDEX) && file_exists(SHOW_PKI_INDEX)) {
  243. $servers_list = get_servers_crt($server['cert_index']);
  244. // Безопасное выполнение скрипта
  245. $command = sprintf(
  246. 'sudo %s %s 2>&1',
  247. escapeshellcmd(SHOW_PKI_INDEX),
  248. escapeshellarg($server['cert_index']),
  249. );
  250. exec($command, $index_content, $return_var);
  251. if ($return_var == 0) {
  252. foreach ($index_content as $line) {
  253. if (empty(trim($line))) { continue; }
  254. if (preg_match('/\/CN=([^\/]+)/', $line, $matches)) {
  255. $username = trim($matches[1]);
  256. }
  257. if (empty($username)) { continue; }
  258. $revoked = false;
  259. if (preg_match('/^R\s+/',$line)) { $revoked = true; }
  260. if (isset($servers_list[$username])) { continue; }
  261. $accounts[$username] = [
  262. "username" => $username,
  263. "ip" => null,
  264. "banned" => isset($banned[$username]) || $revoked,
  265. "revoked" => $revoked
  266. ];
  267. }
  268. }
  269. }
  270. // Получаем список выданных IP из ipp.txt
  271. if (!empty($server['ipp_file']) && file_exists($server['ipp_file'])) {
  272. $ipps = getClientIPsIPP($server);
  273. foreach ($ipps as $username => $ip) {
  274. if (!isset($accounts[$username]) && empty($server['cert_index'])) {
  275. $accounts[$username] = [
  276. "username" => $username,
  277. "banned" => isset($banned[$username]),
  278. "ip" => $ip,
  279. "revoked" => false,
  280. ];
  281. }
  282. if (isset($accounts[$username]) and !empty($server['cert_index'])) {
  283. $accounts[$username]["ip"] = $ip;
  284. }
  285. }
  286. }
  287. // Ищем IP-адреса в CCD файлах
  288. if (!empty($server['ccd']) && is_dir($server['ccd'])) {
  289. $ccds = getClientIPsCCD($server);
  290. foreach ($ccds as $username => $ip) {
  291. if (!isset($accounts[$username]) && empty($server['cert_index'])) {
  292. $accounts[$username] = [
  293. "username" => $username,
  294. "banned" => isset($banned[$username]),
  295. "ip" => $ip,
  296. "revoked" => false,
  297. ];
  298. }
  299. if (isset($accounts[$username]) and !empty($server['cert_index'])) {
  300. $accounts[$username]["ip"] = $ip;
  301. }
  302. }
  303. }
  304. return $accounts;
  305. }
  306. function kickClient($server, $client_name) {
  307. return openvpnManagementCommand($server, "kill $client_name");
  308. }
  309. function removeCCD($server, $client_name) {
  310. if (empty($server["ccd"]) || empty($client_name) || empty(REMOVE_CCD) || !file_exists(REMOVE_CCD)) { return false; }
  311. $script_path = REMOVE_CCD;
  312. $ccd_file = "{$server['ccd']}/$client_name";
  313. $command = sprintf(
  314. 'sudo %s %s 2>&1',
  315. escapeshellcmd($script_path),
  316. escapeshellarg($ccd_file)
  317. );
  318. exec($command, $output, $return_var);
  319. $_SESSION['last_request_time'] = [];
  320. if ($return_var === 0) {
  321. return true;
  322. } else {
  323. return false;
  324. }
  325. }
  326. function unbanClient($server, $client_name) {
  327. if (empty($server["ccd"]) || empty($client_name) || empty(BAN_CLIENT) || !file_exists(BAN_CLIENT)) { return false; }
  328. $script_path = BAN_CLIENT;
  329. $ccd_file = "{$server['ccd']}/$client_name";
  330. $command = sprintf(
  331. 'sudo %s %s unban 2>&1',
  332. escapeshellcmd($script_path),
  333. escapeshellarg($ccd_file)
  334. );
  335. exec($command, $output, $return_var);
  336. $_SESSION['last_request_time'] = [];
  337. if ($return_var === 0) {
  338. return true;
  339. } else {
  340. return false;
  341. }
  342. }
  343. function banClient($server, $client_name) {
  344. if (empty($server["ccd"]) || empty($client_name) || empty(BAN_CLIENT) || !file_exists(BAN_CLIENT)) { return false; }
  345. $script_path = BAN_CLIENT;
  346. $ccd_file = "{$server['ccd']}/$client_name";
  347. $command = sprintf(
  348. 'sudo %s %s ban 2>&1',
  349. escapeshellcmd($script_path),
  350. escapeshellarg($ccd_file)
  351. );
  352. exec($command, $output, $return_var);
  353. $_SESSION['last_request_time'] = [];
  354. if ($return_var === 0) {
  355. // Кикаем клиента
  356. kickClient($server, $client_name);
  357. return true;
  358. } else {
  359. return false;
  360. }
  361. }
  362. function revokeClient($server, $client_name) {
  363. if (empty(REVOKE_CRT) || !file_exists(REVOKE_CRT)) {
  364. return banClient($server, $client_name);
  365. }
  366. $script_path = REVOKE_CRT;
  367. $rsa_dir = dirname(dirname($server['cert_index']));
  368. $command = sprintf(
  369. 'sudo %s %s %s %s 2>&1',
  370. escapeshellcmd($script_path),
  371. escapeshellarg('openvpn-server@'.$server['name']),
  372. escapeshellarg($rsa_dir),
  373. escapeshellarg($client_name)
  374. );
  375. exec($command, $output, $return_var);
  376. if ($return_var === 0) {
  377. return true;
  378. } else {
  379. return false;
  380. }
  381. }
  382. function formatBytes($bytes) {
  383. $bytes = (int)$bytes;
  384. if ($bytes <= 0) return '0 B';
  385. $units = ['B', 'KB', 'MB', 'GB', 'TB'];
  386. $pow = floor(log($bytes)/log(1024));
  387. return round($bytes/pow(1024,$pow),2).' '.$units[$pow];
  388. }
  389. function isClientActive($active_clients,$username) {
  390. $active_names = array_column($active_clients, 'name');
  391. if (in_array($username,$active_names)) { return true; }
  392. return false;
  393. }