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.
Related
My code is too long to post all here so i'm going to sum up what's wrong.
In a server part i'm sending on a socket 3 things :
A message
The content of a file
Another message
In a client part i'm receiving these things but :
This first is to print on terminal
The second to write in a new file
The last to print on the terminal too
But my client is stuck on a read and i really don't know why. I'm on the problem for hour so if someone can help me, it will be very great !
edit : Basically, i think my problem is that i don't know what to write on the server to stop the read on the client.. Is it \n, \0.. ?
Here's the 2 part of code :
server
void send_content(t_server *s, FILE *fd, int rfd)
{
int len;
char *buff;
write(s->socket, "150 File status okay;" \
"about to open data connection.\n\0", strlen("150 File status okay;about to open data connection.\n\0"));
fseek(fd, 0, SEEK_END);
len = ftell(fd);
buff = malloc(len * sizeof(char));
read(rfd, buff, len);
write(s->socket, buff, len);
write(s->socket, "\n\0", strlen("\n\0"));
write(s->socket, "226 Closing data connection.\n\0", strlen("226 Closing data connection.\n\0"));
free(buff);
}
client
void getfile(t_client *c, char **tab)
{
int ret;
int fd;
int z;
char buff[4096];
z = 0;
read(c->fd, buff, 4096);
write(1, buff, strlen(buff));
if (strlen(buff) < 25)
return ;
fd = creat(tab[1], S_IRUSR | S_IWUSR);
while (z == 0 && (ret = read(c->fd, buff, 4096)) > 0)
{
if (ret < 4096)
z = -1;
write(fd, buff, strlen(buff));
memset(buff, '\0', 4096);
}
read(c->fd, buff, 4096); // Stuck here
write(1, buff, strlen(buff));
close(fd);
}
Like noted you need a read function like this to make sure you receive
specified number of bytes(this function will loop till it receives number of bytes it was told to). Just use this receivall method instead of read everywhere.
With files you typically first send the file length, and then receive the file.
I did something similar while ago, hope it will help you a bit. This is the client side, which tries to receive first file length from the server, then the file:
/* create file */
FILE * ptrMyFile = fopen(&filenames[i][0],"wb");
if(NULL == ptrMyFile)
{
printf("Unable to open file \n");
return 1;
}
int size = 0;
int t = 4;
/* first receive file size from server */
/* NOTE: error checking is omitted from code, nevertheless users should stil do some error checking when using this code */
readall(sockfd, (unsigned char*) &size, &t);
/* how many 256 byte chunks are there? */
int div = size / 256;
/* loop to receive each chunk. */
for(int k = 0; k < div; k++)
{
int chunk_size = 256;
/* make sure we receive 256 bytes */
readall(sockfd, buffer, &chunk_size);
/* write to file */
fwrite(buffer, chunk_size, 1, ptrMyFile);
}
/* read the final chunk. */
int whatsleft = size - 256 * div;
readall(sockfd, buffer, &whatsleft);
/* write */
fwrite(buffer, whatsleft, 1, ptrMyFile);
/* close file */
fclose(ptrMyFile);
I leave the server part to you.
char buff[4096];
z = 0;
read(c->fd, buff, 4096);
write(1, buff, strlen(buff));
You should be saving the return value of the call to read(), in order to find out how many bytes you just received. You may have to make several calls to read() in order to get the entire message. It's wrong to use strlen() to find out how many bytes were received, because the buffer contents are uninitialized, and the first chunk of the message could be cut off anywhere, so you can't count on it being null-terminated.
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 am trying to switch modem to command mode, write AT command and parse the response, but I am not too good with C and I do not understand what is wrong. The code I am calling is:
void switch_to_command_mode(int uart)
{
current_command_type = BTCommandTypeSwitchToATMode;
char switchCmd[] = "\x2F\x2F\x2F";
char emptySymbol[] = " ";
char checkAT[] = "AT\r";
ssize_t len = 0;
write(uart, emptySymbol, strlen(emptySymbol));
sleep(1);
write(uart, switchCmd, strlen(switchCmd));
sleep(1);
write(uart, checkAT, strlen(checkAT));
char buffer[255]; /* Input buffer */
char *bufptr; /* Current char in buffer */
int nbytes;
bufptr = buffer;
while (ioctl(uart, FIONREAD, (unsigned long)&len) == 0 && len < 2) {
warnx("waiting");
usleep(1000);
}
while ((nbytes = read(uart, bufptr, buffer + sizeof(buffer) - bufptr - 1)) > 0)
{
bufptr += nbytes;
warnx("buffer %s", buffer);
if (bufptr[-1] == '\n' || bufptr[-1] == '\r')
break;
}
warnx("Final buffer: %s", buffer);
if (strncmp(buffer, "OK", 2) == 0) {
warnx("Great success!");
}
}
What I get as console output is:
waiting
waiting
buffer AT
O p �0
buffer AT
OK
�0
Final buffer: AT
OK
�0
According to documentation for the BT Module, the response should be in form of <CR><LF>OK<CR><LF>.
Any guidance on why this is possibly happening and what can I do with that would be highly appreciated.
P.S. I also already went through Serial programming for POSIX OS.
In C a "string" needs to be 0-terminated.
If they are not before being processed (printed here) the program runs into undefined behaviour. Which could also lead to "garbage" being printed.
To fix this easily initialise the buffer used to all 0s before using it, by doing:
char buffer[255] = "";
Also read() returns ssize_t not int. So you better do:
ssize_t nbytes;
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