I am trying to establish P2P between two PHP daemon deployed on machines in different network (both behind NAT). I searched around for NAT traversal using PHP on Google and seems like their is no existing solution for this in PHP.
Does anyone know about a solution/library to work this around with PHP?
If anybody else is looking for an answer, here is a simple class that does the job:
<?php
class STUNClient
{
private $socket;
public function __construct()
{
//stun.l.google.com
$this->setServerAddr("66.102.1.127", 19302);
$this->createSocket();
}
public function setServerAddr($host, $port = 3478)
{
$this->serverIP = $host;
$this->serverPort = $port;
}
public function createSocket()
{
$this->socket = socket_create(AF_INET, SOCK_DGRAM, getprotobyname("udp"));
socket_set_nonblock($this->socket);
}
public function getPublicIp()
{
$msg = "\x00\x01\x00\x08\xc0\x0c\xee\x42\x7c\x20\x25\xa3\x3f\x0f\xa1\x7f\xfd\x7f\x00\x00\x00\x03\x00\x04\x00\x00\x00\x00";
$numberOfBytesSent = socket_sendto($this->socket, $msg, strlen($msg), 0, $this->serverIP, $this->serverPort);
$st = time();
while (time() - $st < 1) {
socket_recvfrom($this->socket, $data, 32, 0, $remoteIP, $remotePort);
if (strlen($data) < 32) {
continue;
}
break;
}
try {
$info = unpack("nport/C4s", substr($data, 26, 6));
$ip = sprintf("%u.%u.%u.%u", $info["s1"], $info["s2"], $info["s3"], $info["s4"]);
$port = $info['port'];
return [
'ip' => $ip,
'port' => $port
];
} catch (Exception $e) {
return [
'ip' => "0.0.0.0",
'port' => "0"
];
}
}
}
It's used like this:
$sc = new STUNClient;
print_r( $sc->getPublicIp() ); //prnints out the public ip and port
Related
In my project, I have defined two classes Datasource and RedisHelper in Datasource.php file.
Datasource is for getting data from redis.
RedisHelper is for connecting redis.
Here is code of Datasource.php:
<?php
namespace common;
class Datasource {
public static $redises = array();
public function __construct() {}
public static function getRedis($config_name = NULL, $server_region = 'default') {
global $config;
$redis_config = $config['redis'][$config_name];
try {
self::$redises[$config_name] = RedisHelper::instance($config_name, $redis_config, $server_region);
} catch (Exception $e) {
self::$redises[$config_name] = null;
}
return self::$redises[$config_name];
}
}
class RedisHelper {
private $_config_name = "";
private $_redis_config = null;
private $_server_region = null;
public $timeout = 1;
private $_redis = null;
private static $instances = array();
private static $connect_error = 0;
private $call_error = 0;
private function __construct($config_name, $redis_config, $server_region) {
if ($config_name && $redis_config && $server_region) {
$this->_config_name = $config_name;
$this->_redis_config = $redis_config;
$this->_server_region = $server_region;
$this->timeout = isset($this->_redis_config[$server_region]['timeout']) ? $this->_redis_config[$server_region]['timeout'] : $this->timeout;
try {
$this->_redis = new \redis();
$this->_redis->connect($this->_redis_config[$server_region]['host'], $this->_redis_config[$server_region]['port'], $this->timeout);
} catch (Exception $e) {
$this->_redis = null;
}
} else {
$this->_redis = null;
}
}
public static function instance($config_name, $redis_config, $server_region) {
if (!$config_name || !$redis_config) {
return false;
}
$only_key = $config_name . ':' . $server_region;
if (!isset(self::$instances[$only_key])) {
try {
self::$instances[$only_key] = new RedisHelper($config_name, $redis_config, $server_region);
self::$connect_error = 0;
} catch (Exception $e) {
if (self::$connect_error < 2) {
self::$connect_error += 1;
return RedisHelper::instance($config_name, $redis_config, $server_region);
} else {
self::$connect_error = 0;
self::$instances[$only_key] = new RedisHelper(false, false, false);
}
}
}
$redis_config_info = array();
if ($redis_config && isset($redis_config[$server_region]) && isset($redis_config[$server_region]['password'])) {
$redis_config_info = $redis_config[$server_region];
unset($redis_config_info['password']);
}
self::$connect_error = 0;
return self::$instances[$only_key];
}
}
Here is redis.ini.php file:
<?php
$config['redis']['instance1'] = array(
'default' => array(
'host' => '127.0.0.1',
'port' => '6379',
'timeout' => 5,
'pconnect' => 1,
'password' => '',
)
);
$config['redis']['instance2'] = array(
'default' => array(
'host' => '127.0.0.1',
'port' => '6379',
'timeout' => 5,
'pconnect' => 1,
'password' => '',
)
);
Now I want to connect redis with classes, Here is my html code:
<body style="height:100%" >
<div><h3>haha</h3></div>
<?php
include "o1ws1v/class/common/Datasource.php";
include 'o1ws1v/conf/redis.ini.php';
$redis_obj = common\Datasource::getRedis('instance1');
$value = $redis_obj->get("xie");
echo "get key xie is:".$value."\n";
?>
</body>
But It works fail. $redis_obj is nothing in my console. Who can help me?
I try to write code for email verify like that
But i have one problem, 25 port not connect with any mail server
My code like that
Example.php
<?php
require('smtp-validate-email.php');
$from = 'harsukh21#gmail.com'; // for SMTP FROM:<> command
$emails = 'harsukh#gmail.com';
$validator = new SMTP_Validate_Email($emails, $from);
$smtp_results = $validator->validate();
echo "<pre>";print_r($smtp_results);exit;
smtp-validate-email.php
<?php/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. *//** * Description of EmailValidation * * #author Harsukh Makwana <harsukh21#gmail.com> */class EmailValidation{ public $hellodomain = 'itsolutionstuff.com'; public $mailfrom = 'harsukh21#gmail.com'; public $rcptto; public $mx; public $ip; public function __construct() { $this->ip = '192.168.2.14'; } public function checkEmail($email = null) { $this->rcptto = $email; $array = explode('#', $this->rcptto); $dom = $array[1]; if (getmxrr($dom, $mx)) { $this->mx = $mx[rand(0, count($mx) - 1)]; return $this->processRange($this->ip); } return false; } private function asyncRead($sock) { $read_sock = array($sock); $write_sock = NULL; $except_sock = NULL; if (socket_select($read_sock, $write_sock, $except_sock, 5) != 1) { return FALSE; } $ret = socket_read($sock, 512); return $ret; } private function smtpConnect($mta, $src_ip) { $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if ($sock == FALSE) { return array(FALSE, 'unable to open socket'); } if (!socket_bind($sock, $src_ip)) { return array(FALSE, 'unable to bind to src ip'); } $timeout = array('sec' => 10, 'usec' => 0); socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, $timeout); socket_set_nonblock($sock); var_dump(#socket_connect($sock, $mta, 25));exit; #socket_connect($sock, $mta, 25); $ret = $this->asyncRead($sock); if ($ret === FALSE) { return array(FALSE, 'inital read timed out'); } if (!preg_match('/^220/', $ret)) { // Not a good connection. return array(FALSE, $ret); } // Now do the EHLO. socket_write($sock, "HELO ".$this->hellodomain."\r\n"); $ret = $this->asyncRead($sock); if ($ret === FALSE) { return array(FALSE, 'ehlo timed out'); } if (!preg_match('/^250/', $ret)) { // Not a good response. return array(FALSE, $ret); } // Now MAIL FROM. socket_write($sock, "MAIL FROM:<".$this->mailfrom.">\r\n"); $ret = $this->asyncRead($sock); if ($ret === FALSE) { return array(FALSE, 'from timed out'); } if (!preg_match('/^250/', $ret)) // Not a good response. return array(FALSE, $ret); // Now RCPT TO. socket_write($sock, "RCPT TO:<".$this->rcptto.">\r\n"); $ret = $this->asyncRead($sock); if ($ret === FALSE) { return array(FALSE, 'rcpt to timed out'); } if (!preg_match('/^250/', $ret)) { // Not a good response. return array(FALSE, $ret); } // All good. socket_close($sock); return array(true, $ret); } private function processRange($ip) { list($ret, $msg) = $this->smtpConnect($this->mx, $ip); $msg = trim($msg); return $ret; }}
Output
Array
(
[harsukh#gmail.com] =>
[domains] => Array
(
[gmail.com] => Array
(
[users] => Array
(
[0] => harsukh
)
[mxs] => Array
(
[gmail-smtp-in.l.google.com] => 5
[alt1.gmail-smtp-in.l.google.com] => 10
[alt2.gmail-smtp-in.l.google.com] => 20
[alt3.gmail-smtp-in.l.google.com] => 30
[alt4.gmail-smtp-in.l.google.com] => 40
[gmail.com] => 0
)
)
)
)
Here harsukh#gmail.com is a already exist, But i getting null responce
can i use another port for mail server?
You can't use a port that's already in use. So probebly there is already an email server on the same machine where you are running your code.
can i use another port for mail server?
If you plan to use only clients written by yourself, so that you can change the port to which they send data, that can be acceptable, otherwise no.
Trying to sent packages from my webhost to a game server, using this code. However it always results in Unable to open socket: Connection timed out (110) ad I can't figure out why it times out. When I try this on my localhost, it works perfectly but when on webhost, it just times out. I've tried to set the timeout to something else but it didn't help.
define("SERVERDATA_EXECCOMMAND",2);
define("SERVERDATA_AUTH",3);
class RCon {
var $Password;
var $Host;
var $Port = 27015;
var $_Sock = null;
var $_Id = 0;
function RCon ($Host,$Port,$Password) {
$this->Password = $Password;
$this->Host = $Host;
$this->Port = $Port;
$this->_Sock = #fsockopen($this->Host,$this->Port, $errno, $errstr, 30) or
die("Unable to open socket: $errstr ($errno)\n");
$this->_Set_Timeout($this->_Sock,2,500);
}
function Auth () {
$PackID = $this->_Write(SERVERDATA_AUTH,$this->Password);
// Real response (id: -1 = failure)
$ret = $this->_PacketRead();
if ($ret[1]['id'] == -1) {
die("Authentication Failure\n");
}
}
function _Set_Timeout(&$res,$s,$m=0) {
if (version_compare(phpversion(),'4.3.0','<')) {
return socket_set_timeout($res,$s,$m);
}
return stream_set_timeout($res,$s,$m);
}
function _Write($cmd, $s1='', $s2='') {
// Get and increment the packet id
$id = ++$this->_Id;
// Put our packet together
$data = pack("VV",$id,$cmd).$s1.chr(0).$s2.chr(0);
// Prefix the packet size
$data = pack("V",strlen($data)).$data;
// Send packet
fwrite($this->_Sock,$data,strlen($data));
// In case we want it later we'll return the packet id
return $id;
}
function _PacketRead() {
//Declare the return array
$retarray = array();
//Fetch the packet size
while ($size = #fread($this->_Sock,4)) {
$size = unpack('V1Size',$size);
//Work around valve breaking the protocol
if ($size["Size"] > 4096) {
//pad with 8 nulls
$packet = "\x00\x00\x00\x00\x00\x00\x00\x00".fread($this->_Sock,4096);
} else {
//Read the packet back
$packet = fread($this->_Sock,$size["Size"]);
}
array_push($retarray,unpack("V1ID/V1Response/a*S1/a*S2",$packet));
}
return $retarray;
}
function Read() {
$Packets = $this->_PacketRead();
foreach($Packets as $pack) {
if (isset($ret[$pack['ID']])) {
$ret[$pack['ID']]['S1'] .= $pack['S1'];
$ret[$pack['ID']]['S2'] .= $pack['S1'];
} else {
$ret[$pack['ID']] = array(
'Response' => $pack['Response'],
'S1' => $pack['S1'],
'S2' => $pack['S2'],
);
}
}
return $ret;
}
function sendCommand($Command) {
$Command = '"'.trim(str_replace(' ','" "', $Command)).'"';
$this->_Write(SERVERDATA_EXECCOMMAND,$Command,'');
}
function rconCommand($Command) {
$this->sendcommand($Command);
$ret = $this->Read();
//ATM: Source servers don't return the request id, but if they fix this the code below should read as
// return $ret[$this->_Id]['S1'];
return $ret[0]['S1'];
}
}
If I specify udp connection in fsockopen it does not timeout, however if I try tcp, it times out. Most likely caused by the host, but I have no idea where to go from this.
https://developer.valvesoftware.com/wiki/Source_RCON_Protocol
https://developer.valvesoftware.com/wiki/Server_queries
I'm creating this website which lists minecraft servers. Basically I'm trying to ping all these servers as they are displayed. Solving this problem with php doesn't quite solve it for me, pinging all the servers takes time, and I know javascript can execute multiple "pings" at the same time. How would I do this?
The PHP code I'm using now:
class minecraft_server
{
private $address;
private $port;
public function __construct($address, $port = 25565){
$this->address = $address;
$this->port = $port;
}
public function get_ping_info(&$info){
$starttime = microtime(true);
$socket = #fsockopen($this->address, $this->port, $errno, $errstr, 1.0);
$stoptime = microtime(true);
$ping = round(($stoptime-$starttime)*1000);
if ($socket === false){
return false;
}
fwrite($socket, "\xfe\x01");
$data = fread($socket, 256);
if (substr($data, 0, 1) != "\xff"){
return false;
}
if (substr($data, 3, 5) == "\x00\xa7\x00\x31\x00"){
$data = explode("\x00", mb_convert_encoding(substr($data, 15), 'UTF-8', 'UCS-2'));
}else{
$data = explode('ยง', mb_convert_encoding(substr($data, 3), 'UTF-8', 'UCS-2'));
}
if (count($data) == 3){
$info = array(
'version' => '1.3.2',
'motd' => $data[0],
'players' => intval($data[1]),
'max_players' => intval($data[2]),
'ping' => $ping
);
}else{
$info = array(
'version' => $data[0],
'motd' => $data[1],
'players' => intval($data[2]),
'max_players' => intval($data[3]),
'ping' => $ping
);
}
return true;
}
}
And you call the function with:
$server = new minecraft_server(IP, PORT);
if (!$server->get_ping_info($info)){
echo "Offline";
}else{
print_r($info);
}
How would I create a similar thing in javascript?
In your place, I probably would have set up a script that, when called, pings the selected server and prints a parse-able result, then call that script using Ajax whenever a ping needs to be sent.
I have checked the memory whilst sending and receiving data over one connection, and I appear to be correctly clearing variables, as the memory returns to its previous value.
But for some reason if I make a new connection, then close the connection, memory is leaked. I believe the problem may be occurring when a socket is accepted.
I am using PHP 5.2.10
Hopefully one of you can find the time to have a play with the source and figure out where its gone wrong. Thanks in advance
<?php
Class SuperSocket
{
var $listen = array();
var $status_listening = FALSE;
var $sockets = array();
var $event_callbacks = array();
var $recvq = 1;
var $parent;
var $delay = 100; // 10,000th of a second
var $data_buffer = array();
function SuperSocket($listen = array('127.0.0.1:123'))
{
$listen = array_unique($listen);
foreach ($listen as $address)
{
list($address, $port) = explode(":", $address, 2);
$this->listen[] = array("ADDR" => trim($address), "PORT" => trim($port));
};
}
function start()
{
if ($this->status_listening)
{
return FALSE;
};
$this->sockets = array();
$cursocket = 0;
foreach ($this->listen as $listen)
{
if ($listen['ADDR'] == "*")
{
$this->sockets[$cursocket]['socket'] = socket_create_listen($listen['PORT']);
$listen['ADDR'] = FALSE;
}
else
{
$this->sockets[$cursocket]['socket'] = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
};
if ($this->sockets[$cursocket]['socket'] < 0)
{
return FALSE;
};
if (#socket_bind($this->sockets[$cursocket]['socket'], $listen['ADDR'], $listen['PORT']) < 0)
{
return FALSE;
};
if (socket_listen($this->sockets[$cursocket]['socket']) < 0)
{
return FALSE;
};
if (!socket_set_option($this->sockets[$cursocket]['socket'], SOL_SOCKET, SO_REUSEADDR, 1))
{
return FALSE;
};
if (!socket_set_nonblock($this->sockets[$cursocket]['socket']))
{
return FALSE;
};
$this->sockets[$cursocket]['info'] = array("ADDR" => $listen['ADDR'], "PORT" => $listen['PORT']);
$this->sockets[$cursocket]['channels'] = array();
$this->sockets[$cursocket]['id'] = $cursocket;
$cursocket++;
};
$this->status_listening = TRUE;
}
function new_socket_loop(&$socket)
{
$socket =& $this->sockets[$socket['id']];
if ($newchannel = #stream_socket_accept($socket['socket'], 0));//#socket_accept($socket['socket']))
{
socket_set_nonblock($newchannel);
$socket['channels'][]['socket'] = $newchannel;
$channel = array_pop(array_keys($socket['channels']));
$this->remote_address($newchannel, $remote_addr, $remote_port);
$socket['channels'][$channel]['info'] = array('ADDR' => $remote_addr, 'PORT' => $remote_port);
$event = $this->event("NEW_SOCKET_CHANNEL");
if ($event)
$event($socket['id'], $channel, $this);
};
}
function endswith($string, $test) {
$strlen = strlen($string);
$testlen = strlen($test);
if ($testlen > $strlen) return false;
return substr_compare($string, $test, -$testlen) === 0;
}
function recv_socket_loop(&$socket)
{
$socket =& $this->sockets[$socket['id']];
foreach ($socket['channels'] as $channel_id => $channel)
{
unset($buffer);#Flush buffer
$status = #socket_recv($channel['socket'], $buffer, $this->recvq, 0);
if ($status === 0 && $buffer === NULL)
{
$this->close($socket['id'], $channel_id);
}
elseif (!($status === FALSE && $buffer === NULL))
{
$sockid = $socket['id'];
if(!isset($this->data_buffer[$sockid]))
$this->data_buffer[$sockid]='';
if($buffer!="\r"&&$buffer!="\n")
{
//Putty ends with \r\n
$this->data_buffer[$sockid].=$buffer;
}
else if($buffer!="\n") //ignore the additional newline char \n
{
$event = $this->event("DATA_SOCKET_CHANNEL");
if ($event)
$event($socket['id'], $channel_id, $this->data_buffer[$sockid], $this);
unset($this->data_buffer[$sockid]);
}
};
}
}
function stop()
{
$this->closeall();
$this->status_listening = FALSE;
foreach ($this->sockets as $socket_id => $socket)
{
socket_shutdown($socket['socket']);
socket_close($socket['socket']);
};
$event = $this->event("SERVER_STOP");
if ($event)
$event($this);
}
function closeall($socket_id = NULL)
{
if ($socket_id === NULL)
{
foreach ($this->sockets as $socket_id => $socket)
{
foreach ($socket['channels'] as $channel_id => $channel)
{
$this->close($socket_id, $channel_id);
}
}
}
else
{
foreach ($this->sockets[$socket_id]['channels'] as $channel_id => $channel)
{
$this->close($socket_id, $channel_id);
};
};
}
function close($socket_id, $channel_id)
{
unset($this->data_buffer[$socket_id]); //clear the sockets data buffer
$arrOpt = array('l_onoff' => 1, 'l_linger' => 1);
#socket_shutdown($this->sockets[$socket_id]['channels'][$channel_id]['socket']);
#socket_close($this->sockets[$socket_id]['channels'][$channel_id]['socket']);
$event = $this->event("LOST_SOCKET_CHANNEL");
if ($event)
$event($socket_id, $channel_id, $this);
}
function loop()
{
while ($this->status_listening)
{
usleep($this->delay);
foreach ($this->sockets as $socket)
{
$this->new_socket_loop($socket);
$this->recv_socket_loop($socket);
};
$event = $this->event("END_SOCKET_CHANNEL");
if ($event)
$event($this);
};
}
function write($socket_id, $channel_id, $buffer)
{
#socket_write($this->sockets[$socket_id]['channels'][$channel_id]['socket'], $buffer);
#socket_write($this->sockets[$socket_id]['channels'][$channel_id]['socket'], 'Server memory usage: '.memory_get_usage().'/'.memory_get_peak_usage(true)."\r\n");
}
function get_channel_info($socket_id, $channel_id)
{
return $this->sockets[$socket_id]['channels'][$channel_id]['info'];
}
function get_socket_info($socket_id)
{
$socket_info = $this->sockets[$socket_id]['info'];
if (empty($socket_info['ADDR']))
{
$socket_info['ADDR'] = "*";
};
return $socket_info;
}
function get_raw_channel_socket($socket_id, $channel_id)
{
return $this->sockets[$socket_id]['channels'][$channel_id]['socket'];
}
function remote_address($channel_socket, &$ipaddress, &$port)
{
socket_getpeername($channel_socket, $ipaddress, $port);
}
function event($name)
{
if (isset($this->event_callbacks[$name]))
return $this->event_callbacks[$name];
}
function assign_callback($name, $function_name)
{
$this->event_callbacks[$name] = $function_name;
}
};
?>
Server.php
include("supersocket.class.php");
function startswith($string, $test) {
return strpos($string, $test, 0) === 0;
}
function newdata($socket_id, $channel_id, $buffer, &$server)
{
//$server->write($socket_id, $channel_id, ">".$buffer."\r\n");
if($buffer=="STOP")
{
$server->stop();
}
else if($buffer=="DATETIME")
{
$server->write($socket_id, $channel_id, ">".date("dmYHis")."\r\n");
}
else
{
$server->write($socket_id, $channel_id, ">BAD\r\n");
}
};
function newclient($socket_id, $channel_id, &$server)
{
$server->write($socket_id, $channel_id, "HEADER\n\r");
}
$socket = new SuperSocket(array('127.0.0.1:12345'));
$socket->assign_callback("DATA_SOCKET_CHANNEL", "newdata");
$socket->assign_callback("NEW_SOCKET_CHANNEL", "newclient");
$socket->start();
//set_time_limit(60*2);
set_time_limit(60*60*24*5); //5 days
$socket->loop();
Edit: sorry you might need to change the socket accept back to:
if ($newchannel = #socket_accept($socket['socket']))
then close the connection, memory is leaked
This is a tricky one - even the standard reference counting garbage collector only kicks in at intervals which are difficult to predict. Calling gc_collect_cycles() should trigger the gc though. Try calling that whenever you close a connection and see if it makes a difference.
If you're still seeing problems - then check if you've got the cyclic reference counter compiled in - if not, then get it.
The Channel array was never removed upon closing the connection, surprised no one picked up on this. Memory usage is now super tight.
unset($this->sockets[$socket_id]['channels'][$channel_id]);
But it does mean that any event for LOST_SOCKET_CHANNEL is pretty useless for the time being.
Will accept my own answer when stack over flow allows. Thanks for all your help ppl .. i guess..