I am writing a very simple webserver in c (winsock2).
I am able to return the contents of my html pages.
Currently, what I am doing is writing the contents of a file into a char* buffer and sending it using "send()"
Although when I try to read an image (jpg, bmp), I can't write the characters into a buffer a some characters are "null" (0).
How can I send a whole image file ?
Thanks.
You can store null character in a char* buffer. You just have to use a counter to remember how many characters were written, instead of recomputing it by counting number of non-null characters (this can either be an integer or a pointer to the next point of insertion in the buffer).
To send a file, you'll do something like that:
int sendFile(int sock, const char* filename) {
FILE* file = fopen(filename, "rb");
if (file == NULL)
return -1;
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
return -1;
}
off_t size = ftello(file);
if (fseek(file, 0, SEEK_SET) != 0) {
fclose(file);
return -1;
}
if (SendBinaryFileHeaderAndSize(sock, size) < 0) {
fclose(file);
return -1;
}
char buffer[4096];
for (;;) {
size_t read = fread(buffer, 1, sizeof(buffer), file);
if (read == 0) {
int retcode = 0;
if (ferror(file))
retcode = -1;
fclose(file);
return retcode;
}
for (size_t sent = 0; sent < read;) {
int ret = send(sock, buffer + sent, read - sent, 0);
if (ret < 0) {
fclose(file);
return -1;
}
assert(ret <= read - sent);
sent += ret;
}
}
}
You need to understand how send() and fread() work. 0s in the buffer are not a problem for send or fread - they do not interpret their buffers as null-terminated strings.
Depending on how you load the image into your webserver, you would need to use either Winsock:TransmitPackets or Winsock:TransmitFile, also also wrapping the image in the appropriate HTTP headers
Note that these are MS specific extensions.
Also see c++ - Bitmap transfer using winsock getdibits and setdibits
Related
I am currently trying to work out Downloading / Uploading Files via socket (SOCK_STREAM). The following two functions are what I use to send and receive the data.
I am currently running into the following Issue(s):
The result File sometimes is not the same size as the source File
This Problem is more sever, the larger the file.
I am pretty sure that I am missing something obvious, in my loops maybe or determining the end of the data stream. I spent the past week trying a bunch of different approaches / solutions and this is the closest to a working version i got...
I am thankfull for any advice and review, if i need to provide further information please tell me
Function Sending Data from Server to Client:
void send_file(char *filename, int sock)
{
char data[1024] = {0};
FILE *fp = fopen(filename, "rb");
while (fread(data, sizeof(char), sizeof(data), fp) == 1024) {
if (send(sock, data, sizeof(data), 0) == -1) {
printf("%s%s[-] Error Transmitting File\n\n", KRED, BGHT);
break;
}
bzero(data, sizeof(data));
}
bzero(data, sizeof(data));
strcpy(data, "!EOF!");
send(sock, data, sizeof(data), 0);
bzero(data, sizeof(data));
printf("%s%s[+] Upload Successful\n\n", KGRN, BGHT);
fclose(fp);
}
Function of Client Receiving Data from Server:
void write_file(int sock, char *filepath)
{
FILE *fp;
int n;
char *lastSlash = strrchr(filepath, '\\');
char *filename = lastSlash ? lastSlash +1 : filepath;
char data[1024] = {0};
fp = fopen(filename, "wb");
while (1) {
n = recv(sock, data, sizeof(data), 0);
if (strncmp("!EOF!", data, 5) == 0) {
break;
}
if (n <= 0) {
break;
return;
}
fwrite(data, sizeof(char), sizeof(data), fp);
bzero(data, sizeof(data));
}
fclose(fp);
return;
}
i finally figured it out with the help of the following Post:
Sending files over TCP sockets C++ | Windows
I rewrote the example code to fit my needs. Works like a charm so far.
Thanks to all for giving me some more insight on the topic and helping me figure our the necessary checks on the way!
Here the new Code with a brief explanation:
First thing that needs to be recognized, ist that functions like send() recv() fread() fwrite() are likely to not fill their buffer entirely before passing it on. Meaning if you have a buffer specified with size 100, they might only fill it up to 89 95 or whatever. As a result files are likely to be corrupted. To solve this every call of the send() recv() fread() fwrite() functions needs to be checked.
First you need those two functions both on server and client side. These make sure the entire buffer is being sent / received.
Its basically just looping over send() / recv() until the buffer is filled up.
int RecvBuffer(int sock, char* buffer, int bufferSize, int chunkSize) {
int i = 0;
while (i < bufferSize) {
const int l = recv(sock, &buffer[i], __min(chunkSize, bufferSize - i), 0);
if (l < 0) { return l; }
i += l;
}
return i;
}
int SendBuffer(int sock, char* buffer, int bufferSize, int chunkSize) {
int i = 0;
while (i < bufferSize) {
const int l = send(sock, &buffer[i], __min(chunkSize, bufferSize - i), 0);
if (l < 0) { return l; }
i += l;
}
return i;
}
Next we need to apply the same check in the functions that are being called to Download / Upload a file. Here I loop over the above functions that fill our Buffer and over the fread() fwrite() until all bytes (fileSize) have been sent. The chunkSize parameter defines the size of each package being sent. I used 65.536 (64kb) so far without any issues.
int RecvFile(int sock, char *filePath, int chunkSize, int fileSize) {
char *lastSlash = strrchr(filePath, '\\');
char *filename = lastSlash ? lastSlash +1 : filePath;
FILE *fp = fopen(filename, "wb");
char buffer[chunkSize];
int i = fileSize;
while (i != 0) {
const int r = RecvBuffer(sock, buffer, (int)__min(i, (int)chunkSize), chunkSize);
if ((r < 0) || !fwrite(buffer, sizeof(char), r, fp)) { break; }
i -= r;
}
bzero(buffer, sizeof(buffer));
fclose(fp);
printf("\n%s%s[+] Download Successful\n\n", KGRN, BGHT);
return -3;
}
int SendFile(int sock, char *fileName, int chunkSize, int fileSize) {
FILE *fp = fopen(fileName, "rb");
char buffer[chunkSize];
int i = fileSize;
while (i != 0) {
const int ssize = __min(i, (int)chunkSize);
if (!fread(buffer, sizeof(char), ssize, fp)) { break; }
const int l = SendBuffer(sock, buffer, (int)ssize, (int)chunkSize);
if (l < 0) { break; }
i -= l;
}
bzero(buffer, sizeof(buffer));
fclose(fp);
printf("\n%s%s[+] Upload Successful\n\n", KGRN, BGHT);
return -3;
}
I tried and looked up TONS of pages over the net, found NOTHING that fits windows and is working always, I tried this one over the TCP protocol (streaming byte-by-byte untill you bump into 3 - I tried it on files with no 3 in it :PPPP and by 3 I mean the ASCII value 3 and not the digit '3').
Server side:
int sendFile(SOCKET s, const char* file_path)
{
FILE* fp = fopen(file_path, "rb");
int i, err = 0, bytesSent, isOk = 1;
char ch = 0;
if(!fp)
{
fclose(fp);
return 1;
}
while(ch != EOF && isOk)
{
fread(&ch, sizeof(char), 1, fp);
if(ch != EOF)
{
bytesSent = send(s, &ch, sizeof(char), 0);
if(bytesSent <= 0)
{
return 1;
}
}
else
{
isOk = 0;
}
}
ch = 3;
bytesSent = send(s, &ch, sizeof(char), 0);
fclose(fp);
return 0;
}
Client side:
int recvFile(SOCKET s, const char* file_path)
{
FILE* fp = fopen(file_path, "wb");
int bytesRecieved;
char ch;
if(!fp)
{
fclose(fp);
return 1;
}
bytesRecieved = recv(s, &ch, sizeof(char), 0);
if(bytesRecieved <= 0)
{
return 1;
}
while(ch != 3)
{
fwrite(&ch, sizeof(char), 1, fp);
putch(ch);
bytesRecieved = recv(s, &ch, sizeof(char), 0);
if(bytesRecieved <= 0)
{
return 1;
}
}
fclose(fp);
return 0;
}
The sockets are functioning well and sending and receiving well (I'm talking about sending regular messages, without the functions).
It's not returning 1, it's just turns into an infinite loop.
No idea why it's not working, any idea ? I'm totally desperate.
Instead of comparison with EOF, you should use the feof function while reading from file. EOF is just an error code returned by some functions, not an actual character in the file.
Also, I notice that the thing with the character 3 seems a way to signal the end of the file. You should consider normalizing the data transfer. You could send at the beginning of the communication the size of the file, and the client reads exactly the size of the file, to avoid having problems with files that contain that character (3 is still a valid character).
For inspiration, take a look at an old project of mine that implements this behavior (although it's linux only).
I once fell in trouble reading on tcp socket by chunks smaller than what was used for writing. On a socket you write packets of a determined length (write or send), and implementation may discard the end of the packet if read size is shorter.
From man page on recv : All three routines [recv, recvfrom, recvmsg] return the length of the message on successful completion. If a message is too long to fit in the supplied buffer, excess bytes may be discarded depending on the type of socket the message is received from.
When reading, you should allways use a buffer of a size at least equals of the longest buffer used in writing.
You could dump what you receive to confirm.
void download(char *file)
{
int size = getsize(file);
printf("Got size %d\n", size);
sprintf(buff, "GET %s\n", file);
send(sockfd, buff, strlen(buff), 0);
rsize = recv(sockfd, buff, 1000, 0);
sscanf(buff, "%d", &resultcode);
printf("%s", buff);
if (strcmp(buff, "+OK\n") != 0)
{
printf("download failed\n");
}
FILE *dlfile = NULL;
if ((dlfile = fopen(file, "r")) != NULL)
{
dlfile = fopen(file, "w");
do
{
rsize = recv(sockfd, buff, 1000, 0);
for (int i = 0; i < rsize; i++)
{
fprintf(dlfile, "%c", buff[i]);
}
size = size - rsize;
} while (size != 0);
}
fclose(dlfile);
}
I am trying to make the download function print out contents of file user typed, then save it to their current directory. I did a debug line printf("%s", buff); and it prints out +OK\n(filename). It is supposed to print out +OK\n. It also prints out download failed then a segmentation fault error. What am I missing?
Several things going on here. First, recv and send basically operate on arrays of bytes so they do not know about line endings and such. Also note that recv is not guaranteed to fill the buffer - it generally reads what is available up to the limit of the buffer. For your strcmp against "+OK\n", you could use strncmp with a length of 4 but that is a bit direct (see below). Next note that the buff string is not null terminated by recv so your printf could easily crash.
When you go in to your loop, the buffer already has part of the rest of your I/O in it. May include other fields or parts of the file. You need to process it as well. It is not clear to me what getsize does - but using that size to drive your loop seems off. Also, your loop to fprintf the values can be replaced by a call to fwrite.
Overall, you need to properly buffer and then parse the incoming stream of data. If you want to do it yourself, you could look at fdopen to get a FILE object.
I have a "file" as a resource. I can only use read(), write() and fstat() it. This file is a text file which I would like to parse.
Normally I use fgets() to read the text file line by line and parse it. How can I do this in this case?
FILE *fp;
char buffer[128];
fp = fopen( "/home/txtfile", "r" );
if (fp == NULL){
perror("file missing");
}
while (fgets (buffer, sizeof(buffer), fp) != NULL) {
//some code
}
How can I do the same with read() ?
Is this right?
int fd = open("/dev/file",O_RDONLY);
if (fd==-1) {
printf("Failed to open file!!!\n");
}
while (fgets (buffer, sizeof (buffer), fd) != NULL) {
//some code
}
Unless your file is huge, if you're using read(), it would be easier to read in the entire file, then operate on the memory buffer, rather than in discrete chunks. That is, unless each line is of a fixed length.
I'd do something like this:
int rc;
int fd = open("data", O_RDONLY); // open the file for reading
if (fd == -1) {
// error
}
// to be thorough, do a stat() here to find how big to make the buffer
struct stat sb;
rc = fstat(fd, &sb);
if (rc == -1) {
// error
}
char *buffer = calloc(1, sb.st_size);
int bytes_read = 0;
// read in entire file; each read() can be incomplete, hence why it's in a loop,
// and reading from/writing to increasing sections of the memory and file
while ((rc = read(fd, (buffer + bytes_read), (sb.st_size - bytes_read))) > 0) {
if (rc == -1) {
// error
}
bytes_read += rc;
}
close(fd);
// Now, to read it line-by-line...
char line[128]; // 128 is arbitrary
int pos = 0;
while ((rc = sscanf(buffer + pos, "%127[^\n]\n", line)) > 0) {
pos += strlen(line) + 1;
// do stuff with line
}
return 0;
Then you can operate on your memory buffer line-by-line by scanning for newlines, or using sscanf(). Also make sure to free() your buffer!
Edit: I've added some example code for using sscanf() to handle your buffer. If you know the format of the lines (you say you're parsing them) you might be able to make better use of sscanf() by using the format specifiers. All of this is untested, by the way.
Something like this :
int fd = open("/dev/file",O_RDONLY);
ssize_t res = 0;
while((res = read(fd, buffer, sizeof(buffer))) > 0) {
//some code
}
if (res < 0) {
//handle error
} else{
//close fd
}
Is this right?
No.
read() is a system call that operates on a Unix file descriptor, not a stdio FILE*. Other than that, it works by reading data from the file and putting it in the buffer you supply.
int fd = open("/dev/file",O_RDONLY);
if (fd==-1)
{
printf("Failed to open file!!!\n");
}
else
{
char buffer[BUF_SIZE];
ssize_t bytesRead = read(fd, buffer, BUF_SIZE);
while (bytesRead > 0)
{
// do something with the buffer
bytesRead = read(fd, buffer, BUF_SIZE);
}
if (bytesRead == -1)
{
// error
}
// bytesRead == 0 => end of file
}
I have to write a SSL client in C that connects to a server,and gets either a html,either a file. I managed to get the html,but i can't download a binary file. For example,i'm trying to download a 3.8mb file from https://www.openssl.org/source/openssl-1.0.0d.tar.gz and my code only manages to download 1.1mb of them,and i don't even know if i get the right data in it.
Here is the function that i made for it:
char *sslReadfile (connection *c)
{
const int readSize = 1024;
char *rc = NULL;
int received, count = 0;
char buffer[1024];
char filename[40];
printf("Input the file name to be saved:\n");
scanf("%s",filename);
FILE *fp;
fp = fopen(filename, "wb");
if (c)
{
while (1)
{
if (!rc)
rc = malloc (readSize * sizeof (char) + 1);
else
rc = realloc (rc, readSize * sizeof (char) + 1);
received = SSL_read (c->sslHandle, buffer, readSize);
buffer[received] = '\0';
if (received > 0)
fprintf(fp,"%s",buffer);//strcat (rc, buffer);
if (received < readSize)
break;
//count++;
}
}
printf("\nFile saved!! %s !!!\n\n",filename);
fclose(fp);
return rc;
}
oh, and i call it like that:
char command[50];
sprintf(command,"GET /%s\r\n\r\n",relativepath);
sslWrite (c, command);
response = sslReadfile (c);
where c is my connection.
Don't use fprintf to write binary data. Use fwrite. The reason your output is smaller is that fprintf is stopping at the first null character, skipping any characters that remain in the 1024 byte buffer. Also, you don't appear to be using, and don't need the mallocd rc buffer.
So, after the call to SSL_read, you want something like this:
if (received <= 0) break;
fwrite(buffer, 1, received, fp);
You break the loop when received < readSize, instead you should only break the loop when received <= 0 and you have inspected SSL_shutdown() and/or SSL_get_error().
Also, you shouldn't NUL terminate your buffer and use fprintf, but keep the buffer as is while using fwrite. You are now introducing NULs in your data that weren't there.