Rio functions in server-client API - c

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
}

Related

Issues with socket buffer reader

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.

Why won't the output of the client be printed to the screen?

I'm trying to inject remote code through sockets, but for some reason, when writing the command cd or any other command, which in turn gives me an answer through the client, the answer won't be outputted to the screen.
I've tried various ways of outputting to the screen but none of them work.
Server part which outputs:
void *ReadAndWriteSocket(void *entrypoint) {
char message[256];
char buffer[256];
while (1) {
if((sockStructure.n = recv(sockStructure.newsockfd, buffer, strlen(buffer), 0)) > 0) {
for(int i = 0; i < strlen(buffer) - 1; i++) {
printf("%s", buffer[i]);
}
}
}
scanf("%s", &message);
send(sockStructure.newsockfd, message, strlen(message), 0);
}
Client code:
require 'socket'
require 'open3'
def createClient(hostname, port)
s = TCPSocket.new hostname, port
while line = s.gets
if line == "exit"
s.close
elsif line[0..1] == "cd"
output = Dir.chdir(Dir.pwd + "/" + line[2..line.size].delete(" \t\n\r\0"))
s.puts(Dir.pwd)
end
stdin, stdout, stderr, wait_thr = Open3.popen3(line)
s.puts("#{stdout.read}")
end
end
createClient("127.0.0.1", 8082);
As soon as you receive even the tiniest drop of data, you block in scanf rather than trying to receive more data. You either need to support moving data in both directions at the same time (waiting for input in scanf and data in recv) at the same time or you need to be smarter about when you change the direction.
Consider the following scenario:
You call ReadAndWriteSocket.
Your call to recv gets just an "H", the very first byte of the message.
You pass that "H" to printf, but nothing gets printed because your standard output is line buffered.
You call scanf and wait forever, but nothing is coming because the user has no intention of typing anything until they see the received message.
Your code will never call recv again, too bad since the rest of the received message is waiting in the receive buffer but you're stuck in scanf.
When you have code that moves data in two directions, you must be absolutely sure that you never block waiting to receive data in one direction when the data you should be waiting for is coming in the other direction. Your code has nothing to prevent this.
Update: Problems with your new code:
void *ReadAndWriteSocket(void *entrypoint) {
char message[256];
char buffer[256];
while (1) {
if((sockStructure.n = recv(sockStructure.newsockfd, buffer, strlen(buffer), 0)) > 0) {
What's going on here? You can only pass a string to strlen. But buffer doesn't contain a string here (it's uninitialized on the first pass and contains an unrelated length on subsequent passes). So you're passing a nonsensical value for the length to recv.
for(int i = 0; i < strlen(buffer) - 1; i++) {
printf("%s", buffer[i]);
Two problems here: First, buffer[i] is not a string and the %s format specifier is for strings. Second, standard output is line buffered, so unless the buffer happens to contain a line, it won't show.
}
}
}
scanf("%s", &message);
Why are you waiting for user input here? What if you only received a tiny fraction of the message and didn't even receive an entire line and so haven't ouputted anything yet? How do you know the next thing you need to do is receive input from the user and not receive the rest of the message?
send(sockStructure.newsockfd, message, strlen(message), 0);
}
If you have some protocol that specifies who will send when and who will receive when and what constitutes a message and when to wait for user input, you have to write code that implements that protocol. Otherwise, you'll get stuck in scanf waiting for input when there's data in the socket's receive buffer.

Parse This IP To Be The Right Length?

For something I am doing I would like to get the external IP of the PC running the program (written in C). So far I have found the best way is to connect to a site that simply displays the IP of the visitor, and then parse the webpage for the IP. The first part was easy, but when I display the buffer I read the page (which only visibly consisted of my IP) I get a few random extra symbols/characters after the IP. Here is the code I am using ATM (simplified to exclude other stuff):
HINTERNET OpenInternet = NULL;
HINTERNET GetIP = NULL;
DWORD BytesRead = 0;
char IPGrabbed[30];
OpenInternet = InternetOpen("Microsoft Internet Explorer", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (OpenInternet == NULL) {
return 1;
}
GetIP = InternetOpenUrl(OpenInternet, "http://api.externalip.net/ip/", NULL, 0, INTERNET_FLAG_RELOAD, 0);
if (GetIP == NULL)
return 1;
if (!InternetReadFile(GetIP, &IPGrabbed, sizeof(IPGrabbed), &BytesRead))
return 1;
printf("IP: %s", IPGrabbed);
getchar();
I also tried parsing through IPGrabbed stopping at any '\n' or '\r' (because it displays the weird characters on the line below the IP when I printf() it) and then copying everything up till there to another char array, but got the same result. Could anyone help me figure out what is going on here? Thank you.
Initialise the buffer to all 0s and then read one character less then the buffer to read into provides.
This way the 0-terminator a C-"string" relies on is provided implicitly.
char IPGrabbed[30] = ""; /* Initialise the buffer to all `0`s ... */
[...]
/* ... and then read one character less then the buffer to read into provides. */
if (!InternetReadFile(GetIP, &IPGrabbed, sizeof(IPGrabbed) - 1, &BytesRead))
return 1;
fprintf(stderr, "IP: %s", IPGrabbed); /* Print to stderr, as it's not buffered so
everything appear immediately to the console. */
The result from InternetReadFile is not null-terminated, you need to add a null character to the end of the string by code after the read is successful:
IPGrabbed[BytesRead] = 0;
Edit 1
As suggested in the comment by Jonathan Potter, the above code may be subjected to a buffer overflow error if the site being accessed is returning anything longer than a IP string (maximum 16 characters).
Suggest to change the InternetReadFile to read 1 less of the buffer length instead of full buffer length to eliminate the above problem.
InternetReadFile(GetIP, &IPGrabbed, sizeof(IPGrabbed)-1, &BytesRead)

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.)

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