PHP Socket timeout won't go below 0.5 seconds - php

I'm trying to scan an ip range and get info from specific devices.
Problem is, although i have SO_RCVTIMEO to array('sec'=>0,'usec'=>1000)
each request that should timeout gets around 0.5 seconds. The others on the other hand only take 0.005 seconds. You can imagine that if i want to scan a big IP range then i'm doomed.
What am i doing wrong or how can i improve it ?
Below is the code
foreach($ipArray as $ip){
$result = array();
$buf = '';
$from = '';
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_set_option($sock,SOL_SOCKET,SO_RCVTIMEO,$timeout);
socket_set_option($sock, SOL_SOCKET, SO_BROADCAST, 1);
//socket_bind($sock, $from, 2048);
socket_set_option($sock, SOL_SOCKET, SO_BROADCAST, 0);
socket_sendto($sock, $data1, strlen($data1), 0, $ip, 10001);
$time = microtime(true);
if(!socket_recvfrom($sock, $buf, 512000, 0, $from, $port)){
echo (microtime(true) - $time) . ' elapsed<br><br><br>';
continue;
}
echo (microtime(true) - $time) . ' elapsed<br><br><br>';
$result= parse_result(bin2hex($buf));
socket_close($sock);
}
Note that i'm creating a separate socket for each IP because for some reason otherwise results would get confused in between the ips and i would sometimes get the same result twice.
Running this on a Windows Bitnami machine.
Thanks

Turns out Windows has a minimum amount of timeout you can set which is way higher than that on linux.
Basically the array('sec'=>0,'usec'=>1000) usec part of it only works in Linux machines.

Related

Wait for socket_write to complete before writing again

