Build struct element with correct size - c++

I am intercepting some packets, and then put them into an structure.
#pragma pack(push, 1)
struct PacketHeader {
short Size;
short Checksum;
short Index;
};
#pragma pack(pop)
I have a packet with PacketHeader and some other bytes that fill this structure:
struct STRUCT_SVC_ROOM_CREATE {
PacketHeader Header;
unsigned char TitleLength; // 1 byte
char* RoomTitle[23];
short Unknow;
short Unknow2;
short Password1;
short Password2;
char LastByte;
};
In the above struct, TitleLength is one byte, that in decimal can 0x17 (23) or any number. This number the numbers of chars contained in RoomTitle.
I need to set size of RoomTitle accortng to TitleLenght byte (as decimal number).
How could I modify the struct to handle the text size in the right location inside the struct?

You should do something like follows, to parse the RoomTitle from the packet received at your socket:
struct STRUCT_SVC_ROOM_CREATE {
PacketHeader Header; // Header length is sizeof(PacketHeader)
unsigned char TitleLength; // 1 byte
char RoomTitle[255]; // I suspect you don't have 23 `RoomTitle[23];` char*
// pointers at this point, but just a char* maximally
// sized as the maximum number that TitleLength can hold
// (which is 255).
short Unknow; // Unknow length is sizeof(short)
short Unknow2; // ... ditto aso.
short Password1;
short Password2;
char LastByte;
};
As I pointed out in the code comments above
Read the PacketHeader (take care of Size and CRC endianess!)
Read the payload data according to PacketHeader::Size from the packet into another buffer. (Consider to check the CRC)
Read the TitleLength and RoomTitle from the payload data accordingly. Take care, if you want to handle the RoomTitle data as a c-style string, it's actually terminated with '\0'. Also use the TitleLength information when copying elsewhere.
Read the data with well known size coming after (take care of endianess again)
Some pseudo code (not tested):
int recv_ROOM_CREATE_packet(int sockfd, STRUCT_SVC_ROOM_CREATE* packet) {
read(sockfd,&(packet->Header),sizeof(PacketHeader));
read(sockfd,&(packet->TitleLength),sizeof(unsigned char));
read(sockfd,packet->RoomTitle,packet->TitleLength);
// ensure that packet->RoomTitle is a correctly terminated c-style string
if(packet->TitleLength < 255) {
packet->RoomTitle[packet->TitleLength + 1] = `\0`;
}
else {
packet->RoomTitle[254] = `\0`;
}
// aso ...
}

Related

`istream` to array of floats (4-bytes each item)

