Is ioctl()'s FIONREAD on a C socket reliable? - c

I'm seeing strange behavior with the following piece of code:
int len;
ioctl(conn_fd, FIONREAD, &len);
The usual code you might imagine precedes this (it's a toy web server):
...
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
listen(sock_fd, 5);
int conn_fd = accept(sock_fd, (struct sockaddr *) &client_addr, &client_addr_size);
The unusual behavior is when I use curl to make a request to my toy web server, everything works perfect and len is the exact size of the request. When I use Chrome, Postman, or wget, len is 0. Because len is 0, my code treats it as an empty response and doesn't consume the request.
To verify there is actually data (besides the fact that curl works), I follow the code up with:
char full_request[16384];
int bytes_read = read(conn_fd, full_request, 16383);
To my delight, outputting full_request gives me, well, the full request no matter if it's curl, Chrome, Postman, or wget.
What gives? Is my call to ioctl not to be trusted? Is there a better way to know the size of the data coming in so I can consume it?
EDIT for EJP:
char *full_request = malloc(1 * sizeof(char));
*full_request = '\0';
for (;;) {
char buf[64];
int bytes_read;
int new_len;
bytes_read = recv(conn_fd, buf, 63, 0);
buf[bytes_read] = '\0';
if (bytes_read <= 0) break;
new_len = strlen(full_request) + bytes_read + 1;
full_request = realloc(full_request, new_len * sizeof(char));
strcat(full_request, buf);
}

The problem with your reasoning is that read() blocks until data is available, or end of stream or an error occurs. So the fact that it did return data doesn't indicate that FIONREAD was wrong at the moment you called it. There is rarely a good reason to use FIONREAD.
You also cannot rely on a single read returning the full request. It is only obliged to transfer at least one byte. You have to loop.

Related

Why am I getting a segmentation fault in my C server program (but only sometimes)?

Right now, I'm trying to write a simple client/server application in order to measure the round trip time on a LAN for TCP messages of various sizes (I'm doing the timing client side). The program works fine for small packet sizes (> 1000 bytes) but I end up with a segmentation fault: 11 error for inputs of larger magnitude (10KB or greater).
int main()
{
struct sockaddr_in sin;
char buf[MAX_LINE];
int len;
int s, new_s;
/* build address data structure */
bzero((char *)& sin, sizeof( sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons( SERVER_PORT);
/* setup passive open */
if (( s = socket( PF_INET, SOCK_STREAM, 0)) < 0) {
perror("tcp program: socket");
exit(1);
}
if (( bind(s, (struct sockaddr *)& sin, sizeof(sin))) < 0) {
perror("tcp program: bind");
exit( 1);
}
listen(s, MAX_PENDING);
/* wait for connection, then receive and print text */
while(1) {
socklen_t lent = (unsigned int)&len;
if ((new_s = accept(s, (struct sockaddr *)& sin, &lent)) < 0) {
perror("tcp program: accept");
exit( 1);
}
while ((len = recv(new_s, buf, sizeof(buf), 0))){
char msg[len];
send( new_s, msg, len, 0); //echo message of same length as received message
}
close(new_s);
}
}
Again, the goal was to measure RTT, so I wanted the client to send a message, the above server to receive it, then send back a message of equivalent size. I also wanted the server to continue spinning so that the client could run iteratively, sending messages of 1KB, 10KB,...1000KB, etc. However, such iterations usually result in a segmentation fault.
Oddly enough, if I configure my client to run, for example, a single 12KB message send, the server does fine, and continues to run. And if I wait a couple of seconds, I can even repeatedly call my client and the server keeps up. But if I run the single message send in rapid succession, I end up with the segfault again.
Any ideas? I apologize in advance for any elementary errors in style or format. This is my first real foray into the C language beyond "hello world".
Thanks!
I don't know if this is the only part of the code that is wrong, but this is wrong:
while ((len = recv(new_s, buf, sizeof(buf), 0)))
Please read the man page for recv(), in particular (emphasis added)...
These calls return the number of bytes received, or -1 if an error occurred. The return value will be 0 when the peer has performed an orderly shutdown.
We know that networks are unreliable, and it is fairly common for recv() and friends to return errors.
Additionally, variable-length arrays in C are a fairly dangerous construct, because they perform dynamic allocation on the stack. They're basically alloca() in disguise, and we know how dangerous alloca() is. So this bit:
char msg[len]; // serious problems unless we have good bounds for len

sendto: Invalid argument

I'm trying to get a program (client) to communicate with another (server) using sockets in C, but I'm constantly getting the error "Invalid Argument" from the server.
I'll try to show the pieces of code that are relevant to this issue after explaining what's happening:
When it comes to the Client, I assume that everything is working correctly: The client sends a request to server and the server receives the message correctly, but then when the server attempts to reply I get an "Invalid Argument" error.
This is the declaration of the variables used by the main function of the server:
int port = DEFAULT_PORT;
int sock;
struct sockaddr_in hostAdress, clientAdress;
socklen_t clientAdressLength;
Here I do the binding of the socket to the hostAdress:
sock = socket(PF_INET,SOCK_DGRAM,0);
memset(&hostAdress, 0, sizeof(struct sockaddr_in));
memset(&clientAdress, 0, sizeof(struct sockaddr_in));
hostAdress.sin_family = AF_INET;
hostAdress.sin_port = htons(port);
hostAdress.sin_addr.s_addr = INADDR_ANY;
bind(sock, (struct sockaddr*) &hostAdress, sizeof(hostAdress));
After a bit of code, I go into a loop receiving information, using recvfrom in this manner:
recvfrom(sock, dataBuffer, sizeof(dataBuffer), 0, (struct sockaddr*)&clientAdress, &clientAdressLength);
When the server receives the request, it identifies it and jumps to this function (this behaviour is confirmed):
handle_helloRQ(sock, clientAdress, clientAdressLength);
While I am aware that "clientAdressLength" could be obtained doing sizeof(clientAdress), somehow doing this fixed this same issue last time I had it.
This function is declared as:
int handle_helloRQ(int sock, struct sockaddr_in server_addr, socklen_t server_addr_length)
In this function, the main variables are:
char buffer[512];
size_t bufferSize = sizeof(buffer);
memset(&buffer, 0, sizeof(buffer));
stshort(2, buffer);
strcat(buffer+2, "Hello World");
stshort is a simple macro that places a short int in the first 2 bytes of the buffer. This works as intended.
Once that is done, it attempts to do the following:
sendto(sock, buffer, bufferSize, 0, (struct sockaddr*)&server_addr, server_addr_length);
... And this is when things go wrong. sendto returns -1, and using perror("sendto"); I get an "Invalid Argument" error.
I tried a thousand different ways of imputing the parameters, but I can not find an error. I have exhausted my other options when it comes to this kind of problems (namely, asks the teachers of my university and show the code to a few more advanced colleagues), and no one seemed to be able to find the error.
As a side note, I do use
close(sock);
when the server receives the signal to be closed, but I may have had to force-close it once before it managed to reach this function.
To whoever is reading this, thank you for your time!
Try this instead:
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); // <-- add protocol
...
clientAdressLength = sizeof(clientAdress); // <-- add this
recvfrom(sock, dataBuffer, sizeof(dataBuffer), 0, (struct sockaddr*)&clientAdress, &clientAdressLength);
...
handle_helloRQ(sock, (struct sockaddr*)&clientAdress, clientAdressLength); // <-- pass address by pointer
...
close(sock);
int handle_helloRQ(int sock, struct sockaddr* recip_addr, socklen_t recip_addr_length)
{
char buffer[512];
memset(&buffer, 0, sizeof(buffer));
stshort(2, buffer);
strcat(buffer+2, "Hello World");
sendto(sock, buffer, sizeof(buffer), 0, recip_addr, recip_addr_length);
...
}

sendto() with an uninitalized char * buffer

I had an error with the following code, wherein it would return to me that sendto failed because "Address family not supported by protocol". I realized my mistake on the 3rd line, leaving a semicolon after the for loop declaration. Once I removed this, everything worked fine.
struct sockaddr_in their_addr;
if ((numbytes=recvfrom(sockfd, buf, 512, 0,
(struct sockaddr *)&their_addr, &addr_len)) == -1 ) {
perror("recvfrom") ;
exit(1) ;
}
char thedata[512];
int i;
for (i = 0; i < 512; i++);
{
thedata[i] = 'a';
}
unsigned int addr_len = sizeof(struct sockaddr);
if ((numbytes=sendto(sockfd, thedata, 512, 0, (struct sockaddr *)&their_addr,
addr_len)) == -1 ) {
perror("sendto") ;
exit(1) ;
}
My question is why that message specifically was returned to me. I sent an array with uninitialized values, but my assumption was that because the char array was initialized to have 512 elements, that it would have memory allocated for all 512 elements in a row. Why doesn't it send the garbage values? It's a question of curiosity more than anything, but I'd love to know more about this situation.
Thanks
When your loop exits, i is 512, so thedata[i] points to beyond the end of thedata[]. I am guessing this overwrote their_addr, specifically the protocol which from memory is at the head of the struct.
Please insert the normal warning here about the fact you overwrote memory so behaviour is undefined and anything could have happened.

Is this the correct way to send binary data over UDP using C?

I've been set a task to write some software to communicate from our embedded linux device to a server. The server that I'm to communicate with uses a strict protocol, the protocol is very obscure and proprietary - I wouldn't like to name it on here as the information could be sensitive to the company I work for.
So the data must be in either the form of 4 bit Nibbles (N), 32 bit unsigned ints (U), 32 bit signed ints (S), 8 bit unsigned ints (X) and chars (C). So for example a simplified login structure might be NNNN-User ID followed by XX-some more data, CCCC-access code. So I need to send NNNNXXCCCC in that order to login.
The data needs to be sent via UDP, and then listen for an acknowledgement on the same port. So the way I've done this is I've written a send_and_receive function, and then coded a struct, to send the struct through to the server.
typedef u_int8_t NN;
typedef u_int8_t X;
typedef int32_t S;
typedef u_int32_t U;
typedef char C;
#pragma pack(1)
typedef struct{
NN user_id[2];
X some_data[2];
C access_code[4];
} LogOnRequest;
I then declare and fill in the information for the struct and send it using this function:
void send_and_receive(void* message, void* reply, int do_send, int expect_reply){
struct sockaddr_in serv_addr;
int sockfd, i, slen=sizeof(serv_addr);
int buflen = BUFLEN;
void* buf = NULL;
if ( (strlen(message)) >= BUFLEN)
err("Message too big");
buf = malloc(buflen);
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
err("socket");
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_aton(IP_ADDRESS, &serv_addr.sin_addr)==0)
err("inet_aton() failed\n");
if(do_send == TRUE){
strcpy(buf, message);
if (sendto(sockfd, buf, buflen, 0, (struct sockaddr*)&serv_addr, slen)==-1)
err("sendto()");
}
if (expect_reply == TRUE){
if (recvfrom(sockfd, buf, buflen, 0, (struct sockaddr*)&serv_addr, &slen)==-1)
err("recvfrom()");
}
memcpy(reply, buf, BUFLEN);
close(sockfd);
free(buf);
}
Now when I do this I get no reply, and no notice that the packet has been received on the server. Basically I would like to know if I'm doing this correctly, is there a better way to do it? Would it be possible to do the same thing using bash scripts?
Any feedback would be great, because I'm feeling out of my depth on this one.
You need to wait some amount of time for a reply. If you don't get one, you need to retransmit the query and wait again. UDP is a "best effort" delivery service and does not have any built in retries.
You also need to make sure you are sending multi-byte values the same way the other end expects them. Which byte comes first?
As usual in such cases, I'd like to encourage you to use WireShark or any other sniffer software. This will de-cypher all the magic happens after your sendto() attempt: bytes order, actual address/port applied, any feedback received etc. And yes, as #wildplasser said in the comments, you shouldn't send all the buffer. So you have to add "message size" as an input parameter.

