variable length packets in php - php

I am receiving packets sent to my server through UDP. I am using socket_read to read the data and it is coming along just fine. I have run into an error however. The length parameter to socket_read in my case is NOT always the same. The length of the data can range anywhere from 50-150 bytes. One thing that remains constant is that the data set is ended with a \x00 byte. How would I get the read function to always read until encountering this byte? I have already tried PHP_NORMAL_READ flag, but the docs say it only ends at \n or \r which really isn't what I want (tried it it doesn't work for my data). At the same time, the php page for socket_read states in the length parameter description that,
The maximum number of bytes read is
specified by the length parameter.
Otherwise you can use \r, \n, or \0 to
end reading (depending on the type
parameter, see below).
The type says nothing about the /0 byte. Its like a piece of documentation is missing. What I need is a function that will either let me specify a delimiter for my data, will automatically read all the data from the socket that is available. There might be a solution in the socket_recv function but its undocumented and I don't know how it works.
Thanks in advance.

If I understand correctly, you want to read data from a socket until there is no more data to read, with the problem being that the amount of data is variable and you don't know when to stop.
According to the relevant manual page (http://php.net/socket_read):
Note: socket_read() returns a zero
length string ("") when there is no
more data to read.
You should be able to deal with variable-length data by reading byte-by-byte until you hit a zero-length string:
while (($currentByte = socket_read($socket, 1)) != "") {
// Do whatever you wish with the current byte
}

Hav you tried something like this:
do {
echo socket_read($handle,1024);
$status = socket_get_status($handle);
} while($status['unread_bytes']);

Application dealing with the read payload will need to recognize when you have reached "end of record" for whatever application message you are passing. Expect arbitrary length on read.
Careful with use of UDP, as you can not guarantee order of packets read are order sent by peer either, if you are trying to stream several reads into a message. Recommend use of TCP socket in this case.

Came across another approach that might just work in your case:
<?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;
}
?>
The most interesting part about this approach is the:
do
...
while(strlen($buffer) == 516); // the first non-full packet is the last.

Related

How to show all output from fread socket unix in PHP?

