Sending sockets data with a leading length value - php

I want to send JSON messages from a PHP script to a C# app over a network connection using PHP Sockets.
Usually, for binary protocols, the first 4 bytes of every message must be an integer which represents the length (how many bytes) of the message.
In C# I prefix every message with an integer that tells the length of the message as follow:
byte[] msgBytes = UTF8Encoding.UTF8.GetBytes("A JSON msg");
byte[] prefixBytes = BitConverter.GetBytes(msgBytes.Length);
byte[] msgToSend = new byte[prefixBytes.Length + msgBytes.Length];
Buffer.BlockCopy(prefixBytes, 0, msgToSend, 0, prefixBytes.Length);
Buffer.BlockCopy(msgBytes, 0, msgToSend, prefixBytes.Length, msgBytes.Length);
As I understand, in PHP the function socket_send only accept strings. So, how can I do the same prefixing in PHP 5.x?
Update: I posted a follow-up question on how to process such prefixed data when received from a network socket.

In PHP strings are binary.
So you need to encode the integer length value as the binary representation of an unsigned integer as a 4-char (4 Octets; 32 bits) string. See pack:
# choose the right format according to your byte-order needs:
l signed long (always 32 bit, machine byte order)
L unsigned long (always 32 bit, machine byte order)
N unsigned long (always 32 bit, big endian byte order)
V unsigned long (always 32 bit, little endian byte order)
$string = pack('l', $length);

I guess you could use pack() to convert the number of bytes to a binary string. As you send your data over the network, you probably need to convert using the format "N" (unsigned long, always 32 bit, big endian byte order).
Here's an example:
$s="Hello World";
$length=pack("N",strlen($s));
socket_send($sock,$length.$s,4+strlen($s));

Related

Pack Convert byte array into UINT64

I want to convert byte array to UINT64 using PHP.
I can do this easily in C# but I want to do this in PHP.
Here is C# code.
bytes = Encoding.UTF8.GetBytes(hashed);
BitConverter.ToUInt64(bytes, 0);
I want to convert this to PHP.
I tried to use pack() function but this does not works.
Let's say this is a byte array.
$bytes = [101,102,54,55,99,55,56,49];
pack("J*","101","102","54","55","99","55","56","49");
This shows a warning.
epack(): 7 arguments unused on line
How can I fix this?
The major issue here (if I understand it correctly) is you're using PHP numbers to represent a byte array however unpack requires an input string. If you keep the array as is then PHP seems to just convert the numbers to strings meaning a 101 will be '101' which in turn is 3 bytes, which breaks the whole thing down.
You need to first convert the numbers to bytes. A byte is essentially as an unsigned char so you could first pack your array into unsigned chars and then unpack them:
$bytes = [101,102,54,55,99,55,56,49];
$unpacked = unpack("J", pack('C*', ...$bytes));
echo current($unpacked);
Explanation:
C is the pack code for unsigned char and * indicates that you need to use all array entries. This will generate a string of characters based on the array. You can then unpack this string using J (if you know for a fact that the bytes were generated in a big endian byte order) or P if you know the bytes were generated in little endian order or Q if you want to use the machine order. If the bytes were generated in the same machine then Q would probably be a better choice than J otherwise you need to know the endianess.
Example: http://sandbox.onlinephpfunctions.com/code/5cba2c29522f7b9f9a0748b99fac768012e759ce
Note: This is my personal understanding of what is happening so anyone with better pack/unpack knowledge can let me know if I got things wrong.

Exceed PHP binary limit

I was trying to do some sort of XOR binary crypting algorithm in PHP and so I needed to convert large strings into binary. The problem is that PHP seems to be very limited in terms of binary calculation / storage as a string of six letters only, once converted, exceeds the PHP INT limit.
That means unpacking a big string to binary just gives a unusable number. I tried to do the string unpacking by splitting the string into packs of 4 letters and then unpacking them, but then I've got troubles with the repacking where it gives random characters instead of the original ones.
How can I do the unpacking of very long strings, and then store them either in a string (made only of 0s and 1s) or in a big array (where each value is either a 0 or 1, the key indicating the location of this bit) ?
have you tried the GMP library? Man page GMP
quick test code:
<?php
$gmpValue1 = gmp_init("1562767628166296698262", 10); // note: using base 10 (decimal)
$gmpValue2 = gmp_init("2163623626362663286446", 10);
$gmpValue3 = gmp_xor($gmpValue1, $gmpValue2);
echo gmp_strval($gmpValue3, 10) . "\n"; // note: using base 10 (decimal)

PHP equivalent to Python's struct format

