forked from Mirrors/openclonk
Add a way to test hosts for their hosting capabilities to the masterserver.
parent
2b33a55cfb
commit
9f855c6067
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
// If not in Include mode: Perform test!
|
||||
if(!$C4HostTestIncludeMode)
|
||||
if(performHostTest($_GET['ip'],$_GET['tcp'],$_GET['udp'],$_GET['time']))
|
||||
echo "Reached=Yes\n";
|
||||
else
|
||||
echo "Reached=No\n";
|
||||
/**
|
||||
* Parse configuration and call actual host testing.
|
||||
* May open a http connection
|
||||
*
|
||||
* @param string $req Request Data
|
||||
*/
|
||||
function testHostConn(&$req) {
|
||||
global $config;
|
||||
$mode = ParseINI::parseValue('hosttest_mode', $config);
|
||||
if($mode == 0)
|
||||
return true; // Say it works if we didn't test.
|
||||
$timeout = ParseINI::parseValue('hosttest_timeout', $config);
|
||||
$addr = ParseINI::parseValue('Address', $req);
|
||||
preg_match('/TCP:0.0.0.0:([0-9]+),|$/',$addr, $addr_tcp);
|
||||
$tcpport = count($addr_tcp)==2 ? ((int) $addr_tcp[1]) : 0; // Be sure that this is injection safe: this machine might have elevated privilegues on the host test provider based on ip
|
||||
preg_match('/UDP:0.0.0.0:([0-9]+),|$/',$addr, $addr_udp);
|
||||
$udpport = count($addr_udp)==2 ? ((int) $addr_udp[1]) : 0;
|
||||
if(!$udpport && !$tcpport)
|
||||
return false; // That might be a false false, but it's weird anyway.
|
||||
unset($addr, $addr_tcp, $addr_udp);
|
||||
if($mode == 1) { // Perform the check from local host
|
||||
return performHostTest($_SERVER['REMOTE_ADDR'],$tcpport,$udpport,$timeout);
|
||||
} else { // Call hostchecker on remote service
|
||||
$url = ParseINI::parseValue('hosttest_url', $config);
|
||||
$remotetimeout = ParseINI::parseValue('hosttest_remote_timeout', $config);
|
||||
if(!preg_match('#^([a-z.]+\.[a-z]+):([0-9]+)(/.*)$#', $url, $url_split)) {
|
||||
trigger_error('Unable to parse hosttest_url from config.', E_USER_WARNING);
|
||||
return true; // As earlier
|
||||
}
|
||||
$remote_start = microtime(true);
|
||||
$fp = fsockopen($url_split[1], $url_split[2], $errno, $errstr, $remotetimeout);
|
||||
if(!$fp) {
|
||||
trigger_error('Unable to connect to remote C4HostTest '.$url_split[1].':'.$url_split[2]." in $removetimeout secons.", E_USER_WARNING);
|
||||
return true;
|
||||
}
|
||||
$req_str = $url_split[3] . '?remotecall&tcp='.$tcpport.'&udp='.$udpport.'&ip='.$_SERVER['REMOTE_ADDR'].'&time='.$timeout;
|
||||
$remotetimeout += $timeout;
|
||||
fwrite($fp, "GET ".$req_str." HTTP/1.0\r\nHost: ".$url_split[1]."\r\nUser-Agent: C4Masterserver\r\n\r\n"); // Injection warning!
|
||||
$reply = '';
|
||||
do {
|
||||
stream_set_timeout($fp, $remotetimeout-microtime(true)+$remote_start);
|
||||
$reply .= fread($fp, 8192);
|
||||
} while (!feof($fp) && $remotetimeout > (microtime(true)-$remote_start));
|
||||
if(!preg_match('#^HTTP/1.[01] 200#', $reply)) {
|
||||
trigger_error('Unable to process response from C4HostTest. Wrong address in hosttest_url? No redirects allowed!', E_USER_WARNING);
|
||||
return true;
|
||||
}
|
||||
if(!preg_match('/\r\n\r\n(.*\n)?Reached=[Yy]es\r?\n/s', $reply))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Perform host test on $host with $tcpport and $udpport, maximally take $timeout time.
|
||||
*
|
||||
*/
|
||||
function performHostTest ($host,$tcpport,$udpport,$timeout) {
|
||||
global $C4HostTestIncludeMode;
|
||||
if($tcpport < 1024)
|
||||
$tcpport = false;
|
||||
if($udpport < 1024)
|
||||
$udpport = false;
|
||||
if($timeout < 0.001) // 0 check
|
||||
return false;
|
||||
if($timeout > 60)
|
||||
$timeout = 60; // Actually, something larger than 30 seconds doesn't make much sense
|
||||
$udpreqhex = array( // Magic UDP data stolen from a capture.
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
|
||||
0x00, 0x02, 0x00, 0x2b, 0x73, 0x3e, 0xb4, 0x6c,
|
||||
0xec, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00 );
|
||||
$udpreq = '';
|
||||
for($i=0; $i<count($udpreqhex); ++$i) {
|
||||
$udpreq .= chr($udpreqhex[$i]);
|
||||
}
|
||||
$timeend = microtime(true) + $timeout;
|
||||
$udpreply = false;
|
||||
while(($cyclestart = microtime(true)) < $timeend) {
|
||||
if(!$udp && $udpport)
|
||||
if($udp = @fsockopen('udp://'.$host, $udpport, $udperrno, $udperrstr, 0.1)) // Timeout 1 sec, should theretically not take 'time' at all
|
||||
stream_set_blocking($udp, false);
|
||||
if($udp && !$udpreply) {
|
||||
if(microtime(true) - $lastudppack > $timeout/5) // Send 5 Paks a max, usually because tcp waits for half timeout.
|
||||
fwrite($udp, $udpreq);
|
||||
$lastudppack = microtime(true);
|
||||
$udpreply = fread($udp,1500);
|
||||
}
|
||||
if(!$tcp && $tcpport)
|
||||
$tcp = @fsockopen($host, $tcpport, $tcperrno, $tcperrstr, min($timeout/2 - 0.1, $timeend-microtime(true)));
|
||||
usleep(max(0,min($timeout*1000000/20,microtime(true)-$cylcestart))); // That means 20 cycles limit, even if $stuff goes wrong
|
||||
if ($tcp || $udpreply)
|
||||
break;
|
||||
}
|
||||
if(!$C4HostTestIncludeMode)
|
||||
if($tcperrno && !$tcp)
|
||||
echo "TCP=Error $tcperrno - $tcperrstr\n";
|
||||
else if($tcp)
|
||||
echo "TCP=Reached\n";
|
||||
else
|
||||
echo "TCP=Unknown\n"; //Somewhat unbeliegable that UDP replied that quick.
|
||||
if(!$C4HostTestIncludeMode)
|
||||
if($udperrno && !$udp)
|
||||
echo "UDP=Error $udperrno - $udperrstr\n";
|
||||
if(!$udpreply && $udp)
|
||||
$udpreply = fread($udp,1500); // Last chance udp!
|
||||
if(!$C4HostTestIncludeMode)
|
||||
if(!$tcp && !$udpreply) // we can't say if tcp was just faster, so: ignore
|
||||
echo "UDP=Timeout\n";
|
||||
else if($tcp && !$udpreply)
|
||||
echo "UDP=Unknown\n";
|
||||
else if($udpreply)
|
||||
echo "UDP=Reached\n";
|
||||
return ($tcp || $udpreply); // Reachable if a tcp connection could be made, udp replied
|
||||
}
|
||||
?>
|
|
@ -46,3 +46,15 @@ oc_update_url=http://www.example.com/
|
|||
|
||||
;secret key for the HAMC-updating system (using sha256) to verify files
|
||||
oc_update_secret=
|
||||
|
||||
;host port testing: 0 - off (default), 1 - perfrom from local host, 2 - perform from from hosttest_url
|
||||
hosttest_mode=0
|
||||
|
||||
;time for the client to reply - keep in mind that if that test fails, a client will have to wait at least that long.
|
||||
hosttest_timeout=6
|
||||
|
||||
;remote address of host testing script, supply in format of masterserver address in oc config
|
||||
hosttest_url=
|
||||
|
||||
;hosttest provider answer time. Additional time that may be consumed by establishing the http connection, etc.
|
||||
hosttest_remote_timeout=1.5
|
||||
|
|
|
@ -14,6 +14,8 @@ require_once('include/C4Masterserver.php');
|
|||
require_once('include/C4Network.php');
|
||||
require_once('include/FloodProtection.php');
|
||||
require_once('include/ParseINI.php');
|
||||
$C4HostTestIncludeMode = true;
|
||||
require_once('include/C4HostTest.php');
|
||||
|
||||
$config = file_get_contents('include/config.ini');
|
||||
$link = mysql_connect(
|
||||
|
@ -85,7 +87,11 @@ if ($link && $db) {
|
|||
} else {
|
||||
$csid = $server->addReference($reference);
|
||||
if ($csid) {
|
||||
C4Network::sendAnswer(C4Network::createAnswer(array('Status' => 'Success', 'CSID' => $csid)));
|
||||
$answer = array('Status' => 'Success', 'CSID' => $csid);
|
||||
if(!testHostConn($input))
|
||||
$answer['Message'] = 'Your network failed to pass certain tests. It is unlikely that are you able to host for the public.|To fix that, you need port forwarding in your router.';
|
||||
C4Network::sendAnswer(C4Network::createAnswer($answer));
|
||||
unset($answer);
|
||||
} else {
|
||||
C4Network::sendAnswer(C4Network::createError('Round signup failed. (To many tries?)'));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue