Issues with socket buffer reader - c

I've been given this code for an assignment, there supposed to be errors in it but I can't actually figure out what this function is supposed to do, never mind figure out if there's any issues with it...
I am guessing that it's supposed to read the buffer line by line, but I've never seen it done this way before
The buffer that is sent to the function is empty.
int read_line(int sock, char *buffer) {
size_t length = 0;
while (1) {
char data;
int result = recv(sock, &data, 1, 0);
if ((result <= 0) || (data == EOF)){
perror("Connection closed");
exit(1);
}
buffer[length] = data;
length++;
if (length >= 2 && buffer[length-2] == '\r' && buffer[length-1] == '\n') {
buffer[length-2] = '\0';
return length;
}
}
}
Thanks in advance!

I'd say the purpose of this function is to read a line that ends with \r\n from socket stream and store it in a char array as a string, therefore the \0 (string termination character) placement.
Ok, so what's wrong with the code?
I'd start with the input parameter char *buffer - inside the function you do not know its size so you cannot check if it exceeds its size limit and it could lead to buffer overflow.
So it would be better to send buffer length as a parameter and check with every received byte if it can be stored.
EOF - it is defined as -1 and in this case actually doesn't make any sense, because nothing will be setting your data variable to EOF. The only thing you need to look out for is the end of socket stream (recv documentation). And here is an example for EOFusage.
Feel free to remove (data == EOF) from condition.
Let's say you are receiving everything regularly and you receive your last input and connection closes, so you enter this case:
if ((result <= 0) || (data == EOF)){
perror("Connection closed");
exit(1);
}
The problem here is that you won't process your last line and the program will just end. Although, I might be wrong here since I don't know when the connection is getting regularly shut down.
And a minor note here, result that equals to 0 isn't considered as an error, but a regular connection shutdown (or a 0-byte datagram was received).
I hope I haven't missed anything.

Related

Rio functions in server-client API