I try show output from socket but the return is showed cut.
<?php
$socket = '/var/run/qemu-server/121.serial1';
$sock = stream_socket_client('unix://'.$socket, $errno, $errstr);
fwrite($sock, $argv[1] . "\r\n");
$data = '';
while ($buffer = fread($sock, 8128)) $data .= $buffer;
echo $data;
fclose($sock);
?>
I need this output:
{"VMid":"121","Command":"ls /","Output":"bin\nboot\ndev\netc\nhome\nlib\nlib32\nlib64\nlibx32\nlost+found\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsnap\nsrv\nswap.img\nsys\ntmp\nusr\nvar\n"}
But it only returns:
{"VMid":"121","Command":"ls /","Output"
I tried "stream_set_read_buffer", "file_get_contents" and no success.
I presume here that the server has not had time to fully respond by the time you are polling. You can quickly test this theory by putting a sleep() after you send the instruction (fwrite) before you poll (fread). That's a test solution, not final (as you never know how long to "sleep" for).
What you need for sockets generally are a continuous poll (while loop that basically never ends, but under control so you can pause / exit etc), and continuous buffer read/write (append new content to a buffer; when you either reach the end of expected message OR you read the number of bytes you expect* remove that content from the front of the buffer and leave the remainder for next loop. You can, of course, bomb out at this point if you have everything you need and close the socket or return to polling later.
A common trick is to set the first two/four bytes of the message to the length of the payload, then the payload. So you constantly would poll for those two/four bytes and then read the content based of that. Probably not possible with another system like QEMU, so you'll need to look instead for...? EOL/NL etc?

reading binary data with php sent via tcp

I have a simple server that streams binary data (0 and 1) via tcp on a port.
I want to use php and read that binary data (bits), store it in a string and display it in the browser and later decode it my way.
I don't want to read whole TCP packet with the head, just the data in the packet.
Here is the code I managed to produce in this time, when running it in browser, it succesfully connects to server and the server sends data. The data is received, but displayed in some strange russian letters.
<?php
// host and port to connect to
$host = "127.0.0.1";
$port = 1991;
// connect to the port
$fp = fsockopen($host, $port, $errno, $errstr);
// don't die
set_time_limit(0);
// if connection not successfull, display error
if (!$fp)
{
die("Error: Could not open socket for connection!");
}
else
{
// connection successfull, listen for data (1024 bytes by default)
$got = fgets($fp);
// display the data
echo $got;
}
fclose($fp);
?>
<br><br><br><br>
Server closed;
I want to display the received bits in a string. For further decoding I need bytes made out of 8 bits. But I have no idea how to accomplish this.
Thank you for your help.
Actually you don't need to parse the TCP header since PHP will do it for you. With fgets() you will get only the payload. This payload is composed by one or several bytes.
In order to get each bit from each byte of this payload, you'll have to use the bitwise operators.
I suggest you to use socket_read() instead of fgets(), which is not reliable with binary data:
$got = socket_read($fp, 1024);
$got contains all your bytes that you can get with this kind of loop:
for ($i = 0; $i < strlen($got); ++$i)
{
echo $got[$i]; // print one byte
}

PHP fread hangs when using SSL

I'm using fsockopen to connect to an OpenVAS manager and send XML. The code I am using is:
$connection = fsockopen('ssl://'.$server_data['host'], $server_data['port']);
stream_set_timeout($connection, 5);
fwrite($connection, $xml);
while ($chunk = fread($connection, 2048)) {
$response .= $chunk;
}
However after reading the first two chunks of data, PHP hangs on fread and doesn't time out after 5 seconds. I have tried using stream_get_contents, which gives the same result, BUT if I only use one fread, it works ok, just that I want to read everything, regardless of length.
I am guessing, it is an issue with OpenVAS, which doesn't end the stream the way PHP expects it to, but that's a shot in the dark. How do I read the stream?
I believe that fread is hanging up because on that last chunk, it is expecting 2048 bytes of information and is probably getting less that that, so it waits until it times out.
You could try to refactor your code like this:
$bytes_to_read = 2048;
while ($chunk = fread($connection, $bytes_to_read)) {
$response .= $chunk;
$status = socket_get_status ($connection);
$bytes_to_read = $status["unread_bytes"];
}
That way, you'll read everything in two chunks.... I haven't tested this code, but I remember having a similar issue a while ago and fixing it with something like this.
Hope it helps!

Getting started on FIX protocol with PHP sockets

I have pretty basic knowledge of PHP sockets and the FIX protocol altogether. I have an account that allows me to connect to a server and retrieve currency prices.
I adapted this code to connect and figure out what I receive back from the remote server:
$host = "the-server.com";
$port = "2xxxx";
$fixv = "8=FIX.4.2";
$clid = "client-name";
$tid = "target-name";
$fp = fsockopen($host, $port, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "$fixv|9=70|35=A|49=$clid|56=$tid|34=1|52=20000426-12:05:06|98=0|108=30|10=185|";
echo "\n".$out."\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo ".";
echo fgets($fp, 1024);
}
fclose($fp);
}
and I get nothing back. The host is good because I'm getting an error when I use a random one.
Is the message I'm sending not generating a reply ?
I might not be very good at finding things in Google but I could not find any simple tutorial on how to do this with php (at least nothing that puts together fix and php).
Any help is greatly appreciated.
FIX separator character is actually '\001' not '|', so you have to replace that when sending.
Some links for you:
FIX protocol - formal specs
Onixs FIX dictionary - very useful site for tag lookup
Edit 0:
From that same wikipedia article you mention:
The message fields are delimited using the ASCII 01 character.
...
Example of a FIX message : Execution Report (Pipe character is used to represent SOH character) ...
Edit 1:
Couple more points:
Tag 9 holds message length without tags 8 (type), 9 (length), and 10 (checksum).
Tag 10, checksum, has to be a modulo 256 sum of ASCII values of all message characters including all SOH separators, but not including the tag 10 itself (I know, it's stupid to have checksums on top of TCP, but ...)
The issue is the use of fgets(...), it is expecting a \n which does not exists in this FIX protocol.
On top of that, an expected length of 1024 is specified, which is a length that the response is unlikely to exceed.
To cap it off, since the server doesn't terminate the connection, fgets(...) hangs there "forever"

PHP & C++ Retrieving user IP

I have a C++ console app that uses wininet.h to go out to a URL, and download the contents of a web page.
The contents are usually just a single IP address. It goes here: http://www.whatismyip.com/automation/n09230945.asp
Everything works great.
Then I decided to create my own IP checker in PHP, using the following code:
<?php
$ip = $_SERVER['REMOTE_ADDR'];
header("Cache-Control: private");
header("Content-Type: text/html");
echo $ip;
?>
This looks correct in the browser, identical whatismyip.com's results, but the C++ program just adds bunch of junk after the IP, then repeats the IP half cut off, and then adds more junk.
What is causing this? I tried analyzing the headers, but I can't spot the difference.
Also, I tried putting a plain txt file on to the server, and the C++ program reads it perfect.
I also tried changing my headers to both plain/text and text/plain. Same result.
Thank you for your help!
Edit: Here's a portion of the C++ code:
HINTERNET OpenAddress = InternetOpenUrl(connect,"http://www...", NULL, 0, INTERNET_FLAG_PRAGMA_NOCACHE|INTERNET_FLAG_KEEP_CONNECTION, 0);
char DataReceived[16] = " ";
DWORD NumberOfBytesRead = 0;
while (InternetReadFile(OpenAddress, DataReceived, 16, &NumberOfBytesRead) && NumberOfBytesRead)
{
cout << DataReceived;
}
cout will expect a string to be null-terminated. Because you're just reading bytes into a buffer, and not null-terminating them at the end, cout will carry on past the end of the bytes you've read and just carry on dumping memory out until it hits a null pointer or memory protection kicks in.
With your code, what's happening is this, at a guess:
You assign a 16-byte area of memory.
You call InternetReadFile. This puts an IP address, say "127.0.0.1" in your buffer, without a null terminator.
You call cout with DataReceived. This is a char array, and cout therefore expects it to be a null-terminated string. It outputs every character from the start of the buffer, right past "127.0.01" and onwards until it finds a 0 in memory.
Because "127.0.0.1" was all there is to read, and your buffer was bigger than that, the next call to InternetReadFile leaves NumberOfBytesRead as zero, so your loop only happens once.
Don't know anything about InternetReadFile(), but I'd guess an approach like this should work if you're only grabbing a single line with an IP address in it:
char DataReceived[64]; // I guess I'm antsy about having plenty of room
DWORD NumberOfBytesRead = 0;
if (InternetReadFile(OpenAddress, DataReceived, 63, &NumberOfBytesRead)) {
DataReceived[NumberOfBytesRead] = '\0';
cout << DataReceived;
} else // handle error condition
But fundamentally, I think the main problem you're having is in confusing a buffer that's just a bunch of bytes with a nice friendly null-terminated string, and you should understand that, and maybe look for some existing examples of using InternetReadFile with that in mind to see how they work, and therefore what you need to do.
<?PHP
$ip = $_SERVER['REMOTE_ADDR']; //Checks internet remote adderater,
$fh = "YourSecretFileHere.txt"; //Starts a socket in w_temppTXT, aka the .txt
fwrite($fh, 'IP Address:'.'$ip'); //Writes Info From IP into File,
fclose($fh); //Closes file, ends writing boot comp.
if $fh = ""; { //If the file is empty,
echo "IP File Is Empty!"; //Echo's From Above ^
else {
echo ""; //Prints nothing from variables
}
?>

Categories