<?php
ConsoleOut("ngrokphp v1.32-(2016/7/31)");
set_time_limit(0); //设置执行时间
ignore_user_abort(true);
error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);

//检测大小端
define('BIG_ENDIAN', pack('L', 1) === pack('N', 1));

$seraddr = 'tunnel.2bdata.com'; //ngrok服务器地址
$port = 4443; //端口
$is_verify_peer = false; //是否校验证书

//你要映射到通道
$Tunnels = array(
array(
'protocol' => 'http',
'hostname' => 'www.xxx.com',
'subdomain' => '',
'rport' => 0,
'lhost' => '127.0.0.1',
'lport' => 80
),
array(
'protocol' => 'http',
'hostname' => '',
'subdomain' => 'xxx',
'rport' => 0,
'lhost' => '127.0.0.1',
'lport' => 80
),
array(
'protocol' => 'tcp',
'hostname' => '',
'subdomain' => '',
'rport' => 57715,
'lhost' => '127.0.0.1',
'lport' => 22
),

);

//建立隧道协议
$mainsocket = connectremote($seraddr, $port);

//定义变量
$readfds = array();
$writefds = array();

$e = null;
$t = 1;

$socklist = array();
$socklist[] = array('sock' => $mainsocket, 'linkstate' => 0, 'type' => 1);

$ClientId = '';
$recvflag = true;
$starttime = time();//启动时间
$pingtime = 0;

//注册退出执行函数
register_shutdown_function('shutdown',$mainsocket);
while ($recvflag) {

//重排
array_filter($socklist);
sort($socklist);

//检测控制连接是否连接.
if ($mainsocket == false) {
$ip = dnsopen($seraddr, $port);//解析dns, port
if (!$ip) {
ConsoleOut('update dns');
sleep(1);
continue;
}
$mainsocket = connectremote($ip, $port);
if(!$mainsocket) {
ConsoleOut('connect failed...!');
sleep(10);
continue;
}
$socklist[] = array('sock' => $mainsocket, 'linkstate' => 0, 'type' => 1);
}

//如果非cli超过1小时自杀
if (is_cli() == false) {
if ($starttime + 3600 < time()) {
fclose($mainsocket);
$recvflag = false;
break;
}
}

//发送心跳
if ($pingtime + 25 < time() && $pingtime != 0) {
sendpack($mainsocket, Ping());
$pingtime = time();
}

//重新赋值
$readfds = array();
$writefds = array();
foreach ($socklist as $k => $z) {
if (is_resource($z['sock'])) {
$readfds[] = $z['sock'];
if ($z['linkstate'] == 0) {
$writefds[] = $z['sock'];
}
} else {
//close的时候不是资源。。移除
if ($z['type'] == 1) {
ConsoleOut('z:1');
$mainsocket = false;
}
unset($z['type']);
unset($z['sock']);
unset($z['tosock']);
unset($z['recvbuf']);
array_splice($socklist, $k, 1);
}
}

//查询
$res = stream_select($readfds, $writefds, $e, $t);
if ($res === false) {
ConsoleOut('sockerr');
}

//有事件
if ($res > 0) {
foreach ($socklist as $k => $sockinfo) {
$sock = $sockinfo['sock'];
//可读
if (in_array($sock, $readfds)) {

$recvbut = fread($sock, 1024);

if ($recvbut == false || strlen($recvbut) == 0) {
//主连接关闭,关闭所有
if ($sockinfo['type'] == 1) {
$mainsocket = false;
}
if ($sockinfo['type'] == 3) {
fclose($sockinfo['tosock']);
}
unset($sockinfo['type']);
unset($sockinfo['sock']);
unset($sockinfo['tosock']);
unset($sockinfo['recvbuf']);
unset($socklist[$k]);
continue;
}

if (strlen($recvbut) > 0) {
if (!isset($sockinfo['recvbuf'])) {
$sockinfo['recvbuf'] = $recvbut;
} else {
$sockinfo['recvbuf'] = $sockinfo['recvbuf'] . $recvbut;
}
$socklist[$k] = $sockinfo;
}

//控制连接,或者远程未连接本地连接
if ($sockinfo['type'] == 1 || ($sockinfo['type'] == 2 && $sockinfo['linkstate'] == 1)) {
$allrecvbut = $sockinfo['recvbuf'];
//处理
$lenbuf = substr($allrecvbut, 0, 8);
$len = tolen1($lenbuf);
if (strlen($allrecvbut) >= (8 + $len)) {
$json = substr($allrecvbut, 8, $len);
ConsoleOut($json);
$js = json_decode($json, true);

//远程主连接
if ($sockinfo['type'] == 1) {
if ($js['Type'] == 'ReqProxy') {
$newsock = connectremote($seraddr, $port);
if ($newsock) {
$socklist[] = array('sock' => $newsock, 'linkstate' => 0, 'type' => 2);
}
}
if ($js['Type'] == 'AuthResp') {
$ClientId = $js['Payload']['ClientId'];
$pingtime = time();
sendpack($sock, Ping());
foreach ($Tunnels as $tunnelinfo) {
//注册端口
sendpack($sock, ReqTunnel($tunnelinfo['protocol'], $tunnelinfo['hostname'], $tunnelinfo['subdomain'], $tunnelinfo['rport']));
}
}
if ($js['Type'] == 'NewTunnel') {
if ($js['Payload']['Error'] != null) {//判断NewTunnel是否有错误
ConsoleOut('Add tunnel failed,'.$js['Payload']['Error']);//注册失败
sleep(60);//延迟60后继续注册
continue;
}
ConsoleOut('Add tunnel ok,type:' . $js['Payload']['Protocol'] .

Next
Pg.: 1 2 3 4


Back to home | File page

Subscribe | Register | Login | N