receive multicasts with PHP7 on Raspberry - php

I send multicast via PHP (v7.0.8-5) this looking good so far.
My Problem is i am not able to receive the pakets.
CODE IS GOOD, working without Firewall
this is the code.
<?php
error_reporting(E_ALL | E_STRICT);
$mesg = "123456789012" ; //the msg i want to send
$ip = "228.5.6.7"; //the ip to send
$port = 14446 ; //the port to send
//build the socket
$grpparms = array("group"=>$ip,"interface"=>"eth0") ;
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_set_option($sock,IPPROTO_IP,MCAST_JOIN_GROUP,$grpparms);
if($argv[1] == 'r'){ //read multicasts
$from = '';
$dest_port = 0;
$binded = socket_bind($sock, '0.0.0.0',$port);
socket_recvfrom($sock, $buf, 12, 0, $from, $dest_port);
echo "Received $buf from remote address $from and remote port $dest_port" . PHP_EOL;
}
if ($argv[1] =='w'){ //write multicasts
socket_sendto($sock, $mesg, strlen($mesg), 0, $ip,$port);
echo "Send '$mesg' to $ip at port $port".PHP_EOL;
socket_close($sock) ;
}
?>
I call it with the paramter 'r' to read/receive and with 'w' to send/write multicasts.
I start this script over ssh on two different raspberrys (Vers. 2b) on the same switch. One with 'r' and the other with 'w'.

I can't test it myself right now, but change $dest_port in socket_recvfrom() to $port or change the value of $dest_port from 0 to 14446 and try again please.

Related

PHP Socket timeout won't go below 0.5 seconds

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.

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).

How to send datagrams through a unix socket from PHP?

I'm doing:
$socket = socket_create(AF_UNIX, SOCK_DGRAM, 0);
if (#socket_connect($socket, $path) === false) { ... }
But I get this error:
(91): Protocol wrong type for socket
Am I using any of the parameters wrong? I suspect from the second socket_create parameter. I could't find any help in the documentation: http://php.net/manual/es/function.socket-create.php
It's maby outdated, but I've found that this way it works properly:
$sock = stream_socket_client('unix:///tmp/test.sock', $errno, $errst);
fwrite($sock, 'message');
$resp = fread($sock, 4096);
fclose($sock);
For Unix sockets we don't need to use socket_connect.
Here is a very simple working example with a sender and a receiver:
sender.php
<?php
$socket = socket_create(AF_UNIX, SOCK_DGRAM, 0);
socket_sendto($socket, "Hello World!", 12, 0, "/tmp/myserver.sock", 0);
echo "sent\n";
?>
receiver.php
<?php
$file = "/tmp/myserver.sock";
unlink($file);
$socket = socket_create(AF_UNIX, SOCK_DGRAM, 0);
if (socket_bind($socket, $file) === false) {
echo "bind failed";
}
if (socket_recvfrom($socket, $buf, 64 * 1024, 0, $source) === false) {
echo "recv_from failed";
}
echo "received: " . $buf . "\n";
?>
Note that only the receiver needs to bind to an address (the unix socket file) and then use socket_recvfrom. The sender just calls socket_sendto.
Have you tried using getprotobyname() for the 3rd (protocol) parameter?
The third argument to socket_create is incorrect hence the error message.
It should be socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
The value 0 that you specified corresponds to SOL_IP which is not a supported protocol in php.

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