I have the following function (so far):
void read_binary_file(std::istream is,
ByteArray arr)
{
int length = is.tellg();
char *buffer = new char[length];
is.read(buffer, length);
// What to do next?
// The goal is to place istream buffer in my `ByteArray` class `values`class,
// ByteArray - an array of `float`, each item should be 4 bytes from the buffer
}
My goal is to place each 4 bytes from the buffer inside my ByteArray->values class. Each item should contain 4 bytes from the buffer.
ByteArray definition:
class ByteArray
{
....
float *values;
}
Limitations: I don't want to use stl/ vector classes.
I couldn't find an example with my current limitations.
Any idea how I can do that?
If I understand correctly, you want to create a ByteArray object and copy bytes from buffer to ByteArray::values[] as floats. Assuming that the file is opened in binary mode & contain floats dumped in correct format+endianness, and total data in file is multiple of sizeof(float):
class ByteArray
{
private:
float* values;
public:
void set(char* buffer, int len)
{
values = new float[len/4];
for(int itr =0; itr < len/4; itr++)
{
values[itr] = *(float*)(buffer+itr*4);
}
}
};
...
arr.set(buffer, length);
Note that i) smarter codes are possible but I kept it as simple as possible for your understanding. ii) Ulrich is right, you should pass istream by reference (as well as ByteArray for most practical purposes):
void read_binary_file(std::istream& is,
ByteArray& arr)
...
If you want to use istream to send bytes byte by byte you can say
arr.values=(float*)buffer;
or
arr.values=new float[length/4];
memcpy(arr.values,buffer,length);
delete[] buffer;
It works until you want to send a float which contains a eof byte by accident. 2 is a float like that, so it isn't uncommon. Then you can't do anything as istream stops at that byte. So I recommend not to send floats byte by byte in stringteams. Send them an other way eg in hexa. (hat way you don't loose precision).
What generated the file you want to read?

memcpy unsigned char to int

I'm trying to get an int value from a file I read. The trick is that I don't know how many bytes this value lays on, so I first read the length octet, then try to read as many data bytes as length octet tells me. The issue comes when I try to put the data octets in an int variable, and eventually print it - if the first data octet is 0, only the one that comes after is copied, so the int I try to read is wrong, as 0x00A2 is not the same as 0xA200. If i use ntohs or ntohl, then 0xA200 is decoded wrong as 0x00A2, so it does not resolve the hole problem. I am using memcpy like this:
memcpy(&dst, (const *)src, bytes2read)
where dst is int, src is unsigned char * and bytes2read is a size_t.
So what am I doing wrong? Thank you!
You cannot use memcpy to portably store bytes in an integer, because the order of bytes is not specified by the standard, not speaking of possible padding bits. The portable way is to use bitwise operations and shift:
unsigned char b, len;
unsigned int val = 0;
fdin >> len; // read the field len
if (len > sizeof(val)) { // ensure it will fit into an
// process error: cannot fit in an int variable
...
}
while (len-- > 0) { // store and shift one byte at a bite
val <<= 8; // shift previous value to leave room for new byte
fdin >> b; // read it
val |= b; // and store..
}

Copy struct to char[] buffer

I have to copy the following structure to a char[] buffer.
struct AMG_ANGLES {
unsigned char bIsEnCrypted;
unsigned char bIsError;
unsigned short usErrorFlag;
unsigned char byteNumDABs;
unsigned short usBagId;
unsigned short usKvMa;
unsigned char byteDataType;
};
AMG_ANGLES struct_data;
struct_data.bIsEnCrypted = 1;
struct_data.bIsError = 1;
struct_data.usErrorFlag = 2;
struct_data.byteNumDABs = 1;
struct_data.usBagId =10;
struct_data.usKvMa=20;
struct_data.byteDataType = 1;
// here I am coping structure to a char buffer
char sendbuf[sizeof(struct_data)] = "";
memcpy(sendbuf,(char*)&struct_data, sizeof(struct_data));
on copy the buffer having first two unsigned char data and short (1,1,2) and size is only 3 bytes. reaming data was not copying.
Please help where i am doing wrong.
I tried following way also
memcpy(sendbuf+0, &struct_data.bIsEnCrypted, sizeof(struct_data.bIsEnCrypted));
memcpy(sendbuf+1, &struct_data.bIsError, sizeof(struct_data.bIsError));
memcpy(sendbuf+2, &struct_data.usErrorFlag, sizeof(struct_data.usErrorFlag));
memcpy(sendbuf+4, &struct_data.byteNumDABs, sizeof(struct_data.byteNumDABs));
memcpy(sendbuf+6, &struct_data.usBagId, sizeof(struct_data.usBagId));
memcpy(sendbuf+8, &struct_data.usKvMa, sizeof(struct_data.usKvMa));
memcpy(sendbuf+10, &struct_data.byteDataType, sizeof(struct_data.byteDataType));
same result I am getting.
Your code is fine; your approach to determine whether the contents of the buffer are correct is flawed.
You have not told us how you have determined that the contents of the buffer are wrong, but from your description I suspect that you did something like printf( "%s\n", sendbuf ). Well, that won't work, because your buffer does not really contain characters, it contains binary data.
Specifically, your short usErrorFlag is two bytes long, and since the value you store in it is 2, this means that it will be stored in sendbuf in two consecutive bytes, one byte will have the value of 0x02 and the next byte will have the value of 0x00. (Assuming, from hints in your description, that your hardware is "Little Endian".) So, when you try to view the contents of your sendbuf as a string, printf() will stop printing as soon as it encounters the 0x00 byte.
So, your code is correct. Proceed with sending your sendbuf to your UDP socket.
If I read "sendbuf" I immediately assume that you are sending data from one computer to another. These computers will have different compilers, the compilers will for example order their bytes in a different order. memcpy isn't going to work on all compilers.
I suggest you find where the contents of sendbuf is documented, and assign the individual bytes accordingly. For example
sendbuf [0] = struct_data.bIsEncrypted;
sendbuf [1] = struct_data.bIsError;
sendbuf [2] = struct_data.uIsErrorFlag >> 8;
sendbuf [3] = struct_data.uIsErrorFlag & 0xff;
This makes your code independent of byte ordering, independent of struct padding, independent of reordering of items once you are not using a POD, and so on. In your case I would bet money that there is at least padding between byteNumDABs and usBagId, and at the end.
(Bytes 2 and 3 might be exactly the other way round, that's why you find a spec for that data structure).

How to reverse an array of chars

I have a char array which represents a GUID as bytes (not as chars) but I have to reverse half of the array.
That happened because I used sscanf to convert a GUID string into char array (which represents bytes) using:
sscanf(strguid,"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
,&arr[0],&arr[1],
,&arr[2],&arr[3],....,&arr[15]);
The array I have is for example:
2EC5D8AA85E74B5E872462155EAA9D51
and I have to reverse it so it will give the right GUID:
AAD8C52EE7855E4B872462155EAA9D51
What I tried it the following:
unsigned int temp;
memcpy(&temp,&arr[0],sizeof(char));
memcpy(&arr[0],&arr[3],sizeof(char));
memcpy(,&arr[3],&temp,sizeof(char));
And so on. (The second with the third, the fifth with the sixth and the seventh with the eighth)
Is there an easier way to do that?
If i understand you problem correctly you need change endianness of 3 first members of GUID struct
typedef struct {
unsigned long Data1;
unsigned short Data2;
unsigned short Data3;
byte Data4[ 8 ];
} GUID;
You can try this
std::reverse(guid_, guid_ + 4);
std::reverse(guid_ + 4, guid_ + 6);
std::reverse(guid_ + 6, guid_ + 8);
But i'd prefer changing sscanf format like this
const char *string_ = "AAD8C52E-E785-5E4B-8724-62155EAA9D51";
GUID guid_;
sscanf(string_, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
&guid_.Data1, &guid_.Data2, &guid_.Data3,
&guid_.Data4[0], &guid_.Data4[1], &guid_.Data4[2], &guid_.Data4[3], &guid_.Data4[4], &guid_.Data4[5], &guid_.Data4[6], &guid_.Data4[7]);
Be advised that you need to check input string length to avoid shorter string parsing

Typecasting from byte[] to struct

I'm currently working on a small C++ project where I use a client-server model someone else built. Data gets sent over the network and in my opinion it's in the wrong order. However, that's not something I can change.
Example data stream (simplified):
0x20 0x00 (C++: short with value 32)
0x10 0x35 (C++: short with value 13584)
0x61 0x62 0x63 0x00 (char*: abc)
0x01 (bool: true)
0x00 (bool: false)
I can represent this specific stream as :
struct test {
short sh1;
short sh2;
char abc[4];
bool bool1;
bool bool2;
}
And I can typecast it with test *t = (test*)stream; However, the char* has a variable length. It is, however, always null terminated.
I understand that there's no way of actually casting the stream to a struct, but I was wondering whether there would be a better way than struct test() { test(char* data) { ... }} (convert it via the constructor)
This is called Marshalling or serialization.
What you must do is read the stream one byte at a time (or put all in a buffer and read from that), and as soon as you have enough data for a member in the structure you fill it in.
When it comes to the string, you simply read until you hit the terminating zero, and then allocate memory and copy the string to that buffer and assign it to a pointer in the struct.
Reading strings this way is simplest and most effective if you have of the message in a buffer already, because then you don't need a temporary buffer for the string.
Remember though, that with this scheme you have to manually free the memory containing the string when you are done with the structure.
Just add a member function that takes in the character buffer(function input parameter char *) and populates the test structure by parsing it.
This makes it more clear and readable as well.
If you provide a implicit conversion constructor then you create a menace which will do the conversion when you least expect it.
When reading variable length data from a sequence of bytes,
you shouldn't fit everything into a single structure or variable.
Pointers are also used to store this variable length.
The following suggestion, is not tested:
// data is stored in memory,
// in a different way,
// NOT as sequence of bytes,
// as provided
struct data {
short sh1;
short sh2;
int abclength;
// a pointer, maybe variable in memory !!!
char* abc;
bool bool1;
bool bool2;
};
// reads a single byte
bool readByte(byte* MyByteBuffer)
{
// your reading code goes here,
// character by character, from stream,
// file, pipe, whatever.
// The result should be true if not error,
// false if cannot rea anymore
}
// used for reading several variables,
// with different sizes in bytes
int readBuffer(byte* Buffer, int BufferSize)
{
int RealCount = 0;
byte* p = Buffer;
while (readByte(p) && RealCount <= BufferSize)
{
RealCount++
p++;
}
return RealCount;
}
void read()
{
// real data here:
data Mydata;
byte MyByte = 0;
// long enough, used to read temporally, the variable string
char temp[64000];
// fill buffer for string with null values
memset(temp, '\0', 64000);
int RealCount = 0;
// try read "sh1" field
RealCount = (readBuffer(&(MyData.sh1), sizeof(short)));
if (RealCount == sizeof(short))
{
// try read "sh2" field
RealCount = readBuffer(&(MyData.sh2), sizeof(short));
if (RealCount == sizeof(short))
{
RealCount = readBuffer(temp, 64000);
if (RealCount > 0)
{
// store real bytes count
MyData.abclength = RealCount;
// allocate dynamic memory block for variable length data
MyData.abc = malloc(RealCount);
// copy data from temporal buffer into data structure plus pointer
// arrays in "plain c" or "c++" doesn't require the "&" operator for address:
memcpy(MyData.abc, temp, RealCount);
// comented should be read as:
//memcpy(&MyData.abc, &temp, RealCount);
// continue with rest of data
RealCount = readBuffer(&(MyData.bool1), sizeof(bool));
if (RealCount > 0)
{
// continue with rest of data
RealCount = readBuffer(&(MyData.bool2), sizeof(bool));
}
}
}
}
} // void read()
Cheers.