I am doing socket programming for achieve communication between different entities in my application. When sending a message, I prefix the message with the length of the message and I terminate it with \0. I have recv() in a loop as follows:
void receive(int socket)
{
int num_of_bytes_read = 0, msg_len;
char *msg = NULL, *msg_p = NULL;
char recv_buf[MAX_LEN];
while(num_of_bytes_read = recv(socket, recv_buf, MAX_LEN, 0))
{
if(msg == NULL)
{
memcpy(&msg_len, message, 4);
msg_len = ntohl(msg_len);
if((msg = (char *)(sizeof(char) * msg_len)) == NULL)
systemError("Could not receive new message\n");
printf("%p\n", msg); /* prints 0xe!! Why is the address not 4B long??*/
msg_p = msg;
}
if(memcpy(&msg_p, recv_buf, num_of_bytes_read) == NULL)
systemError("memcpy failed in receive()\n");
msg_p += num_of_bytes_read;
}
printf("%p\n", msg); /* prints (nil) !!!!!*/
printf("%p\n", msg + sizeof(uint32_t)); /* prints 0x4 */
/* pass the a pointer to the beginning of the message skipping msg_len*/
int res = processMessage(msg + sizeof(uint32_t));
}
When I run the program I obviously get segmentation fault with the following error:
message=0x4
What is wrong with msg?? Can someone please help.
while(num_of_bytes_read = recv(socket, recv_buf, MAX_LEN, 0))
This is already wrong. You should test for > 0. If num_of_bytes is zero you should close the socket, and if it is -1 you should log the associated errno, e.g. with perror(), and close the socket, and in both cases stop reading.
if(msg == NULL)
{
memcpy(&msg_len, message, 4);
As long as message points to four bytes of addressable memory this will succeed. You have provided no information on the point. The purpose remains obscure.
msg_len = ntohl(msg_len);
Here you are assuming that message pointed to four bytes that magically contain an int that has magically been set to a value you are prepared to regard as a message length. Why, I don't know. Again you have provided no information on the point.
if((msg = (char *)(sizeof(char) * msg_len)) == NULL)
This is complete nonsense. Is there a malloc() missing in there somewhere?
systemError("Could not receive new message\n");
A meaningless error message. The problem appears to be about allocating memory, but it's anybody's guess. It certainly has nothing to do with receiving messages.
printf("%p\n", msg); /* prints 0xe!! Why is the address not 4B long??*/
Here you appear to think the address should be 4B long. I don't know why.
if(memcpy(&msg_p, recv_buf, num_of_bytes_read) == NULL)
You are copying data to the address of msg_p. This doesn't make sense. Also, at this point num_of_bytes_read could be -1 due to your incorrect loop condition above, so anything could happen, including trying to copy 0xffffffff bytes.
systemError("memcpy failed in receive()\n");
The only way you can get to this line is if msg_p's address was null, which is impossible. Remove the & from &msg_p in the memcpy() call. Now you can only get to this systemError() call if msg_p was zero, which would already have caused a SEGV, so you still can't get to this line. A bit of preventative coding is indicated here.
msg_p += num_of_bytes_read;
Again num_of_bytes_read could be -1 at this point, sending your pointer backwards instead of forwards.
printf("%p\n", msg); /* prints (nil) !!!!!*/
Nil indicates that msg was zero.
printf("%p\n", msg + sizeof(uint32_t)); /* prints 0x4 */
0x4 again indicates that msg was zero.
You need to improve your code in the areas indicated.
msg = (char *)(sizeof(char) * msg_len)
You are setting msg to some address based on the msg_len. Not actually anything to do with where the msg resides in memory...
Related
I'm trying to expand an example of a Tcp client developed using Ansi C, following the book "TCP/IP Sockets in C". The client connects to a Tcp Server providing strings of different lengths depending on the request provided by the client (I developed my own simple protocol). When the returned strings are short in length, everything works fine. When they're over a certain length (it happens for example with 4KB), the client crashes with a Segmentation Fault error.
The socket is handled using a wrapper to stream the i/o:
FILE *str = fdopen(sock, "r+"); // Wrap for stream I/O
And the transmission and reception are handled using fwrite() and fread().
This is the call that generates the error in my project (the caller):
uint8_t inbuf[MAX_WIRE_SIZE];
size_t respSize = GetNextMsg(str, inbuf, MAX_WIRE_SIZE); // Get the message
And this is the implementation of the GetNextMsg() function, that use to receive the data and unframe it:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <netinet/in.h>
#include "Practical.h"
/* Read 4-byte length and place in big-endian order.
* Then read the indicated number of bytes.
* If the input buffer is too small for the data, truncate to fit and
* return the negation of the *indicated* length. Thus a negative return
* other than -1 indicates that the message was truncated.
* (Ambiguity is possible only if the caller passes an empty buffer.)
* Input stream is always left empty.
*/
uint32_t GetNextMsg(FILE *in, uint8_t *buf, size_t bufSize)
{
uint32_t mSize = 0;
uint32_t extra = 0;
if (fread(&mSize, sizeof(uint32_t), 1, in) != 1)
return -1;
mSize = ntohl(mSize);
if (mSize > bufSize)
{
extra = mSize - bufSize;
mSize = bufSize; // Truncate
}
if (fread(buf, sizeof(uint8_t), mSize, in) != mSize)
{
fprintf(stderr, "Framing error: expected %d, read less\n", mSize);
return -1;
}
if (extra > 0)
{ // Message was truncated
uint32_t waste[BUFSIZE];
fread(waste, sizeof(uint8_t), extra, in); // Try to flush the channel
return -(mSize + extra); // Negation of indicated size
}
else
return mSize;
}
I suspect that this could be related to the fact that with Tcp, sender and receiver are handling data with a streaming behavior, therefore it's not granted that the receiver
gets all of the data at once, as the simple example from which I started probably assumed. In fact, with short strings everything works. With longer strings, it doesn't.
I've done a simplified debug inserting a printf as a first thing inside of the function, but when I have the crash this doesn't even get printed.
It seems like an issue with the FILE *str passed as an argument to the function, when
via the socket a message longer than usual is received.
The buffers are sized far bigger than the length of the message causing the issue (1MB vs 4KB).
I've even tried to increase the size of the socket buffer via the setsockopt:
int rcvBufferSize;
// Retrieve and print the default buffer size
int sockOptSize = sizeof(rcvBufferSize);
if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvBufferSize, (socklen_t*)&sockOptSize) < 0)
DieWithSystemMessage("getsockopt() failed");
printf("Initial Receive Buffer Size: %d\n", rcvBufferSize);
// Double the buffer size
rcvBufferSize *= 10;
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvBufferSize,
sizeof(rcvBufferSize)) < 0)
DieWithSystemMessage("setsockopt() failed");
but this didn't help.
Any ideas about the reason and how could I fix it?
This code:
{ // Message was truncated
uint32_t waste[BUFSIZE];
fread(waste, sizeof(uint8_t), extra, in); // Try to flush the channel
reads extra bytes into a buffer of size 4*BUFSIZE (4 because you intended to make the buffer unit8_t, but accidentally made it uint32_t instead).
If extra is larger than 4*BUFSIZE, then you will have a local buffer overflow and stack corruption, possibly resulting in a crash.
To do this correctly, something like this is needed:
int remaining = extra;
while (remaining > 0) {
char waste[BUFSIZE];
int to_read = min(BUFSIZE, remaining);
int got = fread(waste, 1, to_read, in);
if (got <= 0) break;
remaining -= got;
}
Code -- Main loop:
int handleClient (struct clientData* clientData)
{
void* Buffer = malloc (INET_BUFFER_SIZE); <-- VOID* BUFFER FOR RECV()
int listenSocket = clientData->listenSocket;
struct sockaddr_in clientAddress = clientData->clientAddress;
printf("Received connection from client %s:%d.\n", inet_ntoa(clientAddress.sin_addr), ntohs(clientAddress.sin_port));
while (1)
{
int packetSize;
if ((packetSize = recv(listenSocket, &Buffer, INET_BUFFER_SIZE, 0)) > 0)
{
if (packetSize == ECHO_SIZE)
{
handleEchoPacket(Buffer);
continue;
}
if (packetSize == MESSAGE_SIZE) <---THIS IS TRIGGERED BECAUSE OF PACKET SIZE
{
handleMessagePacket(Buffer);
continue;
}
}
}
Code -- handleMessagePacket(void* Buffer):
void handleMessagePacket (void* Buffer)
{
void* localBuffer = (void*) malloc(INET_BUFFER_SIZE);
memcpy(localBuffer, Buffer, INET_BUFFER_SIZE); <--SEGFAULT
(...)
}
GDB -- Partial output:
Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned () (.....) <--Tells me it doesn't have source files, not relevant to the problem.
Basically, the problem happens when I copy one memory block from a void pointer to a local buffer. Both were malloc() heap memory of same type: void.
Any suggestions or comments are welcome!
Here is what is happening: when you pass the address of Buffer to recv, received data is placed in the space allocated to the address of your buffer, which is on the stack. When you subsequently call handleMessagePacket, the address that you pass is no longer valid: it has been written over by recv!
Removing the ampersand from the call of recv should fix this problem:
if ((packetSize = recv(listenSocket, Buffer, INET_BUFFER_SIZE, 0)) > 0)
// ^^ No ampersand
In general, situations like this are best diagnosed with a memory profiler, such as valgrind. The tool would promptly tell you that there is an invalid write into stack area for the Buffer write, and that the subsequent dereference of received data as a pointer (the issue that causes SIGSEGV now) is an invalid read.
I have a client/server that exchanges messages, and i am trying to add the size of the string i am sending, in the beginning of the string, in order for the server to know how many bytes to read.
I added the message starting from the +4 pos of the char* and used memcpy to copy the strlen of the string. It doesnt seem to work and something tells me its the wrong way to do it. This is my code.
//*CLIENT*//
send_message = malloc(1024 * sizeof(char));
strcpy(send_message + 4,"GETFILES ");
strcat(send_message,"/");
strcat(send_message,directory_name);
size = strlen(send_message) + 1;
csize = malloc(4 * sizeof(char));
csize = (char*)&size;
memcpy(&send_message,csize,4);
if((sent = send(sock, send_message, strlen(send_message) + 1, 0)) < 0)
perror("write");
//*SERVER*//
while(1){
count = recv(events[i].data.fd, buf, sizeof(buf),0);
if(count == -1){
//if errno = EAGAIN we have read all data. going back to main loop
if(errno != EAGAIN){
perror("read");
done = 1;
}
break;
}
else if(count == 0){
//End of file. The remote has closed the connections
done = 1;
break;
}
printf("received message %s and count %d\n", buf, count);
}
if i comment these lines
csize = malloc(4 * sizeof(char));
csize = (char*)&size;
memcpy(send_message,csize,4);
I get this output:
received message ▒�w�GETFILES /test and count 19
otherwise i get no output..Any ideas how to fix it and add the header so my server knows in advance how many bytes to read?
As commented already, the main problem is the use of strlen(), but there is some more error.
First the strlen() and other str functions could be used in this way.
strcpy(send_message + 4,"GETFILES ");
strcat(send_message + 4,"/");
strcat(send_message + 4,directory_name);
size = strlen(send_message + 4) + 1;
It is not a good way to fix it, but it is easier to understand why your code is not working.
This is unnecessary
csize = malloc(4 * sizeof(char));
csize = (char*)&size;
memcpy(&send_message,csize,4);
You could simply do this
memcpy(send_message,&size,4);
BUT, for good practice and portability, replace all your magic 4 for sizeof(int32_t).
send_message is an array, so you do not need to get it address (&send_message), it may work this way, but if it was a pointer instead of an array it would break your code.
And last, you are printing the entire buff, but you forgot that you have a 4 bytes header, and that is the main reason it do not print anything if you properly initialize the csize.
If you try to do this
printf("received message %s and count %d\n", buf+4, count);
It will probably works, but it do not mean it is correct.
Edit: I will not update my answer to not make it bigger and harder to you see your mistake, but consider #thuovila comments below and look for more informations about htonl ntohl and how to use them.
I am developing a client that needs to parse Chunked-type HTTP transfers. I've beat my head against the wall trying to figure out the error with the following, and would appreciate it if someone might be able to catch my error a bit quicker. To sum up the issue: it seems as though, the client does not receive ALL of the chunk, thereby screwing up the rest of the process. Thanks in advance!
while(cflag){
pfile_chunk = malloc(CHUNK_SIZE+1);
memset(pfile_chunk, 0, CHUNK_SIZE);
cPtr = pfile_chunk;
cPtr2 = NULL;
k=0;
while(*(cPtr-1) != '\n'){
k++;
recv(sock, cPtr, 1, 0);
cPtr = pfile_chunk+k;
}
cPtr2 = strchr(pfile_chunk, '\r');
*cPtr2 = '\0';
sscanf(pfile_chunk, "%x", &l);
if(l == 0)
break;
printf("\nServer wants to deliver %ld bytes.\n", l);
pfile_chunk = realloc(pfile_chunk, l+1);
memset(pfile_chunk, 0, l);
recv(sock, pfile_chunk, l, 0);
fputs(pfile_chunk, f);
printf("GOT THIS, SIZE %ld:\n%s\n", strlen(pfile_chunk), pfile_chunk);
//get next \r\n bytes.
recv(sock, NULL, 2, 0);
}
At the very least, you should check the return value of recv to see if you are getting the number of bytes you are expecting to get.
A short read is definitely possible on the network, since the system call will return whatever is available in the socket receive buffer at the time you make the call.
Implement a loop until you have read in your entire chunk, or pass the MSG_WAITALL flag to recv in the last parameter. However, you still need to check for an error from recv.
ssize_t r = recv(sock, pfile_chunk, l, MSG_WAITALL);
if (r < l) {
/* check for errors ... */
} else {
/* got the data */
}
It looks as though your very first dereference for the check in your while loop will access before the beginning of your array, which is likely not to be desired behavior. Hopefully, that memory location usually won't contain \n. That could mess up your read. I expect it probably contains some information to do with your malloc, which is unlikely to be \n, so you might never see a problem from that.
Also, hopefully you can trust the other end of the socket not to send more than CHUNK_SIZE+1 before they give you a \n. Otherwise, it could seg-fault out. Normally, though, I would expect a sender to just send 10 or fewer ASCII numeric characters and a CRLF for a chunk header anyways, but they could theoretically send a bunch of long chunk extension header fields with it.
Apart from that, there's just the more important issue already found by user315052 that you should either tell the recv method to wait for all the data you requested, or check how much data it actually read.
This loop is supposed to take data from a socket line by line and put it in a buffer. For some reason, when there is no new data to return, recv returns the last couple lines it got. I was able to stop the bug by commenting out the first recv, but then I cant tell how long the next line will be. I know it's not a
while(this->connected){
memset(buf, '\0', sizeof(buf));
recv(this->sock, buf, sizeof(buf), MSG_PEEK); //get length of next message
ptr = strstr(buf, "\r\n");
if (ptr == NULL) continue;
err = recv(this->sock, buf, (ptr-buf), NULL); //get next message
printf("--%db\n%s\n", err, buf);
tok[0] = strtok(buf, " ");
for(i=1;tok[i-1]!=NULL;i++) tok[i] = strtok(NULL, " ");
//do more stuff
}
The manual states:
MSG_PEEK
This flag causes the receive operation to return data from the
beginning of the receive queue without
removing that data from the queue.
Thus, a subsequent receive call will
return the same data.
So I think you're getting the correct behavior, but perhaps expecting something else.
Your problem is that when you use recv with MSG_PEEK, you are giving recv the whole size of your buffer, if there are two lines already there, like "HELLO\r\nHELLO\r\n" it will read them into your buff.
ptr would point to the first \r\n, then you call recv with (ptr - buff) which will make recv to read only the first HELLO, into buf, but since you already READ that info into buff, you will process the two lines, but leaving \r\nHELLO\r\n in your queue, because you did not fully read them.
Next time you would peek into it and have info hanging that you already processed, leading you to believe that you are getting repeated data.
(I hope I wrote this clear enough, it is a very confusing bug you got there :)
I needed to add 2 to the length of the second recv so I'd take the "\r\n". Otherwise, it sees the first "\r\n" and thinks the line of the end is buf[0].
Hi i find the solution :
void receiver(int accepted_client) {
// Ready to receive data from client.
while (true) {
char buffer[256];
recv(accepted_client, &buffer, 255, 0);
int sum = 0;
for (int i = 0; i < 256; i++) // Check that buffer value is zero or not.
sum |= buffer[i];
if (sum != 0) {// If buffer value is not zero then start to print the new received message.
string string_message(buffer);
cout << string_message << endl;
}
memset(&buffer, 0, 256); // Clear the buffer.
}
}