When I write between my server and my client, I make use of the rio functions (specified in the csapp.c), more specifically:
Rio_writen --> when writing to a socket
Rio_readlineb --> when reading from a socket
My problem is that whenever I send information from e.g. my client to my server, example:
Rio_writen(name_server_socket, "null", 4);
Rio_writen(name_server_socket, "\n", 1);
I expect my server when calling:
Rio_readlineb(&rio, name, MAXLINE);
char* lookup = name;
assert(lookup == "null"); --> fail
assert(strcmp(lookup, "null") == 0); --> fail
To assert that the message is equivalent to "null". However, when I use prints to show my messages on either site, this is what I get:
// client side message send
lookup_name: (null)|
// server side message received
lookup_name: (null)
|
I use the | character to see if anything is added after my message is sent (by printf("x: %s|",something)). In this case every message I sent get a newline character at the end, and I do not understand why.
Using the rio function, in particular Rio_getlineb to read inputs: it reads until it reaches the end of the line (provided a newline character). Thus, I'm expected to provide a newline character at the end of any message I sent (hence why i do (Rio_writen(name_server_socket, "\n", 1);) after each message sent).
Can anyone see what I am missing (or in any case know a solution to the problem)? All my setups are working correctly: sockets, rio_t etc. The messages are sent and received, but somehow add a newline at the end.
EDIT: The assertion is just a placeholder for doing something with the data. So adding a newline to the assertion might solve the assert, but not my problem: which is why a newline is added in the first place :-) My point is that I want to parse the "lookup" to a function that compares that value to a name, given in a struct. So I cannot just add a newline to the struct->name that I am looking at.
For what I see in the documentation:
– rio_readlineb reads a text line of up to maxlen bytes from file fd
and stores the line in usrbuf
Especially useful for reading text lines from network sockets
– Stopping conditions
maxlen bytes read
EOF encountered
Newline (‘ \n ’) encountered
That is, the same as fgets does.
And looking at the source code:
/* $begin rio_readlineb */
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen)
{
int n, rc;
char c, *bufp = usrbuf;
for (n = 1; n < maxlen; n++) {
if ((rc = rio_read(rp, &c, 1)) == 1) {
*bufp++ = c;
if (c == '\n') {
n++;
break;
}
It includes the trailing newline in the buffer.
This newline is very useful, the fact that the message does not include it is an indication that you have not read the whole line (in your case received the entire package), in which case you can continue requesting data.
What we usually do with fgets to skip the trailing newline is:
char *ptr = strchr(lookup, '\n');
if (ptr != NULL)
{
*ptr = '\0'; // Replace newline with NUL
}

Writing a C socket readline() function

I am writing a portable application in C which is supposed to work on mac and windows,
The socket code I am writing is using POSIX sockets Winsock api on windows has similar features.
The server is written in java and this is a client applicaiton which connects to server and then communicates,
I need to write a readline function Just as we have in C# or Java which reads a line terminated by newline character.
I am currently using following function but I do not think it is very efficient way to do it because I am reading character by character
//return number of bytes read or -1 on error
int readline(SOCKET s, char* pResponse)
{
char c = '0';
int status = 0;
int i = 0;
while(true)
{
status = recv(s,&c, 1,0);
if(status == SOCKET_ERROR)
{
//socket error
return -1;
}
else if(status == 0)
{
//closed ?
return -1;
}
else if(status > 0)
{
pResponse[i] = c;
i++;
if(c == '\n' || c == '\r\n')
{
break;
}
}
}
return i;
}
The sender/server will only send this line and wait for a response to come from client, so we can assume that it's okay to read entire socket data
So can I write this one better than this ? is reading char by char a performance issue ? any help would be appreciated.
Yes, you can do better. Readline should read larger amounts of data from the socket and if there is any data after the first '\n' it should leave that data in a buffer somewhere for later. This is called "buffering" and will require a solid knowledge of pointers and arrays.
Your socket library might already be buffering for you, in which case you don't need to do this, but probably not.
You should break, when the last character in your buffer is '\r' and the character before it is '\n'.
If you are going to treat the contents of the buffer as a c string, then replace the '\r' by a '\0' before you return.
If the file you are reading is a dos text file, and the \r\n suggests that it is, consider the case, that the final line in the file may, or may not have a \r\n. That is a case you are likely to run into.

String compare error in transmission over TCP

I have written a program in c to send a file line by line to a server from a client in c. After the file is transmitted completely, I have provided a line endoffile in the txt file to string compare in the server to identify that the file is over and the next file is being transmitted next. The next file has to be written to another file in the server. But the problem is that the strcmp is never detecting the endoffile in the code and endoffile recieved from the file in client as equal strings amd continues to write the next file from the client to the same file in server.
char enof[]="endoffile";
...
do
{
rewind(appcrt);
bytes_recieved = recv(pass_arg.connected, recv_data_c, strlen(recv_data_c), 0);
recv_data_c[bytes_recieved-1] = '\0';
fputs(recv_data_c, appcrt);
if(strcmp(enof,recv_data_c) == 0){break;}
}while(check<count);
The text file:
Necessary data
that is
being transmitted
to be written to the file
endoffile
The code used for reading from the txt file and sending to the server:
while (fgets(line, sizeof(line), crt) != NULL)
{
send(sock, line, sizeof(line), 0);
}
What change I have to make in the condition so that the problem is resolved and the code exits from the do....while(); loop. Thanks in advance.
Operating platform: Linux
Edit1: Edited the do....while() as follows:
do
{
rewind(appcrt);
bytes_recieved = recv(pass_arg.connected, recv_data_c, 100, 0);
recv_data_c[bytes_recieved] = '\0';
fputs(recv_data_c, appcrt);
printf("%s-%s",enof,recv_data_c);
//if(strcmp(enof,recv_data_c) == 0){break;}
}while(check<count);
Got the following output in terminal:
endoffile-file1line1
endoffile-file1line2
endoffile-file1line3
endoffile-file1line4
endoffile-file1line5
endoffile-file1line6
endoffile-endoffile
endoffile-file2line1
endoffile-file2line2
endoffile-file2line3
endoffile-file2line4
endoffile-file2line5
endoffile-file2line6
.
.
.
Still no hope.
Even though the client is writing a line at a time to the socket this is not how the server will consume it as data transferred over a socket is just a stream of bytes. The server must read up to the next new line character and then compare. A simple algorithm would read a byte at a time and check if it is the newline character and if is not then append it to a string until a newline character is read:
/* Read next line. */
memset(line, 0, sizeof(line));
size_t line_len = 0;
while (line_len < sizeof(line) - 1 &&
1 == recv(pass_arg.connected, &line[line_len], 1, 0))
{
if ('\n' == line[line_len]) break;
line_len++;
}
Apart from that, there are several problems with the code:
you need to send what was read from the file as it may be less than the sizeof(line). Change:
send(sock, line, sizeof(line), 0);
to:
send(sock, line, strlen(line), 0);
and also check the return value of send() to determine if it was successful.
the following is incorrect as it would only read at a maximum what was previously read (or if was initialised to an empty string nothing would be read):
bytes_recieved = recv(pass_arg.connected,
recv_data_c, strlen(recv_data_c), 0);
and, again, check the return value especially as the return value is being used to index an array. If recv() fails it returns -1, which will result in out of bounds access on the array causing undefined behaviour.
while (fgets(line, sizeof(line), crt) != NULL)
{
send(sock, line, sizeof(line), 0);
}
Don't forget that fgets() may only read one byte, if the line is blank. Thus your send() call is sending a lot of uninitialized data every call -- either contents of previous lines or random memory free()d by your application earlier.
Thus, your receiving program would need to compare against:
endoffiletten to the file
to finally see the final string. (Assuming that the line buffer started out full of ASCII NUL characters.)

Segmentation fault trying to read until header is encountered in C

I am new to C so pardon the simplicity of the question. I am trying to write a function (as a lib, so it must be robust) that constantly reads one byte (EDIT: from a serial port) until a header start byte is encountered. If it finds it, it will read in the rest of the header and payload and store it in a struct. The start of my code looks something like this (some pseudocode will be included):
soh_read = 0;
bytes_read = 0;
bytes_left = 1;
do{
n = read(fd, buf + bytes_read, bytes_left);
if(n < 0){
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR){
return -1;
}
}else{
bytes_read += n;
if(!soh_read){
if(buf[0] != SOH){
bytes_read = 0;
continue;
}
}
soh_read = 1;
//read header ...
//read payload ...
}while(timeout is not reached);
I assumed I could reset the bytes_read to 0 if SOH is not encountered and try to read in the buf[0] position again, overwriting the garbage it previously read. But it seems like this a case of a buffer overflow and why I am getting a segmentation fault? Why would that not work though? If so, what is the best way to go about this? I wanted to start at buf[0] so it'd be easy to keep track of each of the message fields. Just trying to learn from the experts here, thanks.
You've left out some information crucial to diagnosing the problem with your code as it stands. The single most important thing is (probably) whether your SOH might occur later in the file than you've allowed room for in your buf.
That said, however, I think I'd do things rather differently: since you apparently don't need (or even care about) the data that precedes the SOH anyway, why not just read all that data into one character, overwriting the previous value at each iteration, and only save more than one byte of data after you encounter the SOH so you actually have a use for it.
do {
read(fd, buf, 1);
if (n<0 && errno != EWOULDBLOCK && /* ... */)
return -1;
} while (buf[0] != SOH and !timeout_reached);
// read the header here

c recv() read until newline occurs

I'm working on writing a IRC bot in C, and have ran into a snag.
In my main function, I create my socket and connect, all that happy stuff. Then I have a (almost) infinite loop to read what's being sent back from the server. I then pass what's read off to a helper function, processLine(char *line) - the problem is, that the following code reads until my buffer is full - I want it to only read text until a newline (\n) or carriage return (\r) occurs (thus ending that line)
while (buffer[0] && buffer[1]) {
for (i=0;i<BUFSIZE;i++) buffer[i]='\0';
if (recv(sock, buffer, BUFSIZE, 0) == SOCKET_ERROR)
processError();
processLine(buffer);
}
What ends up happening is that many lines get jammed all together, and I can't process the lines properly when that happens.
If you're not familiar with IRC protocols, a brief summary would be that when a message is sent, it often looks like this: :YourNickName!YourIdent#YourHostName PRIVMSG #someChannel :The rest on from here is the message sent...
and a login notice, for instance, is something like this: :the.hostname.of.the.server ### bla some text bla with ### being a code(?) used for processing - i.e. 372 is an indicator that the following text is part of the Message Of The Day.
When it's all jammed together, I can't read what number is for what line because I can't find where a line begins or ends!
I'd appreciate help with this very much!
P.S.: This is being compiled/ran on linux, but I eventually want to port it to windows, so I am making as much of it as I can multi-platform.
P.S.S.: Here's my processLine() code:
void processLine(const char *line) {
char *buffer, *words[MAX_WORDS], *aPtr;
char response[100];
int count = 0, i;
buffer = strdup(line);
printf("BLA %s", line);
while((aPtr = strsep(&buffer, " ")) && count < MAX_WORDS)
words[count++] = aPtr;
printf("DEBUG %s\n", words[1]);
if (strcmp(words[0], "PING") == 0) {
strcpy(response, "PONG ");
strcat(response, words[1]);
sendLine(NULL, response); /* This is a custom function, basically it's a send ALL function */
} else if (strcmp(words[1], "376") == 0) { /* We got logged in, send login responses (i.e. channel joins) */
sendLine(NULL, "JOIN #cbot");
}
}
The usual way to deal with this is to recv into a persistent buffer in your application, then pull a single line out and process it. Later you can process the remaining lines in the buffer before calling recv again. Keep in mind that the last line in the buffer may only be partially received; you have to deal with this case by re-entering recv to finish the line.
Here's an example (totally untested! also looks for a \n, not \r\n):
#define BUFFER_SIZE 1024
char inbuf[BUFFER_SIZE];
size_t inbuf_used = 0;
/* Final \n is replaced with \0 before calling process_line */
void process_line(char *lineptr);
void input_pump(int fd) {
size_t inbuf_remain = sizeof(inbuf) - inbuf_used;
if (inbuf_remain == 0) {
fprintf(stderr, "Line exceeded buffer length!\n");
abort();
}
ssize_t rv = recv(fd, (void*)&inbuf[inbuf_used], inbuf_remain, MSG_DONTWAIT);
if (rv == 0) {
fprintf(stderr, "Connection closed.\n");
abort();
}
if (rv < 0 && errno == EAGAIN) {
/* no data for now, call back when the socket is readable */
return;
}
if (rv < 0) {
perror("Connection error");
abort();
}
inbuf_used += rv;
/* Scan for newlines in the line buffer; we're careful here to deal with embedded \0s
* an evil server may send, as well as only processing lines that are complete.
*/
char *line_start = inbuf;
char *line_end;
while ( (line_end = (char*)memchr((void*)line_start, '\n', inbuf_used - (line_start - inbuf))))
{
*line_end = 0;
process_line(line_start);
line_start = line_end + 1;
}
/* Shift buffer down so the unprocessed data is at the start */
inbuf_used -= (line_start - inbuf);
memmove(innbuf, line_start, inbuf_used);
}
TCP doesn't offer any sequencing of that sort. As #bdonlan already said you should implement something like:
Continuously recv from the socket into a buffer
On each recv, check if the bytes received contain an \n
If an \n use everything up to that point from the buffer (and clear it)
I don't have a good feeling about this (I read somewhere that you shouldn't mix low-level I/O with stdio I/O) but you might be able to use fdopen.
All you would need to do is
use fdopen(3) to associate your socket with a FILE *
use setvbuf to tell stdio that you want it line-buffered (_IOLBF) as opposed to the default block-buffered.
At this point you should have effectively moved the work from your hands to stdio. Then you could go on using fgets and the like on the FILE *.

Resources