I am trying to make a structure for a data packet that has a dynamic payload length and is determined by a variable within the header struct (LEN).
I am unsure on how to do this properly and I am confused by some of the examples that i have come across. Bellow is the Structure that is the basis of what i will be using.
Thanks.
struct packet
{
unsigned char payload;
unsigned int CRC : 16;
struct header
{
unsigned char SRC;
unsigned char DST;
unsigned char NS : 3; //3 bits long
unsigned char NR : 3;
unsigned char RSV : 1; //1 bit long
unsigned char LST : 1;
unsigned char OP;
unsigned char LEN;
} HEADER;
};
struct packet PACKET;
You can use a construct sometimes referred to as a "stretchy array". (Or as #Jerry Coffin points out, a "flexible array member") The variable-length payload needs to be at the end:
struct packet
{
struct header
{
unsigned char SRC;
unsigned char DST;
unsigned char NS : 3; //3 bits long
unsigned char NR : 3;
unsigned char RSV : 1; //1 bit long
unsigned char LST : 1;
unsigned char OP;
unsigned char LEN;
} HEADER;
unsigned int CRC : 16;
unsigned char payload[1]; //STRETCHY.
};
struct packet PACKET;
This type of structure needs to be dynamically allocated, since you need to manually make enough room for the payload.
PACKET * p = malloc( sizeof(PACKET)+payloadLength*sizeof(char) );
p->HEADER->LEN = payloadLength;
//fill in rest of header here.
memcpy(p->payload, incomingData, payloadLength);
Make the payload a pointer instead and allocate it at runtime according to the value of LEN in the header field.
You need to include the length in your struct but not the data. Depending on what you are doing you handle the data differently. The struct should probably contain a pointer to the data but you have to handle this when you serialize deserialize the struct. That is the pointer will mean nothing when you read it back in.
So you write the struct out including the size of the data field, then the data field out. When you read it back you fscanf the struct, then read the size of bytes the struct tells you to from the stream and store that as your data, and then finally store a pointer to the newly read data in the struct that was created with fscanf. If you are reading in multiple items like this you can continue at that point reading the next struct then data and so on.
Related
It is a simple question, what does this set SrcPkt to?
extern PacketStruct RxPacket[NUM_PACKETS], TxPacket[NUM_PACKETS];
extern IPMBPacketRequestStruc *SrcPkt;
SrcPkt = (IPMBPacketRequestStruc *)&(RxPacket[i].packetdata[0]);
I have a solid understanding of C and C++ but pointers have always been my weak point.
This line of code seems to set the pointer SrcPkt to point to the reference of RxPacket[i].packetdata[0]
I am only partly confident with what I believe is the correct answer, I just want some validation.
EDIT:
typedef struct
{
unsigned char status; // Buffer status
unsigned char stat2; // re-send status
unsigned char channel; // Channel source/destination
unsigned char length; // Total # of bytes in packetdata
unsigned char index; // Current byte being processed in packetdata
unsigned char packetdata[IPMB_MAXDATALENGTH];
} PacketStruct;
typedef struct
{
unsigned char rsSA;
unsigned char netFNrsLUN;
unsigned char cksm1;
unsigned char rqSA;
unsigned char rqSEQrqLUN;
unsigned char cmd;
unsigned char pktdta[37]; // rest of packet data
} IPMBPacketRequestStruc;
This sets SrcPkt to point to the address of RxPacket[i].packetdata[0]. The & operator returns the address of (a pointer to) the packet data, which is cast to the same type as SrcPkt and assigned to the SrcPkt pointer variable.
References, as exist in C++, do not exist in C. In C++, they are usually implemented as syntactic sugar around pointers so that you can use the objects they point to without having to manually dereference them. In C, you must handle and dereference pointers yourself.
I have a problem when using memcpy on a struct.
Consider the following struct
struct HEADER
{
unsigned int preamble;
unsigned char length;
unsigned char control;
unsigned int destination;
unsigned int source;
unsigned int crc;
}
If I use memcpy to copy data from a receive buffer to this struct the copy is OK, but if i redeclare the struct to the following :
struct HEADER
{
unsigned int preamble;
unsigned char length;
struct CONTROL control;
unsigned int destination;
unsigned int source;
unsigned int crc;
}
struct CONTROL
{
unsigned dir : 1;
unsigned prm : 1;
unsigned fcb : 1;
unsigned fcb : 1;
unsigned function_code : 4;
}
Now if I use the same memcpy code as before, the first two variables ( preamble and length ) are copied OK. The control is totally messed up, and last three variables are shifted one up, aka crc = 0, source = crc, destination = source...
ANyone got any good suggestions for me ?
Do you know that the format in the receive buffer is correct, when you add the control in the middle?
Anyway, your problem is that bitfields are the wrong tool here: you can't depend on the layout in memory being anything in particular, least of all the exact same one you've chosen for the serialized form.
It's almost never a good idea to try to directly copy structures to/from external storage; you need proper serialization. The compiler can add padding and alignment between the fields of a structure, and using bitfields makes it even worse. Don't do this.
Implement proper serialization/deserialization functions:
unsigned char * header_serialize(unsigned char *put, const struct HEADER *h);
unsigned char * header_deserialize(unsigned char *get, struct HEADER *h);
That go through the structure and read/write as many bytes as you feel are needed (possibly for each field):
static unsigned char * uint32_serialize(unsigned char *put, uint32_t x)
{
*put++ = (x >> 24) & 255;
*put++ = (x >> 16) & 255;
*put++ = (x >> 8) & 255;
*put++ = x & 255;
return put;
}
unsigned char * header_serialize(unsigned char *put, const struct HEADER *h)
{
const uint8_t ctrl_serialized = (h->control.dir << 7) |
(h->control.prm << 6) |
(h->control.fcb << 5) |
(h->control.function_code);
put = uint32_serialize(put, h->preamble);
*put++ = h->length;
*put++ = ctrl_serialized;
put = uint32_serialize(put, h->destination);
put = uint32_serialize(put, h->source);
put = uint32_serialize(put, h->crc);
return put;
}
Note how this needs to be explicit about the endianness of the serialized data, which is something you always should care about (I used big-endian). It also explicitly builds a single uint8_t version of the control fields, assuming the struct version was used.
Also note that there's a typo in your CONTROL declaration; fcb occurs twice.
Using struct CONTROL control; instead of unsigned char control; leads to a different alignment inside the struct and so filling it with memcpy() produces a different result.
Memcpy copies the values of bytes from the location pointed by source directly to the memory block pointed by destination.
The underlying type of the objects pointed by both the source and destination pointers are irrelevant for this function; The result is a binary copy of the data.
So if there is any structure padding then you will have messed up results.
Check sizeof(struct CONTROL) -- I think it would be 2 or 4 depending on the machine. Since you are using unsigned bitfields (and unsigned is shorthand of unsigned int), the whole structure (struct CONTROL) would take at least the size of unsigned int -- i.e. 2 or 4 bytes.
And, using unsigned char control takes 1 byte for this field. So, definitely there should be mismatch staring with the control variable.
Try rewriting the struct control as below:-
struct CONTROL
{
unsigned char dir : 1;
unsigned char prm : 1;
unsigned char fcb : 1;
unsigned char fcb : 1;
unsigned char function_code : 4;
}
The clean way would be to use a union, like in.:
struct HEADER
{
unsigned int preamble;
unsigned char length;
union {
unsigned char all;
struct CONTROL control;
} uni;
unsigned int destination;
unsigned int source;
unsigned int crc;
};
The user of the struct can then choose the way he wants to access the thing.
struct HEADER thing = {... };
if (thing.uni.control.dir) { ...}
or
#if ( !FULL_MOON ) /* Update: stacking of bits within a word appears to depend on the phase of the moon */
if (thing.uni.all & 1) { ... }
#else
if (thing.uni.all & 0x80) { ... }
#endif
Note: this construct does not solve endianness issues, that will need implicit conversions.
Note2: and you'll have to check the bit-endianness of your compiler, too.
Also note that bitfields are not very useful, especially if the data goes over the wire, and the code is expected to run on different platforms, with different alignment and / or endianness. Plain unsigned char or uint8_t plus some bitmasking yields much cleaner code. For example, check the IP stack in the BSD or linux kernels.
I want to create a DNS response to send to my browser. I've created some structs like in the rfc:
//DNS header
struct DNS_HEADER
{
unsigned short id;
unsigned char rd :1;
unsigned char tc :1;
unsigned char aa :1;
unsigned char opcode :4;
unsigned char qr :1;
unsigned char rcode :4;
unsigned char cd :1;
unsigned char ad :1;
unsigned char z :1;
unsigned char ra :1;
unsigned short q_count;
unsigned short ans_count;
unsigned short auth_count;
unsigned short add_count;
};
#pragma pack(push, 1)
struct R_DATA
{
unsigned short type;
unsigned short _class;
unsigned int ttl;
unsigned short data_len;
};
#pragma pack(pop)
struct RES_RECORD
{
unsigned char *name;
struct R_DATA *resource;
unsigned char *rdata;
};
Now I'm trying to fill in this structs so I could send a valid DNS response. I'm trying to send for example www.google.com with ipaddres 112.12.12.12 (just for fun).
This is what I have:
dns = (DNS_HEADER*)malloc(sizeof(DNS_HEADER));
dns->id = (unsigned short) htons(GetCurrentProcessId()); // ID
dns->qr = 1; // We give a response, Volgens RFC: (= query (0), or a response (1).)
dns->opcode = 0; // default
dns->aa = 0; //Not Authoritative,RFC: (= Authoritative Answer - this bit is valid in responses, and specifies that the responding name server is an authority for the domain name in question section.)
dns->tc = 0; // Not truncated
dns->rd = 1; // Enable recursion
dns->ra = 0; // Nameserver supports recursion?
dns->z = 0; // RFC: (= Reserved for future use. Must be zero in all queries and responses.)
dns->rcode = 0; // No error condition
dns->q_count = 0; // No questions!
dns->ad = 0; // How man resource records?
dns->cd = 0; // !checking
dns->ans_count = 1; // We give 1 answer
dns->auth_count = 0; // How many authority entries?
dns->add_count = 0; // How many resource entries?
But as you can see I'm have some questions about what to fill in.
Also the R_Data and res_record I can't find out via the rfc what to fill in for a random response I've made...
Can someone help me with this?
Your approach is fundamentally flawed. You cannot express a DNS packet with a struct, because the strings in a DNS packet are variable length, i.e. fields following a string will be at different offsets in the packet depending on the length of the preceding strings.
Your struct has char-pointers in place of each string, and each pointer is typically a 32-bit value that points to some other place in memory. Therefore when you try to send the struct as represented in memory, you will be sending more or less random 32-bit values in place of the strings.
Here is a fairly illustrative guide to what the DNS packets should look like: http://www.tcpipguide.com/free/t_DNSMessageProcessingandGeneralMessageFormat.htm
A few pointers at a glance: id in your response needs to be the id you received in the query. q_count should be 1 and repeat the query you received (in your example that'd be e.g. \x03www\x06google\x03com\x00\x00\x01\x00\x01 for www.google.com IN A). What needs to go in rdata is explained in RFC1035 Section 3.4.1 (in your example it'd be \x70\x0c\x0c\x0c).
I've run across this source in a legacy code base and I don't really know why exactly it behaves the way it does.
In the following code, the pData struct member either contains the data or a pointer to the real data in shared memory. The message is sent using IPC (msgsnd() and msgrcv()). Using the pointer casts (that are currently commented out), it fails using GCC 4.4.1 on an ARM target, the member uLen gets modified. When using memcpy() and everything works as expected. I can't really see what is wrong with the pointer casting. What is wrong here?
typedef struct {
long mtype;
unsigned short uRespQueue;
unsigned short uID;
unsigned short uLen;
unsigned char pData[8000];
} message_t;
// changing the pointer in the struct
{
unsigned char *pData = <some_pointer>;
#if 0
*((unsigned int *)pMessage->pData) = (unsigned int)pData;
#else
memcpy(pMessage->pData, &pData, sizeof(unsigned int));
#endif
}
// getting the pointer out
{
#if 0
unsigned char *pData; (unsigned char *)(*((unsigned int *)pMessage->pData));
#else
unsigned char *pData;
memcpy(&pData, pMessage->pData, sizeof(int));
#endif
}
I suspect it's an alignment problem and either GCC or the processor is trying to compensate. The structure is defined as:
typedef struct {
long mtype;
unsigned short uRespQueue;
unsigned short uID;
unsigned short uLen;
unsigned char pData[8000];
} message_t;
Assuming normal alignment restrictions and a 32-bit processor, the offsets of each field are:
mtype 0 (alignment 4)
uRespQueue 4 (alignment 2)
uID 6 (alignment 2)
uLen 8 (alignment 2)
pData 10 (alignment 1)
On all but the most recent versions of the ARM processor, memory access must be aligned on the ARM processor and with the casting:
*((unsigned int *)pMessage->pData) = (unsigned int)pData;
you are attempting to write a 32-bit value on a misaligned address. To correct the alignment, the address appears to have truncated the LSB's of the address to have the proper alignment. Doing so happened to overlap with the uLen field causing the problem.
To be able to handle this correctly, you need to make sure that you write the value to a properly aligned address. Either offset the pointer to align it or make sure pData is aligned to be able to handle 32-bit data. I would redefine the structure to align the pData member for 32-bit access.
typedef struct {
long mtype;
unsigned short uRespQueue;
unsigned short uID;
unsigned short uLen;
union { /* this will add 2-bytes of padding */
unsigned char *pData;
unsigned char rgData[8000];
};
} message_t;
The structure should still occupy the same amount of bytes since it has a 4-byte alignment due to the mtype field.
Then you should be able to access the pointer:
unsigned char *pData = ...;
/* setting the pointer */
pMessage->pData = pData;
/* getting the pointer */
pData = pMessage->pData;
That is a very nasty thing to do (the thing that's compiled out). You're trying basically to hack the code, and instead of using the data copy in the message (in the provided 8000 bytes for it), you try to put a pointer, and pass it through IPC.
The main issue is sharing memory between processes. Who knows what happens to that pointer after you send it? Who knows what happens to the data it points to? That's a very bad habbit to send out a pointer to data that is not under your control (i.e.: not protected/properly shared).
Another thing that might happen, and is probably what you're actually talking about, is the alignment. The array is of char's, the previous member in the struct is short, the compiler might attempt packing them. Recasting char[] to int * means that you take memory area and represent it as something else, without telling the compiler. You're stomping over the uLen by the cast.
memcopy is the proper way to do it.
The point here is the code "int header = (((int)(txUserPtr) - 4))"
Illustration of UserTypes and struct pointer casting is great of help!
typedef union UserTypes
{
SAUser AUser;
BUser BUser;
SCUser CUser;
SDUser DUser;
} UserTypes;
typedef struct AUser
{
int userId;
int dbIndex;
ChannelType ChanType;
} AUser;
typedef struct AUser
{
int userId;
int dbIndex;
ChannelType ChanType;
} AUser;
typedef struct BUser
{
int userId;
int dbIndex;
ChannelType ChanType;
} BUser;
typedef struct CUser
{
int userId;
int dbIndex;
ChannelType ChanType;
} CUser;
typedef struct DUser
{
int userId;
int dbIndex;
ChannelType ChanType;
} DUser;
//this is the function I want to test
void Fun(UserTypes * txUserPtr)
{
int header = (*((int*)(txUserPtr) - 4));
//the problem is here
//how should i set incoming pointer "txUserPtr" so that
//Fun() would skip following lines.
// I don't want to execute error()
if((header & 0xFF000000) != (int)0xAA000000)
{
error("sth error\n");
}
/*the following is the rest */
}
I am having some trouble with parsing DNS Response. Following is my code. The following are the structures. I'm getting a segmentation fault in the printf(), where im trying to print QNAME.
I'm pretty amateur when it comes to C programming, so im not really sure where im going wrong. Any helps/hints or link to useful resources/tutorials, will be appreciated. The function verfify_header() works correctly. I'm not sure why HEADER is properly extracted using memcpy(). and other fields are not.
struct HEADER{
unsigned short ID;
unsigned char RD:1;
unsigned char TC:1;
unsigned char AA:1;
unsigned char Opcode:4;
unsigned char QR:1;
unsigned char RCODE:4;
unsigned char Z:3;
unsigned char RA:1;
unsigned short QDCOUNT;
unsigned short ANCOUNT;
unsigned short NSCOUNT;
unsigned short ARCOUNT;
};
struct REQ_DATA{
unsigned short qtype;
unsigned short qclass;
};
struct QUESTION{
char* qname;
struct REQ_DATA field;
};
struct RES_DATA{
unsigned short type;
unsigned short class;
unsigned int ttl;
unsigned short rdlength;
};
struct RESPONSE{
char* name;
struct RES_DATA field;
char* rdata;
};
The following is the function that parses the dns response.
void parse_response(char *recvbuf, struct result *res)
{
struct HEADER *rechd = (struct HEADER*) malloc(sizeof(struct HEADER));
struct QUESTION qst;
struct RESPONSE *rp = (struct RESPONSE*) malloc(sizeof(struct RESPONSE));
struct RES_DATA fld;
char* rname = (char*)malloc(sizeof(char));
int hlen,qlen,rlen;
hlen = sizeof(struct HEADER);
memcpy(rechd,recvbuf,hlen);
verify_header(rechd); //This function works correctly
qlen = sizeof(struct QUESTION);
//RESPONSE is after QUESTION and HEADER
rlen = sizeof(struct RESPONSE);
int length = hlen + qlen;
rp = (struct RESPONSE*)(recvbuf + length);
//memcpy(rp, recbbuf + length, sizeof(struct RESPONSE));
memcpy(rname, rp, strlen(rname) + 1);
printf("QNAME: %s\n", *rname); //Segmentation Fault occurs over here!!!!!
}
Thanks,
Chander
The problem is that you're trying to use C structures to parse data from over the network. If your machine is big endian and your compiler happens to order bitfields the way you want them ordered, this might work okay (until you get to the pointer fields...), but it's very fragile. You should be parsing packets as an array of unsigned char.
Now, look at this:
struct QUESTION{
char* qname;
struct REQ_DATA field;
};
This is a structure that's 8 or 16 bytes (depending on your platform), nothing like the variable-length field in the actual DNS packet. And there's certainly no way you're going to get a valid pointer (which would be local to your own machine and process's address space) out of the data off the network.