Reputation: 463
I'm trying to create a function that puts together a buffer from arbitrary types. Think basic RPC. So the buffer looks something like
{ char opcode, uint32_t param1_size, param1, ... , uint32_t paramN_size, paramN }
It seems to be working but when extracting the data from the buffer it doesn't seem to be interpreting the string correctly.
int enqueue_into_buf(char *buf, size_t buf_pos, const uint32_t param_len, const void *param) {
/* If param_len is NULL, then change opcode */
if(param_len == 0) {
memcpy( buf, param, 1 );
if(buf_pos == 0)
buf_pos++;
return buf_pos;
}
memcpy( buf + buf_pos, ¶m_len, sizeof(param_len) );
buf_pos += sizeof(param_len);
memcpy( buf + buf_pos, param, param_len );
buf_pos += param_len;
return buf_pos;
}
int main(int argc, char *argv[])
{
char opcode;
uint32_t param_len, num_params, buf_size, buf_pos = 0, received_num;
char *buf, *temp;
char *input_string = "file01"; /* example string to use as parameter */
size_t input_size = 4, received_size; /* example variable to also use as a parameter */
opcode = '1'; /* opcode equals function 1 */
num_params = 2; /* number of parameters */
/* setting the size of the buffer that will be sent over the network */
buf_size = sizeof(opcode) + ( num_params * sizeof(uint32_t) ) + (strlen(input_string) * sizeof(char)) + sizeof(input_size);
buf = malloc( buf_size );
/* Notice the ampersand! */
buf_pos = enqueue_into_buf(buf, buf_pos, 0, &opcode);
buf_pos = enqueue_into_buf(buf, buf_pos, strlen(input_string), &input_string);
buf_pos = enqueue_into_buf(buf, buf_pos, sizeof(input_size), &input_size);
/* At this point, since we inserted everything into the buffer,
the buffer size and current buffer position should be equal */
if(buf_pos == buf_size)
printf("Calculated buffer size correctly and inserted everything correctly as well. Buffer size = %d\n", buf_size);
/** Extract from buffer **/
buf_pos = 0;
printf("Opcode: %c\n", buf[buf_pos]);
buf_pos++;
memcpy(&received_num, buf + buf_pos, sizeof(uint32_t));
printf("Size of parameter 1: %d\n", received_num);
buf_pos += sizeof(uint32_t);
temp = malloc(received_num + 1);
memcpy(temp, buf + buf_pos, received_num);
temp[received_num] = '\0';
printf("Parameter 1: %s\n", temp);
buf_pos += received_num;
memcpy(&received_num, buf + buf_pos, sizeof(uint32_t));
printf("Size of parameter 2: %d\n", received_num);
buf_pos += sizeof(uint32_t);
memcpy(&received_size, buf + buf_pos, sizeof(size_t));
printf("Parameter 2: %d\n", received_size);
buf_pos += sizeof(size_t);
return 0;
}
EDIT: Output of code:
Calculated buffer size correctly and inserted everything correctly as well. Buffer size = 23
Opcode: 1
Size of parameter 1: 6
Parameter 1: @ @
Size of parameter 2: 8
Parameter 2: 4
I think I'm not copying the data correctly into the buffer because, using the same parameters (input_string = "file01", input_size = 4), this code works...
/* OPCODE */
buf[buf_pos] = opcode;
buf_pos++;
/* PARAMETER 1 */
param_len = (strlen(input_string) * sizeof(char)); /* size of parameter 1 */
memcpy(buf + buf_pos, ¶m_len, sizeof(uint32_t));
buf_pos += sizeof(uint32_t);
//memcpy( buf + buf_pos, &input_string, (strlen(input_string) * sizeof(char)) );
strcat( buf + buf_pos, input_string );
buf_pos += strlen(input_string) * sizeof(char);
/* PARAMETER 2 */
param_len = sizeof(input_size);
memcpy(buf + buf_pos, ¶m_len, sizeof(param_len)); /* same as saying sizeof(uint32_t) */
buf_pos += sizeof(uint32_t);
memcpy(buf + buf_pos, &input_size, sizeof(input_size));
buf_pos += sizeof(input_size);
EDIT: Output of code:
Calculated buffer size correctly and inserted everything correctly as well. Buffer size = 23
Opcode: 1
Size of parameter 1: 6
Parameter 1: file01
Size of parameter 2: 8
Parameter 2: 4
But obviously I don't want to use strcat() because I won't know what kind of data type it will be. Am I using memcpy incorrectly??
EDIT: put outputs of programs
Upvotes: 0
Views: 1057
Reputation: 49483
There are a couple of problems here.
First:
buf_pos = enqueue_into_buf(buf, buf_pos, strlen(input_string), &input_string);
You're trying to pass the fourth parameter to enque as a void *
well a string is already a pointer type, so passing the address of the string is not what you wanted to do, just:
buf_pos = enqueue_into_buf(buf, buf_pos, strlen(input_string), input_string);
Is correct.
Second:
You never zero'ed out your buf, so you don't know what is sitting in there:
buf = malloc( buf_size );
memset(buf, 0, buf_size); // add this line here to clear the buffer before using it
Declare, allocate, initialize... then use. It will save you a lot of headaches.
Upvotes: 1
Reputation: 183978
You highlighted the problem yourself:
/* Notice the ampersand! */
buf_pos = enqueue_into_buf(buf, buf_pos, 0, &opcode);
buf_pos = enqueue_into_buf(buf, buf_pos, strlen(input_string), &input_string);
You have declared
char *input_string = "file01";
So when you pass &input_string
to enqueue_into_buf
, you are copying strlen(input_string)
bytes starting from where the pointer input_string
is stored into the buffer.
Usually, on 64-bit systems, that would be six of the eight bytes of the pointer value, on 32-bit systems the four bytes of the pointer plus two behind that (invoking undefined behaviour).
But you want to copy the string "file01"
to the buffer, i.e. what the pointer points to, hence you must not pass the address of the pointer but the pointer itself:
buf_pos = enqueue_into_buf(buf, buf_pos, strlen(input_string), input_string);
// ^^ No address taken here!
Upvotes: 1