C UDP networking, extract numbers from datagram message

I am developing a network software as a part of an university exam. The software is almost finished, but actually I am finishing the concurrent part (with fork()).
My needs are to exchange between client and server these two messages as handshake.
Here an example:
PING:3506:DOWNLOAD
PONG:5605
Here my way to handle these messages:
On client side, that is the host who sends PING:3506:DOWNLOAD, I wrote
int *childLocalPort;
childLocalPort = malloc(sizeof(int));
childLocalPort[0] = (SERV_PORT_OFFSET + getPort(&portArray, &pidArray, &arrayCounter, cpid));
char *pingProcedureString;
pingProcedureString = malloc(30*sizeof(char));
strcpy(pingProcedureString, "PING:");
char *itoaPortBuffer;
itoaPortBuffer = malloc(6*sizeof(char));
itoa((childLocalPort[0]), itoaPortBuffer, 10);
strcat(pingProcedureString, itoaPortBuffer);
strcat(pingProcedureString, ":DOWNLOAD");
if (sendto(sockfd, pingProcedureString, strlen(pingProcedureString), 0, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("errore in sendto1");
exit(1);
}
free(itoaPortBuffer);
free(pingProcedureString);
n = recvfrom(sockfd, buff, MAXLINE, 0, NULL, NULL);
buff[n] = 0;
if(strcmp(buff,"PONG"))
{
int *childRemotePort;
childRemotePort = malloc(sizeof(int));
strtok(buff, ":");
childRemotePort[0] = ntohs(strtok(NULL, ":"));
printf("Remote port is %d\n", childRemotePort[0]);
close(pipeLocalPort[0]); /* Close unused read end */
write(pipeLocalPort[1], childLocalPort, sizeof(int)
close(pipeLocalPort[1]); /* Reader will see EOF */
close(pipeRemotePort[0]);
write(pipeRemotePort[1], childRemotePort, sizeof(int));
close(pipeRemotePort[1]);
}
On server side, that is the host who sends PONG:5605, I wrote
if ((n > 0) && strcmp(recvline,"PING"))
{
int *childRemotePort;
childRemotePort = malloc(sizeof(int));
strtok(recvline, ":");
char *buffTemp;
buffTemp = calloc(5, sizeof(char));
strcpy(buffTemp,strtok(NULL, ":"));
childRemotePort[0] = ntohs(atoi(buffTemp));
strtok(recvline, ":");
printf("Remote child client port is: %d\n", childRemotePort[0]);
}
As you can notice, the PONG part is missing, because I would like to focus on first non working part. The server receives correctly (as I can see from Wireshark) the message PING:3506:DOWNLOAD, but he tells me he received 19476 instead of 3506, and this is not true.
I also noticed that if I try to send numeric messages without converting them into network byte order, things get worse. I am fighting with this from many days, and I don't know anymore what to think about.
when you'r sending a PING from client , the client fails to do a ntohs on the integer port before converting to ascii but while receiving at server you do a ntohs , i think this is causing the error. Doing something on these lines on the client PING side might help however this is still error prone if the endianness of server and client is different.
char *itoaPortBuffer;
itoaPortBuffer = malloc(6*sizeof(char));
itoa(ntohs(childLocalPort[0]), itoaPortBuffer, 10);
strcat(pingProcedureString, itoaPortBuffer);
strcat(pingProcedureString, ":DOWNLOAD");

Resources