I need to send some data from a PHP server to a Node server.
Here's a simplified version of what I have so far on the PHP side:
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = socket_connect($socket, '127.0.0.1', '3000');
socket_write($socket, "Hello World", 11);
socket_close($socket);
Running this code successfully sends the message to my Node server and outputs this.
However I need to send more than one message back to the socket. I actually need to send several thousand responses. But if I were to call the socket_write() function within a loop like so:
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = socket_connect($socket, '127.0.0.1', '3000');
for($i=0;$i<1000;$i++){
socket_write($socket, "Hello World", 11);
}
socket_close($socket);
it seems to append all the data together and send it in one go at the end of the script. I need to send the data back in real time. Is there any way I can do this in PHP?
After looking through many PHP socket libraries, I noticed that nearly all of them just implement a usleep(1000000) after sending a socket. I'm not sure if this is the best method, but maybe it's the only way due to PHP's limitations.
In the end, I decided to store the time that data was last written to a socket. Then, calculate the time elapsed since then and subtract that from 1000000 microseconds. This way, the script will not wait longer than necessary.
public function send(array $payload){
$json = json_encode($payload);
$write = socket_write($this->socket, $json, strlen($json);
$sleep_time = 1000000 - (microtime(true) - $this->last_sent);
if($sleep_time > 0) usleep($sleep_time);
$this->last_sent = microtime(true);
return $write !== false ? true : false;
}

How can I ping a server port with PHP?

I want a PHP script which allows you to ping an IP address and a port number (ip:port). I found a similar script but it works only for websites, not ip:port.
<?php
function ping($host, $port, $timeout)
{
$tB = microtime(true);
$fP = fSockOpen($host, $port, $errno, $errstr, $timeout);
if (!$fP) { return "down"; }
$tA = microtime(true);
return round((($tA - $tB) * 1000), 0)." ms";
}
//Echoing it will display the ping if the host is up, if not it'll say "down".
echo ping("www.google.com", 80, 10);
?>
I want this for a game server.
The idea is that I can type in the IP address and port number, and I get the ping response.
I think the answer to this question pretty much sums up the problem with your question.
If what you want to do is find out whether a given host will accept
TCP connections on port 80, you can do this:
$host = '193.33.186.70';
$port = 80;
$waitTimeoutInSeconds = 1;
if($fp = fsockopen($host,$port,$errCode,$errStr,$waitTimeoutInSeconds)){
// It worked
} else {
// It didn't work
}
fclose($fp);
For anything other than TCP it will be more difficult (although since
you specify 80, I guess you are looking for an active HTTP server, so
TCP is what you want). TCP is sequenced and acknowledged, so you will
implicitly receive a returned packet when a connection is successfully
made. Most other transport protocols (commonly UDP, but others as
well) do not behave in this manner, and datagrams will not be
acknowledged unless the overlayed Application Layer protocol
implements it.
The fact that you are asking this question in this manner tells me you
have a fundamental gap in your knowledge on Transport Layer protocols.
You should read up on ICMP and TCP, as well as the OSI Model.
Also, here's a slightly cleaner version to ping to hosts.
// Function to check response time
function pingDomain($domain){
$starttime = microtime(true);
$file = fsockopen ($domain, 80, $errno, $errstr, 10);
$stoptime = microtime(true);
$status = 0;
if (!$file) $status = -1; // Site is down
else {
fclose($file);
$status = ($stoptime - $starttime) * 1000;
$status = floor($status);
}
return $status;
}
In case the OP really wanted an ICMP-Ping, there are some proposals within the User Contributed Notes to socket_create() [link], which use raw sockets. Be aware that on UNIX like systems root access is required.
Update: note that the usec argument has no function on windows. Minimum timeout is 1 second.
In any case, this is the code of the top voted ping function:
function ping($host, $timeout = 1) {
/* ICMP ping packet with a pre-calculated checksum */
$package = "\x08\x00\x7d\x4b\x00\x00\x00\x00PingHost";
$socket = socket_create(AF_INET, SOCK_RAW, 1);
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $timeout, 'usec' => 0));
socket_connect($socket, $host, null);
$ts = microtime(true);
socket_send($socket, $package, strLen($package), 0);
if (socket_read($socket, 255)) {
$result = microtime(true) - $ts;
} else {
$result = false;
}
socket_close($socket);
return $result;
}
Test different ports:
$wait = 1; // wait Timeout In Seconds
$host = 'example.com';
$ports = [
'http' => 80,
'https' => 443,
'ftp' => 21,
];
foreach ($ports as $key => $port) {
$fp = #fsockopen($host, $port, $errCode, $errStr, $wait);
echo "Ping $host:$port ($key) ==> ";
if ($fp) {
echo 'SUCCESS';
fclose($fp);
} else {
echo "ERROR: $errCode - $errStr";
}
echo PHP_EOL;
}
// Ping example.com:80 (http) ==> SUCCESS
// Ping example.com:443 (https) ==> SUCCESS
// Ping example.com:21 (ftp) ==> ERROR: 110 - Connection timed out
Try this :
echo exec('ping -n 1 -w 1 72.10.169.28');
function ping($ip){
$output = shell_exec("ping $ip");
var_dump($output);
}
ping('127.0.0.1');
UPDATE:
If you pass an hardcoded IP (like in this example and most of the real-case scenarios), this function can be enough.
But since some users seem to be very concerned about safety, please remind to never pass user generated inputs to the shell_exec function:
If the IP comes from an untrusted source, at least check it with a filter before using it.
You can use exec function
exec("ping ".$ip);
here an example
You don't need any exec or shell_exec hacks to do that, it is possible to do it in PHP. The book 'You want to do WHAT with PHP?' by Kevin Schroeder, show's how.
It uses sockets and the pack() function which lets you read and write binary protocols. What you need to do is to create an ICMP packet, which you can do by using the 'CCnnnA*' format to create your packet.
socket_create needs to be run as root on a UNIX system with;
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
If you want to send ICMP packets in php you can take a look at this Native-PHP ICMP ping implementation, but I didn't test it.
EDIT:
Maybe the site was hacked because it seems that the files got deleted, there is copy in archive.org but you can't download the tar ball file, there are no contact email only contact form, but this will not work at archive.org, we can only wait until the owner will notice that sit is down.

