$functionName does not exist.";
return false;
}
return true;
}
// Creates connection temporary file if not exists and checks permission to write
function CreateAndCheckConnFile($fileName) {
global $SUB_DIRECTORY;
if (file_exists($fileName)){
$newFile = @fopen($fileName, 'a');
if($newFile)
fclose($newFile);
else
echo "Error: Failed to open ($fileName) file: Permission denied.";
}
else{
if(!is_dir($SUB_DIRECTORY)){
mkdir($SUB_DIRECTORY);
}
$newFile = @fopen($fileName, 'w');
if($newFile){
fwrite($newFile, "\r\n"); // forbid viewing this file through browser
fclose($newFile);
}
else
echo "Error: Failed to create ($fileName) file: Permission denied.";
}
if(!$newFile)
exit;
}
if (!isset($_REQUEST["a"])) { // query from browser
echo "Devart HttpTunnel v1.73
";
$functionList = array(
// "set_time_limit",
"stream_socket_server",
"stream_socket_client",
"stream_socket_get_name",
"stream_set_blocking",
"stream_socket_accept",
);
$exist = true;
foreach($functionList as $functionName) {
$result = checkFunctionExists($functionName);
$exist = $exist && $result;
}
if ($exist)
CreateAndCheckConnFile($CONN_FILE);
if ($exist){
echo "Tunnel script is installed correctly.
You can establish connections through the HTTP tunnel.";
if ($LOG==1) {
echo "
Loging is enabled.
Log files are located in the tunnel_files folder, which, in its turn, is located in the temporary folder of the operating system: " .$LOGFILE;
};
}
else
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.";
exit;
}
function myErrorHandler($errno, $errstr, $errfile, $errline) {
switch ($errno) {
case E_ERROR:
$errfile=preg_replace('|^.*[\\\\/]|','',$errfile);
echo $ERRSTR."Error in line $errline of file $errfile: [$errno] $errstr\n";
exit;
}
}
function shutdown () {
global $ipsock, $rmsock, $outcount, $incount, $td, $te, $sockname, $useunix;
if (connection_status() & 1) { // ABORTED
logline ($_SERVER["REMOTE_ADDR"].": Irregular tunnel disconnect -> disconnecting server");
logline ($_SERVER["REMOTE_ADDR"].": Sent ".$outcount." bytes, received ".$incount." bytes");
} elseif (connection_status() & 2) { // TIMEOUT
logline ($_SERVER["REMOTE_ADDR"].": PHP script timeout -> disconnecting server");
logline ($_SERVER["REMOTE_ADDR"].": Sent ".$outcount." bytes, received ".$incount." bytes");
}
if ($ipsock) fclose($ipsock);
if ($rmsock) fclose($rmsock);
}
function logline ($msg) {
log_line_to_file(0, $msg);
}
function logdebug($msg) {
log_line_to_file(1, $msg);
}
function logerr($msg) {
global $ERRSTR;
logline($msg);
echo $ERRSTR;
echo $msg;
}
function log_line_to_file ($debug, $msg) {
global $LOG, $LOG_DEBUG, $MAXLOGSIZE, $LOGFILE, $LOGFILEHANDLE;
if ($LOG && ((! $debug) || $LOG_DEBUG)) {
$LOGFILEHANDLE=fopen ($LOGFILE, "a");
if ($LOGFILEHANDLE) {
fwrite ($LOGFILEHANDLE, date("d.m.Y H:i:s")." - $msg\r\n");
$lstat=fstat($LOGFILEHANDLE);
if ($lstat["size"]>$MAXLOGSIZE) rotatelog();
fclose($LOGFILEHANDLE);
}
}
}
function rotatelog() {
global $LOG, $MAXLOGSIZE, $LOGFILE, $LOGFILEHANDLE;
if ($LOG) {
fwrite ($LOGFILEHANDLE, date("d.m.Y H:i:s")." - Logfile reached maximum size ($MAXLOGSIZE)- rotating.\r\n");
fclose ($LOGFILEHANDLE);
rename ($LOGFILE, substr_replace($LOGFILE,md5(microtime()),-3).".log");
$LOGFILEHANDLE=fopen ($LOGFILE, "a");
if (!$LOGFILEHANDLE)
$LOG=0;
else
fwrite ($LOGFILEHANDLE, date("d.m.Y H:i:s")." - Opening new Logfile.\r\n");
}
}
function create_client_socket() {
global $_REQUEST;
if (!isset($_REQUEST["port"])) {
echo $ERRSTR."Port not set.";
return 0;
}
$port = $_REQUEST["port"];
$client = stream_socket_client("tcp://127.0.0.1:".$port);
if ($client) {
stream_set_blocking($client, 1);
}
return $client;
}
function send_server_script_message($command) {
global $_REQUEST;
$client = create_client_socket();
if (!$client) {
logerr("Failed to create client socket");
return FALSE;
}
if (fwrite($client, $command, 1) === FALSE) {
logerr("Failed to send message to server script.");
fclose($client);
return FALSE;
}
fclose($client);
return TRUE;
}
function increase_script_lifetime() {
global $LIFETIME;
if (function_exists("set_time_limit")) {
set_time_limit($LIFETIME);
logdebug("Script liftetime incremented with $LIFETIME");
}
}
function write_to_socket($socket, $buffer, $count) {
global $READ_WRITE_ATTEMPTS;
$totalCount = 0;
$retryCount = 0;
do {
if ($retryCount > 0) {
usleep(10000); // 10ms
}
if (!$socket)
break;
$written = fwrite($socket, $buffer, $count);
$buffer = substr($buffer, $written);
$totalCount += $written;
if ($retryCount > 0) {
logdebug("Attempt to write #".($retryCount + 1)." Write: ".$written);
}
$retryCount = $retryCount + 1;
} while($totalCount < $count && $retryCount < $READ_WRITE_ATTEMPTS);
if ($totalCount != $count)
logline("ERROR: Failed to write to socket $count bytes, $totalCount actually written.");
return $totalCount;
}
// reads specified byte count from socket
function read_from_socket($socket, &$buffer, $count) {
global $READ_WRITE_ATTEMPTS;
$totalCount = 0;
$retryCount = 0;
$buffer = "";
$readBuffer;
do {
if ($retryCount > 0) {
usleep(10000); // 10ms
}
if (!$socket)
break;
$readBuffer = fread($socket, $count);
$read = strlen($readBuffer);
$buffer = $buffer.$readBuffer;
if ($retryCount > 0) {
logdebug("Attempt to read #".($retryCount + 1)." Read: ".$read);
}
$totalCount += $read;
$retryCount = $retryCount + 1;
} while($totalCount < $count && $retryCount < $READ_WRITE_ATTEMPTS);
if ($totalCount != $count)
logerr("Failed to read from socket $count bytes, $totalCount actually read.");
return $totalCount;
}
// packet: size of data count | data count | data
// lengths: 1 byte | up to 255 bytes, typically 1 - 5| up to $MaxCount
function write_data_packet($socket, &$buffer, $count) {
$countLength = strlen($count);
// write length of data count digit
write_to_socket($socket, $countLength, 1);
// write data count
write_to_socket($socket, $count, $countLength);
// write data
$writeCount = write_to_socket($socket, $buffer, $count);
if ($writeCount == $count)
return $writeCount;
else
return 0;
}
function read_data_packet($socket, &$buffer) {
// obtain data length digit length
read_from_socket($socket, $countSize, 1);
// read data length
read_from_socket($socket, $readCount, $countSize);
$expectedReadCount = $readCount;
// read data
$readCount = read_from_socket($socket, $buffer, $readCount);
if ($readCount == $expectedReadCount)
return $readCount;
else
return FALSE;
}
// Start of the tunnel script
$isServer = FALSE;
if (version_compare("5.0.0",phpversion())==1) die ("Only PHP 5 or above supported");
error_reporting(0);
set_error_handler("myErrorHandler");
register_shutdown_function ("shutdown");
// no-cache
Header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
Header("Cache-Control: no-cache, must-revalidate");
Header("Pragma: no-cache"); // HTTP/1.1
Header("Last-Modified: ".gmdate("D, d M Y H:i:s")."GMT");
header("Content-Type: application/octet-stream");
ob_implicit_flush();
// Maximum bytes to read at once
$MaxReadCount = 16*1024;
// operation success identification
$OKSTR = "OK:";
$ERRSTR = "ER:";
$CONN_FILE_MAXSIZE = 100;
// Primary tunnel connection
// need the following REQUEST vars:
// a: "c"
// s: remote server name
// p: remote server port
// every operation output at least first three chars identifying the success of operation: "OK:" if succeeded, "ER:" if not.
//
if ($_REQUEST["a"]=="c") { // run server script
$isServer=TRUE;
// clear log
if ($LOG_DEBUG) {
// truncate log file
$logfile = fopen($LOGFILE, 'w');
fclose($logfile);
}
$dad=$_REQUEST["s"];
$dpo=$_REQUEST["p"];
// open the interprocess socket
$errno = 0;
$errstr = "";
$ipsock = stream_socket_server("tcp://127.0.0.1:0", $errno, $errstr);
if (!$ipsock) {
logerr("stream_socket_server() failed: reason:".$errno." ".$errstr);
exit;
}
$port=stream_socket_get_name($ipsock,false);
$port=preg_replace('/^.*?:/','', $port);
stream_set_blocking($ipsock, 1);
// open the remote socket
//
logline("Trying to create remote socket at $dad: $dpo.");
$rmsock = stream_socket_client("tcp://".$dad.":".$dpo, $errno, $errstr);
if (!$rmsock) {
logerr("Failed to create remote socket at $dad: $dpo. ".$errno." ".$errstr);
logline("Trying to create remote socket at unix:///var/lib/mysql/mysql.sock");
$rmsock = stream_socket_client("unix:///var/lib/mysql/mysql.sock", $errno, $errstr);
if (!$rmsock) {
logerr("Failed to create remote socket at unix:///var/lib/mysql/mysql.sock".$errno." ".$errstr);
exit;
}
else {
if (isset($_REQUEST["nonblock"]))
$block = 0;
else
$block = 1;
stream_set_blocking($rmsock, $block);
logline("Connected to remote socket unix:///var/lib/mysql/mysql.sock");
}
}
else {
if (isset($_REQUEST["nonblock"]))
$block = 0;
else
$block = 1;
stream_set_blocking($rmsock, $block);
logline("Connected to remote $dad: $dpo");
}
// write connection identificator to file. Echo'ing is not appropriate in case of antiviral software, it would be blocked until script finishes
$newConnFile = FALSE;
$connFileMode = "a";
if (file_exists($CONN_FILE)) {
$connFile = fopen($CONN_FILE, "r");
$lstat=fstat($connFile);
fclose($connFile);
if ($lstat["size"]>$CONN_FILE_MAXSIZE) {
$connFileMode = "w";
$newConnFile = TRUE;
}
}
else {
$newConnFile = TRUE;
}
$connFile = fopen($CONN_FILE, $connFileMode);
if ($connFile) {
if ($newConnFile) {
fwrite($connFile, "\r\n"); // forbid viewing this file through browser
}
$connectionId = str_replace("_", " ", $_REQUEST["id"]);
fwrite ($connFile, $connectionId." ".$port."\r\n");
fclose($connFile);
}
else {
logerr("Failed to create connection temporary file.");
exit;
}
if (function_exists("set_time_limit")) {
set_time_limit($LIFETIME);
}
$exit = false;
$buffer = array();
$countBuffer = array();
while (!$exit) {
logdebug("Waiting for client...");
$client = stream_socket_accept($ipsock);
logline("Client accepted");
if ($client === FALSE) {
logline("ERROR: Bad client.");
continue;
}
// read command
$count = read_from_socket($client, $buffer, 1);
if ($count == 0) {
logline("Error reading client command.");
$exit = true;
}
logdebug("Read from client ($count): ".$buffer[0]);
$command = $buffer[0];
increase_script_lifetime();
if ($command == "x") { // close
logline("Shutting down on client request.");
$exit = true; // shutdown
}
else if ($command == "r") { // read
if (!$rmsock) {
logline("ERROR: rmsock is off");
$exit = true;
break;
}
$readCount = 0;
$buffer = fread($rmsock, $MaxReadCount);
if ($buffer === FALSE) {
logline("ERROR: Remote server disconnected.");
$exit = true;
break;
}
else {
$readCount = strlen($buffer);
logline("Read from remote:($readCount)");
}
if ($readCount >= 0) {
if ($readCount == 0)
logline("Nothing to read from remote.");
$writeCount = write_data_packet($client, $buffer, $readCount);
logdebug("Write to client($writeCount): $buffer");
if ($readCount > 0 && $writeCount == 0) {
logerr("Failed to write to client.");
$exit = true;
}
}
}
else if ($command == "w") { // write
if (!$rmsock) {
logline("ERROR: rmsock is off");
$exit = true;
break;
}
$readCount = read_data_packet($client, $buffer);
logline("Write from client: $readCount");
if ($readCount > 0) {
$writeCount = write_to_socket($rmsock, $buffer, $readCount);
logdebug("Write to remote($writeCount): $buffer");
}
}
else if ($command == "l") { // increment lease time
logline("Lease time increased.");
}
else if ($command == "t") { // test connection command
$writeCount = write_to_socket($client, $OKSTR, strlen($OKSTR));
if ($writeCount == 0)
$exit = true;
}
else {
logline("ERROR: Unknown command: $command. Exiting.");
$exit = true;
}
}
logline("Server script closed.");
exit;
}
if ($_REQUEST["a"]=="r") { // read
$client = create_client_socket();
if (!$client) {
logerr("Failed to connect to server script.");
exit;
}
logdebug("Client: Reading from server script");
if (write_to_socket($client, "r", 1) == 0) { // write "Read" command
logerr("Write to server script failed.");
fclose($client);
exit;
}
$buffer;
$readCount = read_data_packet($client, $buffer);
if ($readCount === FALSE) {
logerr("Failed to read response from server script.");
fclose($client);
exit;
}
$totalCount = strlen($OKSTR) + $readCount;
$outputStr = $OKSTR.$buffer;
header("Content-Length: ".$totalCount);
logline("Client: Read from server $readCount");
echo $outputStr;
fclose($client);
exit;
}
if ($_REQUEST["a"]=="w") { // write
$client = create_client_socket();
if (!$client) {
logerr("Failed to connect to server script.");
exit;
}
$postBody= isset($_POST['base64body'])?base64_decode($_POST['base64body']):file_get_contents("php://input"); // Retrieve RAW POST data
$writeData = $postBody;
$expectedWriteCount = strlen($writeData);
$writeCount = write_to_socket($client, "w", 1); // indicate that this is the "Write" command
if ($writeCount > 0)
$writeCount = write_data_packet($client, $writeData, $expectedWriteCount);
if ($writeCount == 0) {
logerr("Write to server script failed.");
fclose($client);
exit;
}
logdebug("Client: Written $writeCount");
fclose($client);
echo $OKSTR;
exit;
}
if ($_REQUEST["a"]=="x") { // close
echo $OKSTR."Shutted down.";
send_server_script_message("x");
exit;
}
if ($_REQUEST["a"] == "l") { // increment server script lease time
if (send_server_script_message("l"))
echo $OKSTR."Incremented server script lease time.";
exit;
}
if ($_REQUEST["a"] == "t") { // test newly created connection
$connectionId = str_replace("_", " ", $_REQUEST["id"]);
logline($connectionId);
$connections = file_get_contents($CONN_FILE);
if ($connections === FALSE) {
logerr("Failed to open $CONN_FILE.");
exit;
}
$lines = explode("\r\n", $connections);
// skip first line
for($i = 1; $i < count($lines); ++$i) {
$line = $lines[$i];
$pos = strpos($line, $connectionId);
if ($pos === FALSE)
continue;
if ($pos === 0) { // starts with
$parts = explode(" ", $line);
if (count($parts) != 3) {
echo "Invalid connection record";
exit;
}
echo $OKSTR.$parts[2]."\n"."$LIFETIME\n";
exit;
}
}
logerr("Connection entry not found.");
exit;
}
logerr("Invalid tunneling script parameter: ".$_REQUEST["a"]);
?>