I am trying to write Python code to PHP. But I couldn't PHP pack format equivalent to Python pack format
Python:
format = "!LLLLQ"
mystr = struct.unpack(format, str)
I am using like this
$mystr = unpack("!LLLLQ", $str); // But its not working
Because there is no ! or Q in PHP pack.
Is there anyway to achieve that?
Format codes for Python: https://docs.python.org/2/library/struct.html
Format codes for PHP: http://www.php.net/manual/en/function.pack.php
Thanks!
The following sequence:
!LLLL
represents 4 unsigned longs (32 bit) in big endian format. In PHP this would be:
NNNN
However, the Q at the end is problematic. It represents an unsigned long long (64 bit). PHP's pack function doesn't offer unsigned long longs ( Correct me if I'm wrong).
If you unpack the 8-byte unsigned long long using two 4-byte unsigned longs (NN format in PHP), then you can bit-shift the first long 32 bits and add that to the second long to recover the 8-byte unsigned long long. In Python,
In [49]: import struct
In [50]: c = 12345678912345678912
In [51]: struct.pack('!Q', c) # this is the packed string
Out[51]: '\xabT\xa9\x8f\x81e$#'
# unpack the string using `NN` in PHP
In [52]: a, b = struct.unpack('!LL', struct.pack('!Q', c))
In [53]: a, b
Out[53]: (2874452367L, 2170889280L)
This bit-shifts the first number by 32-bits then adds it to the second number.
In [54]: (a<<32)+b
Out[54]: 12345678912345678912L
And this shows that the original number c is recovered:
In [55]: assert (a<<32)+b == c

Same algorithm , different result

Good day, I am making my hashing algorthm, so I am rewriting it to C++ from PHP.
But result in C++ is different than php result. PHP result contains more than 10 characters, C++ result only 6 - 8 characters. But those last 8 characters of PHP result are same as C++ result.
So here is PHP code:
<?php function JL1($text) {
$text.="XQ";
$length=strlen($text);
$hash=0;
for($j=0;$j<$length;$j++) {
$p=$text[$j];
$s=ord($p);
if($s%2==0) $s+=9999;
$hash+=$s*($j+1)*0x40ACEF*0xFF;
}
$hash+=33*0x40ACEF*0xFF;
$hash=sprintf("%x",$hash);
return $hash; } ?>
And here C++ code:
char * JL1(char * str){
int size=(strlen(str)+3),s=0; //Edit here (+2 replaced with +3)
if(size<=6) //Edit here (<9 replaced with <=6)
size=9;
char *final=new char[size],temp;
strcpy(final,str);
strcat(final,"XQ");
long length=strlen(final),hash=0L;
for(int i=0;i<length;i++){
temp=final[i];
s=(int)temp;
if(s%2==0)s+=9999;
hash+=((s)*(i+1)*(0x40ACEF)*(0xFF));
}
hash+=33*(0x40ACEF)*(0xFF);
sprintf(final,"%x",hash); //to hex string
final[8]='\0';
return final; }
Example of C++ result for word: "Hi!" : 053c81be
And PHP result for this word: 324c053c81be
Does anyone know,where is that mistake and how to fix that, whether in php or in cpp code?
By the way, when I cut those first letters in php result I get C++ result, but it wont help, because C++ result have not to be 8 characters long, it can be just 6 characters long in some cases.
Where to begin...
Data types do not have fixed guaranteed sizes in C or C++. As such, hash may overflow every iteration, or it may never do so.
chars can be either signed or unsigned, therefore converting one to an integer may result in negative and positive values on different implementations, for the same character.
You may be writing past the end of final when printing the value of hash into it. You may also be cutting the string off prematurely when setting the 9th character to 0.
strcat will write past the end of final if str is at least 7 characters long.
s, a relatively short-lived temporary variable, is declared way too soon. Same with temp.
Your code looks very crowded with almost no whitespace, and is very hard to read.
The expression "33*(0x40ACEF)*(0xFF)" overflows; did you mean 0x4DF48431L?
Consider using std::string instead of char arrays when dealing with strings in C++.
long hash in C++ is most likely limited to 32 bits on your platform. PHP's number isn't.
sprintf(final, "%x", hash) produces a possibly incorrect result. %x interprets the argument as an unsigned int, which is 32 bits on both Windows and Linux x64. So it's interpreting a long as an unsigned int, if your long is more than 32 bits, your result will get truncated.
See all the issues raised by aib. Especially the premature termination of the result.
You will need to deal with the 3rd point yourself, but I can answer the first two. You need to clamp the result to 32 bits: $hash &= 0xFFFFFFFF;.
If you clamp the final value, the php code will produce the same results as the C++ code would on x64 Linux (that means 64 bit integers for intermediate results).
If you clamp it after every computation, you should get the same results as the C++ code would on 32 bit platforms or Windows x64 (32 bit integers for intermediate results).
There seems to be a bug here...
int size=(strlen(str)+2),s=0;
if(size<9)
size=9;
char *final=new char[size],temp;
strcpy(final,str);
strcat(final,"XQ");
If strlen was say 10, then size will be 12 and 12 chars will be allocated.
You then copy in the original 10 characters, and add XQ, but the final terminating \0 will be outside of the allocated memory.
Not sure if that's your bug or not but it doesn;t look right

How can you parse a Java 64-bit long from a binary file into a PHP string?

I've used unpack to convert most of the data types I have in a binary file that I'm parsing with little problems. I have no idea how to work with a big endian 64-bit signed long. I think this data type is stored using 2's complement. The application of the data file I'm reading is a java app so I assume it's 2's complement. I don't need to work with it as a number but simply work with it as a string.
Java 64-bit integers are indeed stored natively as "network-order" (big endian, i.e. start with the most significant byte) 8-byte 2's complement format. So typically you take byte at a time, shift left by 8, repeat. Byte values can be thought of as unsigned (while result is signed), but with left-shifting this should not matter.
So: first you just created equivalent 64-bit int from bytes, and display from there. No point in using short cuts; while it is possible, you just end up with more complicated and less efficient code.
32-bit PHP will only have signed 32-bit integers, thus as far as I know, there's no way to natively unpack the data.
The following code should be able to read a big endian, two's complement 64-bit integer:
<?php
function read_int64($fp)
{
$hex = unpack('H16a', fread($fp, 8));
$hex = '0x'.$hex['a'];
$n = gmp_init($hex);
if (gmp_testbit($n, 63))
{
$n = gmp_xor($n, '0xffffffffffffffff'); // flip the bits
$n = gmp_neg(gmp_add($n, 1)); // add one and negate
}
return gmp_strval($n);
}
?>
It returns the integer as a string. It can be used like:
$fp = fopen('test.bin', 'rb');
echo read_int64($fp)."\n";
fclose($fp);
(Edit: Updated code to call fewer GMP functions.)

Categories