PHP Udp socket close not freeing system resources

I am using the following piece of code in a php script to process incoming data over http and forward it to another module and waits for the response. It then closes the socket.
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_bind($sock,$host,$port) or die("<?xml version=\"1.0\"?>");
if (!socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1)) {
echo socket_strerror(socket_last_error($sock));
exit;
}
$arrOpt = array('l_onoff' => 1, 'l_linger' => 1);
socket_set_block($sock);
socket_set_option($sock, SOL_SOCKET, SO_LINGER, $arrOpt);
$address = gethostbyname($host);
$msg = $url;
$len = strlen($msg);
socket_sendto($sock, $msg, $len, 0, $remotehost, $remoteport) ;
socket_recvfrom($sock, $buff, 1000, 0, $host, $newport);
socket_close($sock);
The problem faced is that the response is received correctly and the socket_close error is also returning a success(output of socket_last_error). But after that if I do a netstat I see the port being in used and the process (output of /proc/pid/status) is in sleep state.
This behavior is random in nature and I am using PHP version 5.3.8 on a Amazon EC2 cloud.
its about TIME_WAIT ... to free the socket
in Linux run # /proc/sys/net/ipv4/tcp_fin_timeout to find out .... (default 60 sec)
more about

Wake on lan script that works

is there a wake on lan script using a web language preferably php that works? Also one that has some documentation on how to get it to work like what needs to be enabled on your server etc
function wol($broadcast, $mac)
{
$hwaddr = pack('H*', preg_replace('/[^0-9a-fA-F]/', '', $mac));
// Create Magic Packet
$packet = sprintf(
'%s%s',
str_repeat(chr(255), 6),
str_repeat($hwaddr, 16)
);
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($sock !== false) {
$options = socket_set_option($sock, SOL_SOCKET, SO_BROADCAST, true);
if ($options !== false) {
socket_sendto($sock, $packet, strlen($packet), 0, $broadcast, 7);
socket_close($sock);
}
}
}
Should work - call it with a broadcast IP address, and a MAC address
I know this is an old questions, but it's still the first Google result, so here's what I ended up doing after a bit of research:
Prerequisites:
Linux box on the same network
Install the wakeonlan package from your system's package manager (i.e. sudo apt-get install wakeonlan)
Now the script is as easy as this:
<?php
# replace with your target MAC address
$mac = 'aa:bb:cc:11:22:33';
exec("wakeonlan $mac");
?>
Hope that helps someone.
HTML (test.htm)
<body>
Click to WOL XX:XX:XX:XX:XX:XX
</body>
PHP (test.php)
<?php
$mymac = $_REQUEST['mymac'];
wol("255.255.255.255", $mymac);
echo 'WOL sent to '.$mymac;
function wol($broadcast, $mac){
$mac_array = preg_split('#:#', $mac); //print_r($mac_array);
$hwaddr = '';
foreach($mac_array AS $octet){
$hwaddr .= chr(hexdec($octet));
}
//Magic Packet
$packet = '';
for ($i = 1; $i <= 6; $i++){
$packet .= chr(255);
}
for ($i = 1; $i <= 16; $i++){
$packet .= $hwaddr;
}
//set up socket
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($sock){
$options = socket_set_option($sock, 1, 6, true);
if ($options >=0){
$e = socket_sendto($sock, $packet, strlen($packet), 0, $broadcast, 7);
socket_close($sock);
}
}
} //end function wol
?>
Since the split() function was removed from PHP 7.0.0, this script uses preg_split() to be compatible with current and previous PHP versions.
Replace XX:XX:XX:XX:XX:XX in the HTML with your target MAC to test the script.
Building upon the previous answers. Had to set udp port to 9 and repeat the MAC a couple more times before it worked for me:
function wol($mac)
{
$hwaddr = pack('H*', preg_replace('/[^0-9a-fA-F]/', '', $mac));
// Create Magic Packet
$packet = sprintf(
'%s%s',
str_repeat(chr(255), 6),
str_repeat($hwaddr, 20)
);
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($sock !== false) {
$options = socket_set_option($sock, SOL_SOCKET, SO_BROADCAST, true);
if ($options !== false) {
socket_sendto($sock, $packet, strlen($packet), 0, "255.255.255.255", 9);
socket_close($sock);
}
}
}
Please read if you're running PHP in Docker, otherwise disregard this.
It seems that UDP broadcast from docker isn't being routed properly (possibly only broadcasted in the container itself, not on the host).
You may try setting (CLI) --network host or (compose) network_mode: host, but for PHP this was causing issues.
You can't send UDP WoL messages directly, as the device you're trying to control is 'offline' it doesn't show up in your router's ARP table and thus the direct message can't be delivered.
I ended up running #reboot root /usr/bin/php -S 0.0.0.0:8877 -t /home/user/php and putting the wol.php file there.
As a 'more proper' solution you may run an 'wol proxy' docker container that does exactly that (but then in a network host privileged docker container).

