PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/tunnel.php

https://github.com/srgg6701/auction-ruseasons
PHP | 615 lines | 458 code | 118 blank | 39 comment | 109 complexity | d2e7a317207b4b027c44381beb657795 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-3.0, LGPL-2.1, BSD-3-Clause, JSON
  1. <?php
  2. /* ##########################
  3. Devart HttpTunnel v1.69.
  4. HTTP tunnel script.
  5. This script allows you to manage database server even if the corresponding port is blocked or remote access to database server is not allowed.
  6. ##########################
  7. */
  8. $LOG = 1; // Set to "0" to disable logging
  9. $LOG_DEBUG = 1; // Set to "0" to disable additional debug logging
  10. $LOGFILE = "httptunnel_server.log";
  11. $LOGFILEHANDLE = 0;
  12. $MAXLOGSIZE = "4000000";
  13. $LIFETIME = 180; // script lifetime in seconds. If script was started and got no client within that time - it exits.
  14. $READ_WRITE_ATTEMPTS = 100;
  15. $CONN_FILE = "_connections.id.php";
  16. function checkFunctionExists($functionName) {
  17. if (!function_exists($functionName)) {
  18. echo "Required function <b>$functionName</b> does not exist.</br>";
  19. return false;
  20. }
  21. return true;
  22. }
  23. // Creates connection temporary file if not exists and checks permission to write
  24. function CreateAndCheckConnFile($fileName) {
  25. if (file_exists($fileName)){
  26. $newFile = @fopen($fileName, 'a');
  27. if($newFile)
  28. fclose($newFile);
  29. else
  30. echo "<b>Error</b>: Failed to open ($fileName) file: Permission denied.";
  31. }
  32. else{
  33. $newFile = @fopen($fileName, 'w');
  34. if($newFile){
  35. fwrite($newFile, "<?php echo 'Devart HTTP tunnel temporary file.'; exit; ?>\r\n"); // forbid viewing this file through browser
  36. fclose($newFile);
  37. }
  38. else
  39. echo "<b>Error</b>: Failed to create ($fileName) file: Permission denied.";
  40. }
  41. if(!$newFile)
  42. exit;
  43. }
  44. if (!isset($_REQUEST["a"])) { // query from browser
  45. echo "Devart HttpTunnel v1.69<br />";
  46. $functionList = array(
  47. "set_time_limit",
  48. "stream_socket_server",
  49. "stream_socket_client",
  50. "stream_socket_get_name",
  51. "stream_set_blocking",
  52. "stream_socket_accept",
  53. );
  54. $exist = true;
  55. foreach($functionList as $functionName) {
  56. $result = checkFunctionExists($functionName);
  57. $exist = $exist && $result;
  58. }
  59. if ($exist)
  60. CreateAndCheckConnFile($CONN_FILE);
  61. if ($exist)
  62. echo "Tunnel script is installed correctly. <br />You can establish connections through the HTTP tunnel.";
  63. else
  64. echo "Required PHP functions listed above are not available. Tunneling script will not work without these functions. Please read PHP manuals about how to install listed functions.";
  65. exit;
  66. }
  67. function myErrorHandler($errno, $errstr, $errfile, $errline) {
  68. switch ($errno) {
  69. case E_ERROR:
  70. $errfile=preg_replace('|^.*[\\\\/]|','',$errfile);
  71. echo $ERRSTR."Error in line $errline of file $errfile: [$errno] $errstr\n";
  72. exit;
  73. }
  74. }
  75. function shutdown () {
  76. global $ipsock, $rmsock, $outcount, $incount, $td, $te, $sockname, $useunix;
  77. if (connection_status() & 1) { // ABORTED
  78. logline ($_SERVER["REMOTE_ADDR"].": Irregular tunnel disconnect -> disconnecting server");
  79. logline ($_SERVER["REMOTE_ADDR"].": Sent ".$outcount." bytes, received ".$incount." bytes");
  80. } elseif (connection_status() & 2) { // TIMEOUT
  81. logline ($_SERVER["REMOTE_ADDR"].": PHP script timeout -> disconnecting server");
  82. logline ($_SERVER["REMOTE_ADDR"].": Sent ".$outcount." bytes, received ".$incount." bytes");
  83. }
  84. if ($ipsock) fclose($ipsock);
  85. if ($rmsock) fclose($rmsock);
  86. }
  87. function logline ($msg) {
  88. log_line_to_file(0, $msg);
  89. }
  90. function logdebug($msg) {
  91. log_line_to_file(1, $msg);
  92. }
  93. function logerr($msg) {
  94. global $ERRSTR;
  95. logline($msg);
  96. echo $ERRSTR;
  97. echo $msg;
  98. }
  99. function log_line_to_file ($debug, $msg) {
  100. global $LOG, $LOG_DEBUG, $MAXLOGSIZE, $LOGFILE, $LOGFILEHANDLE;
  101. if ($LOG && ((! $debug) || $LOG_DEBUG)) {
  102. $LOGFILEHANDLE=fopen ($LOGFILE, "a");
  103. if ($LOGFILEHANDLE) {
  104. fwrite ($LOGFILEHANDLE, date("d.m.Y H:i:s")." - $msg\r\n");
  105. $lstat=fstat($LOGFILEHANDLE);
  106. if ($lstat["size"]>$MAXLOGSIZE) rotatelog();
  107. fclose($LOGFILEHANDLE);
  108. }
  109. }
  110. }
  111. function rotatelog() {
  112. global $LOG, $MAXLOGSIZE, $LOGFILE, $LOGFILEHANDLE;
  113. if ($LOG) {
  114. fwrite ($LOGFILEHANDLE, date("d.m.Y H:i:s")." - Logfile reached maximum size ($MAXLOGSIZE)- rotating.\r\n");
  115. fclose ($LOGFILEHANDLE);
  116. rename ($LOGFILE,"$LOGFILE.old");
  117. $LOGFILEHANDLE=fopen ($LOGFILE, "a");
  118. if (!$LOGFILEHANDLE)
  119. $LOG=0;
  120. else
  121. fwrite ($LOGFILEHANDLE, date("d.m.Y H:i:s")." - Opening new Logfile.\r\n");
  122. }
  123. }
  124. function create_client_socket() {
  125. global $_REQUEST;
  126. if (!isset($_REQUEST["port"])) {
  127. echo $ERRSTR."Port not set.";
  128. return 0;
  129. }
  130. $port = $_REQUEST["port"];
  131. $client = stream_socket_client("tcp://127.0.0.1:".$port);
  132. if ($client) {
  133. stream_set_blocking($client, 1);
  134. }
  135. return $client;
  136. }
  137. function send_server_script_message($command) {
  138. global $_REQUEST;
  139. $client = create_client_socket();
  140. if (!$client) {
  141. logerr("Failed to create client socket");
  142. return FALSE;
  143. }
  144. if (fwrite($client, $command, 1) === FALSE) {
  145. logerr("Failed to send message to server script.");
  146. fclose($client);
  147. return FALSE;
  148. }
  149. fclose($client);
  150. return TRUE;
  151. }
  152. function increase_script_lifetime() {
  153. global $LIFETIME;
  154. set_time_limit($LIFETIME);
  155. logdebug("Script liftetime incremented with $LIFETIME");
  156. }
  157. function write_to_socket($socket, $buffer, $count) {
  158. global $READ_WRITE_ATTEMPTS;
  159. $totalCount = 0;
  160. $retryCount = 0;
  161. do {
  162. if ($retryCount > 0) {
  163. usleep(10000); // 10ms
  164. }
  165. if (!$socket)
  166. break;
  167. $written = fwrite($socket, $buffer, $count);
  168. $buffer = substr($buffer, $written);
  169. $totalCount += $written;
  170. if ($retryCount > 0) {
  171. logdebug("Attempt to write #".($retryCount + 1)." Write: ".$written);
  172. }
  173. $retryCount = $retryCount + 1;
  174. } while($totalCount < $count && $retryCount < $READ_WRITE_ATTEMPTS);
  175. if ($totalCount != $count)
  176. logline("ERROR: Failed to write to socket $count bytes, $totalCount actually written.");
  177. return $totalCount;
  178. }
  179. // reads specified byte count from socket
  180. function read_from_socket($socket, &$buffer, $count) {
  181. global $READ_WRITE_ATTEMPTS;
  182. $totalCount = 0;
  183. $retryCount = 0;
  184. $buffer = "";
  185. $readBuffer;
  186. do {
  187. if ($retryCount > 0) {
  188. usleep(10000); // 10ms
  189. }
  190. if (!$socket)
  191. break;
  192. $readBuffer = fread($socket, $count);
  193. $read = strlen($readBuffer);
  194. $buffer = $buffer.$readBuffer;
  195. if ($retryCount > 0) {
  196. logdebug("Attempt to read #".($retryCount + 1)." Read: ".$read);
  197. }
  198. $totalCount += $read;
  199. $retryCount = $retryCount + 1;
  200. } while($totalCount < $count && $retryCount < $READ_WRITE_ATTEMPTS);
  201. if ($totalCount != $count)
  202. logerr("Failed to read from socket $count bytes, $totalCount actually read.");
  203. return $totalCount;
  204. }
  205. // packet: size of data count | data count | data
  206. // lengths: 1 byte | up to 255 bytes, typically 1 - 5| up to $MaxCount
  207. function write_data_packet($socket, &$buffer, $count) {
  208. $countLength = strlen($count);
  209. // write length of data count digit
  210. write_to_socket($socket, $countLength, 1);
  211. // write data count
  212. write_to_socket($socket, $count, $countLength);
  213. // write data
  214. $writeCount = write_to_socket($socket, $buffer, $count);
  215. if ($writeCount == $count)
  216. return $writeCount;
  217. else
  218. return 0;
  219. }
  220. function read_data_packet($socket, &$buffer) {
  221. // obtain data length digit length
  222. read_from_socket($socket, $countSize, 1);
  223. // read data length
  224. read_from_socket($socket, $readCount, $countSize);
  225. $expectedReadCount = $readCount;
  226. // read data
  227. $readCount = read_from_socket($socket, $buffer, $readCount);
  228. if ($readCount == $expectedReadCount)
  229. return $readCount;
  230. else
  231. return FALSE;
  232. }
  233. // Start of the tunnel script
  234. $isServer = FALSE;
  235. if (version_compare("5.0.0",phpversion())==1) die ("Only PHP 5 or above supported");
  236. error_reporting(0);
  237. set_error_handler("myErrorHandler");
  238. register_shutdown_function ("shutdown");
  239. // no-cache
  240. Header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
  241. Header("Cache-Control: no-cache, must-revalidate");
  242. Header("Pragma: no-cache"); // HTTP/1.1
  243. Header("Last-Modified: ".gmdate("D, d M Y H:i:s")."GMT");
  244. header("Content-Type: application/octet-stream");
  245. ob_implicit_flush();
  246. // Maximum bytes to read at once
  247. $MaxReadCount = 16*1024;
  248. // operation success identification
  249. $OKSTR = "OK:";
  250. $ERRSTR = "ER:";
  251. $CONN_FILE_MAXSIZE = 100;
  252. // Primary tunnel connection
  253. // need the following REQUEST vars:
  254. // a: "c"
  255. // s: remote server name
  256. // p: remote server port
  257. // every operation output at least first three chars identifying the success of operation: "OK:" if succeeded, "ER:" if not.
  258. //
  259. if ($_REQUEST["a"]=="c") { // run server script
  260. $isServer=TRUE;
  261. // clear log
  262. if ($LOG_DEBUG) {
  263. // truncate log file
  264. $logfile = fopen($LOGFILE, 'w');
  265. fclose($logfile);
  266. }
  267. $dad=$_REQUEST["s"];
  268. $dpo=$_REQUEST["p"];
  269. // open the interprocess socket
  270. $errno = 0;
  271. $errstr = "";
  272. $ipsock = stream_socket_server("tcp://127.0.0.1:0", $errno, $errstr);
  273. if (!$ipsock) {
  274. logerr("stream_socket_server() failed: reason:".$errno." ".$errstr);
  275. exit;
  276. }
  277. $port=stream_socket_get_name($ipsock,false);
  278. $port=preg_replace('/^.*?:/','', $port);
  279. stream_set_blocking($ipsock, 1);
  280. // open the remote socket
  281. $rmsock = stream_socket_client("tcp://".$dad.":".$dpo, $errno, $errstr);
  282. if (!$rmsock) {
  283. logerr("Failed to create remote socket at $dad: $dpo. ".$errno." ".$errstr);
  284. exit;
  285. }
  286. else {
  287. if (isset($_REQUEST["nonblock"]))
  288. $block = 0;
  289. else
  290. $block = 1;
  291. stream_set_blocking($rmsock, $block);
  292. logline("Connected to remote $dad: $dpo");
  293. }
  294. // write connection identificator to file. Echo'ing is not appropriate in case of antiviral software, it would be blocked until script finishes
  295. $newConnFile = FALSE;
  296. $connFileMode = "a";
  297. if (file_exists($CONN_FILE)) {
  298. $connFile = fopen($CONN_FILE, "r");
  299. $lstat=fstat($connFile);
  300. fclose($connFile);
  301. if ($lstat["size"]>$CONN_FILE_MAXSIZE) {
  302. $connFileMode = "w";
  303. $newConnFile = TRUE;
  304. }
  305. }
  306. else {
  307. $newConnFile = TRUE;
  308. }
  309. $connFile = fopen($CONN_FILE, $connFileMode);
  310. if ($connFile) {
  311. if ($newConnFile) {
  312. fwrite($connFile, "<?php echo 'Devart HTTP tunnel temporary file.'; exit; ?>\r\n"); // forbid viewing this file through browser
  313. }
  314. $connectionId = str_replace("_", " ", $_REQUEST["id"]);
  315. fwrite ($connFile, $connectionId." ".$port."\r\n");
  316. fclose($connFile);
  317. }
  318. else {
  319. logerr("Failed to create connection temporary file.");
  320. exit;
  321. }
  322. set_time_limit($LIFETIME);
  323. $exit = false;
  324. $buffer = array();
  325. $countBuffer = array();
  326. while (!$exit) {
  327. logdebug("Waiting for client...");
  328. $client = stream_socket_accept($ipsock);
  329. logline("Client accepted");
  330. if ($client === FALSE) {
  331. logline("ERROR: Bad client.");
  332. continue;
  333. }
  334. // read command
  335. $count = read_from_socket($client, $buffer, 1);
  336. if ($count == 0) {
  337. logline("Error reading client command.");
  338. $exit = true;
  339. }
  340. logdebug("Read from client ($count): ".$buffer[0]);
  341. $command = $buffer[0];
  342. increase_script_lifetime();
  343. if ($command == "x") { // close
  344. logline("Shutting down on client request.");
  345. $exit = true; // shutdown
  346. }
  347. else if ($command == "r") { // read
  348. if (!$rmsock) {
  349. logline("ERROR: rmsock is off");
  350. $exit = true;
  351. break;
  352. }
  353. $readCount = 0;
  354. $buffer = fread($rmsock, $MaxReadCount);
  355. if ($buffer === FALSE) {
  356. logline("ERROR: Remote server disconnected.");
  357. $exit = true;
  358. break;
  359. }
  360. else {
  361. $readCount = strlen($buffer);
  362. logline("Read from remote:($readCount)");
  363. }
  364. if ($readCount >= 0) {
  365. if ($readCount == 0)
  366. logline("Nothing to read from remote.");
  367. $writeCount = write_data_packet($client, $buffer, $readCount);
  368. logdebug("Write to client($writeCount): $buffer");
  369. if ($readCount > 0 && $writeCount == 0) {
  370. logerr("Failed to write to client.");
  371. $exit = true;
  372. }
  373. }
  374. }
  375. else if ($command == "w") { // write
  376. if (!$rmsock) {
  377. logline("ERROR: rmsock is off");
  378. $exit = true;
  379. break;
  380. }
  381. $readCount = read_data_packet($client, $buffer);
  382. logline("Write from client: $readCount");
  383. if ($readCount > 0) {
  384. $writeCount = write_to_socket($rmsock, $buffer, $readCount);
  385. logdebug("Write to remote($writeCount): $buffer");
  386. }
  387. }
  388. else if ($command == "l") { // increment lease time
  389. logline("Lease time increased.");
  390. }
  391. else if ($command == "t") { // test connection command
  392. $writeCount = write_to_socket($client, $OKSTR, strlen($OKSTR));
  393. if ($writeCount == 0)
  394. $exit = true;
  395. }
  396. else {
  397. logline("ERROR: Unknown command: $command. Exiting.");
  398. $exit = true;
  399. }
  400. }
  401. logline("Server script closed.");
  402. exit;
  403. }
  404. if ($_REQUEST["a"]=="r") { // read
  405. $client = create_client_socket();
  406. if (!$client) {
  407. logerr("Failed to connect to server script.");
  408. exit;
  409. }
  410. logdebug("Client: Reading from server script");
  411. if (write_to_socket($client, "r", 1) == 0) { // write "Read" command
  412. logerr("Write to server script failed.");
  413. fclose($client);
  414. exit;
  415. }
  416. $buffer;
  417. $readCount = read_data_packet($client, $buffer);
  418. if ($readCount === FALSE) {
  419. logerr("Failed to read response from server script.");
  420. fclose($client);
  421. exit;
  422. }
  423. $totalCount = strlen($OKSTR) + $readCount;
  424. $outputStr = $OKSTR.$buffer;
  425. header("Content-Length: ".$totalCount);
  426. logline("Client: Read from server $readCount");
  427. echo $outputStr;
  428. fclose($client);
  429. exit;
  430. }
  431. if ($_REQUEST["a"]=="w") { // write
  432. $client = create_client_socket();
  433. if (!$client) {
  434. logerr("Failed to connect to server script.");
  435. exit;
  436. }
  437. $postBody= isset($_POST['base64body'])?base64_decode($_POST['base64body']):file_get_contents("php://input"); // Retrieve RAW POST data
  438. $writeData = $postBody;
  439. $expectedWriteCount = strlen($writeData);
  440. $writeCount = write_to_socket($client, "w", 1); // indicate that this is the "Write" command
  441. if ($writeCount > 0)
  442. $writeCount = write_data_packet($client, $writeData, $expectedWriteCount);
  443. if ($writeCount == 0) {
  444. logerr("Write to server script failed.");
  445. fclose($client);
  446. exit;
  447. }
  448. logdebug("Client: Written $writeCount");
  449. fclose($client);
  450. echo $OKSTR;
  451. exit;
  452. }
  453. if ($_REQUEST["a"]=="x") { // close
  454. echo $OKSTR."Shutted down.";
  455. send_server_script_message("x");
  456. exit;
  457. }
  458. if ($_REQUEST["a"] == "l") { // increment server script lease time
  459. if (send_server_script_message("l"))
  460. echo $OKSTR."Incremented server script lease time.";
  461. exit;
  462. }
  463. if ($_REQUEST["a"] == "t") { // test newly created connection
  464. $connectionId = str_replace("_", " ", $_REQUEST["id"]);
  465. logline($connectionId);
  466. $connections = file_get_contents($CONN_FILE);
  467. if ($connections === FALSE) {
  468. logerr("Failed to open $CONN_FILE.");
  469. exit;
  470. }
  471. $lines = explode("\r\n", $connections);
  472. // skip first line
  473. for($i = 1; $i < count($lines); ++$i) {
  474. $line = $lines[$i];
  475. $pos = strpos($line, $connectionId);
  476. if ($pos === FALSE)
  477. continue;
  478. if ($pos === 0) { // starts with
  479. $parts = explode(" ", $line);
  480. if (count($parts) != 3) {
  481. echo "Invalid connection record";
  482. exit;
  483. }
  484. echo $OKSTR.$parts[2]."\n"."$LIFETIME\n";
  485. exit;
  486. }
  487. }
  488. logerr("Connection entry not found.");
  489. exit;
  490. }
  491. logerr("Invalid tunneling script parameter: ".$_REQUEST["a"]);
  492. ?>