TCP server (php)

I have a gps simulator, which sends continuous location data (nmea strings) through tcpip to 192.168.0.178:2323. How do I get that data using tcp socket and write to DB (php & mysql)?
Thanks!
This is a loaded question. First you need to read the data. Then you have to put some structure to the data for it to be usable. It doesn't do you much good to just dump lat-lon to a database. You probably want to save it as a waypoint or part of a track etc.
So I have no answer to the DB question. Here is part of the PHP program I use to read GPS data off my phone connected to a CradlePoint router. It's modified from GoogleNav code.
function read_gps() {
set_time_limit(5);
$fp = fsockopen ("192.168.0.1", 8080, $errno, $errstr, 30);
if (!$fp) {
die ("$errstr ($errno)");
}
$point=false;
$status="";
$fix=0;
while (!$point) {
$string=#fgets($fp, 4096);
switch (substr($string,0,6)) {
case "\$GPRMC" :
list($sentence, $time, $status, $latitude, $NS, $longitude, $EW, $speed, $course, $date, $magvar, $magvarEW)= explode(",", trim($string));
$latd=convdec($latitude, $NS);
$lond=convdec($longitude, $EW);
break;
case "\$GPGGA" :
list($sentence, $time, $latitude, $NS, $longitude, $EW, $fix, $nbsat, $HDOP, $altitude,,,,,)= explode(",", trim($string));
$latd=convdec($latitude, $NS);
$lond=convdec($longitude, $EW);
break;
default :
break;
}
if ($status=="A" and $fix == 1){
$point=true;
}
}
...
?>
Start here: PHP Sockets
One of the user comments gives a helpful sample implementation of a TFTP client
<?php
function tftp_fetch($host, $filename)
{
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
// create the request packet
$packet = chr(0) . chr(1) . $filename . chr(0) . 'octet' . chr(0);
// UDP is connectionless, so we just send on it.
socket_sendto($socket, $packet, strlen($packet), 0x100, $host, 69);
$buffer = '';
$port = '';
$ret = '';
do
{
// $buffer and $port both come back with information for the ack
// 516 = 4 bytes for the header + 512 bytes of data
socket_recvfrom($socket, $buffer, 516, 0, $host, $port);
// add the block number from the data packet to the ack packet
$packet = chr(0) . chr(4) . substr($buffer, 2, 2);
// send ack
socket_sendto($socket, $packet, strlen($packet), 0, $host, $port);
// append the data to the return variable
// for large files this function should take a file handle as an arg
$ret .= substr($buffer, 4);
}
while(strlen($buffer) == 516); // the first non-full packet is the last.
return $ret;
}
?>

Categories