Using the below code, I'm able to read and store text files sent by FTP clients to my FTP server just fine, but images and other binary data ends up being corrupted. I'm sure I'm overlooking something in the spec. Is the error obvious?
//open the file sepcified for output
std::ofstream stor_file(cmd_arg, std::ios::binary);
unsigned buf_size = 500;
unsigned char *buf = new unsigned char[ buf_size ];
const char *open_conn = "150 openeing data connection\r\n";
CNetwork::write_to_socket( cntl_fd, open_conn, strlen(open_conn) );
// slurp the whole file from the socket
int bytes_read = read( data_fd, buf, buf_size);
while (bytes_read > 0)
{
// write what's in the buffer to the file
for (int i = 0; i < bytes_read; ++i)
{
stor_file << buf[i];
}
memset( buf, 0, sizeof( buf ) );
bytes_read = read( data_fd, buf, sizeof( buf ));
}
delete [] buf;
// done writing...close the file!
stor_file.close();
// write to socket: "226 File successfully transferred\r\n";
Related
I have made this code below to download large file and save it to file. (I removed not important parts)
int nDataLength;
int i = 0;
static char buffer[4096];
while ((nDataLength = recv(Socket, buffer, sizeof(buffer), NULL)) > 0)
{
//MessageBoxA(0, std::to_string(nDataLength).c_str(), "TEST", 0);
fwrite(buffer, nDataLength, 1, pFile);
}
Now It saves file, but it also saves HTTP header. Now I don't really know how to strip the header from received data.
If it was small enough I could read Content-Length from buffer and then open the file again and remove header, but thats not the option cause buffer will be overwritten with new data.
Also I cannot use other libraries like libcurl etc.
EDIT:
char* content = strstr(buffer, "\r\n\r\n");
if (content != NULL) {
content += 4;
fwrite(content, nDataLength, 1, pFile);
}
else
{
fwrite(buffer, nDataLength, 1, pFile);
}
OK, I came up with function that strips header before saving.
int nDataLength;
int i = 0;
static char buffer[4096];
while ((nDataLength = recv(Socket, buffer, sizeof(buffer), NULL)) > 0)
{
char* content = strstr(buffer, "\r\n\r\n");
if (content != NULL) {
std::string s2(buffer);
size_t p = s2.find("\r\n\r\n");
fwrite(buffer+p+4, nDataLength-p-4, 1, pFile);
}
else
{
fwrite(buffer, nDataLength, 1, pFile);
}
}
I am trying to send files of all types over a socket to the browser in C++. I am able to send .txt and .html files over the socket fine, but when I try to send a jpeg, I get the error The image "localhost:8199/img.jpg" cannot be displayed because it contains errors. I am not sure why my program works fine for sending text files but cannot handle images. This is how I read the file and write it to the client:
int fileLength = read(in, buf, BUFSIZE);
buf[fileLength] = 0;
char *fileContents = buf;
while (fileLength > 0) {
string msg = "HTTP/1.0 200 OK\r\nContent-Type:" + fileExt + "\r\n\r\n\r\nHere is response data";
int bytes_written;
if(vrsn == "1.1" || vrsn == "1.0"){
write(fd, msg.c_str(), strlen(msg.c_str()));
bytes_written = write(fd, fileContents, fileLength);
} else {
bytes_written = write(fd, fileContents, fileLength);
}
fileLength -= bytes_written;
fileContents += bytes_written;
}
Full code is here: http://pastebin.com/vU9N0gRi
If I check the response headers in my browser network console, I see that the Content-Type is image/jpeg so I am not sure what I am doing wrong.
Are image files handled differently than normal text files? If so, what exactly do I have to do in order to handle sending image files to the browser?
string msg = "HTTP/1.0 200 OK\r\nContent-Type:" + fileExt + "\r\n\r\n\r\nHere is response data";
This is an invalid HTTP response for binary data, like images. After the terminating \r\n\r\n at the end of the HTTP headers, everything after that is message body data. So, you are sending \r\nHere is response data as the first few bytes of your images, corrupting them. You need to remove that altogether, even for your txt and html files.
Worse, you are sending msg on every loop iteration, so you are preceding every buffer of file data with your HTTP response string, thoroughly corrupting your image data further.
Also, your response is missing Content-Length and Connection: close response headers.
Try something more like this instead:
int sendRaw(int fd, const void *buf, int buflen)
{
const char *pbuf = static_cast<const char*>(buf);
int bytes_written;
while (buflen > 0) {
bytes_written = write(fd, pbuf, buflen);
if (written == -1) return -1;
pbuf += bytes_written;
buflen -= bytes_written;
}
return 0;
}
int sendStr(int fd, const string &s)
{
return sendRaw(fd, s.c_str(), s.length());
}
...
struct stat s;
fstat(in, &s);
off_t fileLength = s.st_size;
char buf[BUFSIZE];
int bytes_read, bytes_written;
if ((vrsn == "1.1") || (vrsn == "1.0")) {
ostringstream msg;
msg << "HTTP/1.0 200 OK\r\n"
<< "Content-Type:" << fileExt << "\r\n"
<< "Content-Length: " << fileLength << "\r\n"
<< "Connection: close\r\n"
<< "\r\n";
sendStr(fd, msg.str());
}
while (fileLength > 0) {
bytes_read = read(in, buf, min(fileLength, BUFSIZE));
if (bytes_read <= 0) break;
if (sendRaw(fd, buf, bytes_read) == -1) break;
fileLength -= bytes_read;
}
close(fd);
write(fd, msg.c_str(), strlen(msg.c_str())); - have you considered that an image may contain null (zero) bytes? so treating it as a C style string is not really a good idea.
You should be sending your raw data and writing the size of that data - which is not just up until the first null byte.
I have a simple implementation of AES (CTR Mode, which allows encrypting/decrypting more than 16bytes)
And I want to read a file to a char array and then to encrypt it.
I have tried the following, without success:
long GetFileSize(const char* filePath)
{
FILE* pFile = fopen(filePath, "rb");
if (pFile == NULL)
{
printf("error");
getchar();
}
fseek(pFile, 0, SEEK_END);
long lSize = ftell(pFile);
rewind(pFile);
fclose(pFile);
return lSize;
}
// Reads given file to buffer, and returns pointer to that buffer.
// Caller should free this memory later.
unsigned char * ReadFile(const char * filePath)
{
FILE * pFile;
long lSize;
unsigned char * buffer;
size_t result;
pFile = fopen(filePath, "rb");
if (pFile == NULL) { fputs("File error", stderr); ; }
// obtain file size:
fseek(pFile, 0, SEEK_END);
lSize = ftell(pFile);
rewind(pFile);
// allocate memory to contain the whole file:
buffer = (unsigned char*)malloc(sizeof(unsigned char)*lSize);
if (buffer == NULL) { fputs("Memory error", stderr); ; }
// copy the file into the buffer:
result = fread(buffer, 1, lSize, pFile);
if (result != lSize) { fputs("Reading error", stderr); ; }
// terminate
fclose(pFile);
// the whole file is now loaded in the memory buffer. Return pointer to data.
return buffer;
}
int main(int argc, char *argv[])
{
//All char arrays are null terminated here...
uchar szkey[KEY_128] = "very strong key";
const char *filePath = "key.txt";
// Get file size which must be encrypted
long originalSize = GetFileSize(filePath);
// Make this size multiple of block size (16 bytes).
long multipleSize = (originalSize / 16 + 1) * 16;
// Store number of bytes that were needed for padding.
int nrOfPaddingBytes = (int)(multipleSize - originalSize);
// Now, read the file.
unsigned char * fileContents = ReadFile(filePath);
// Create a large buffer to store file contents (including padding bytes),
// and copy file contents to it.
unsigned char * buffer = (unsigned char*)malloc(multipleSize);
memcpy(buffer, fileContents, originalSize);
// Allocate space for ciphertext also (should also have size multiple of 8).
unsigned char * ciphertext = (unsigned char*)malloc(multipleSize);
char* cipherhex = (char*)malloc(multipleSize * 2);
char* decryptedhex = (char*)malloc(multipleSize);
unsigned char* dechex = (unsigned char*)malloc(multipleSize);
// Delete old file buffer.
free(fileContents);
fileContents = NULL;
// Pad the plaintext
for (int i = 0; i < nrOfPaddingBytes; i++)
{
buffer[originalSize + i] = nrOfPaddingBytes;
}
aes_ctx_t *ctx;
u64 nonce;
virtualAES::initialize();
ctx = virtualAES::allocatectx(szkey, sizeof(szkey));
virtualAES::rand_nonce(&nonce);
//encrypt
virtualAES::encrypt_ctr(ctx, buffer, ciphertext, sizeof(buffer), nonce);
cout << "cipherdata in ansi:\n" << ciphertext << "\n\n";
virtualAES::strtohex(ciphertext, cipherhex, originalSize);
cout << "cipherdata in hex:\n" << cipherhex << "\n\n";
return 0;
}
but it fails for some reason. Could someone provide me an algorithm to read a file for encrypting?
I will rephrase the whole question here so that it is answerable.
I am able to copy binary file perfectly in the same machine not using sockets but just making a simple copy function. Trying to implement this code for copying onto a TCP/IP connection but can't get it to work.
FILE *filehandle = fopen("imagefile.jpg", "rb");
FILE *dest =fopen("imagecopy.jpg", "wb"); // copied image file
fseek(filehandle, 0, SEEK_END);
unsigned long filesize = ftell(filehandle);
char *buffer = (char*)malloc(sizeof(char)*filesize);
rewind(filehandle);
int bytesread = fread(buffer, sizeof(char), filesize, filehandle);
for( int i=0; i<filesize; i++ )
{
fputc(buffer[i], filehandle); // copies all the contents to dest
}
The code above works perfectly for copying an image file in the computer but when implemented to copy on server, it is difficult to go about it.
I am trying to send an image file from a server to a client both which have been made manually in C. The length of the file to be sent by the server is only known to the server when it's sending the file so the buffer is dynamically generated in the server, something like this:
SERVER
fseek(filehandle, 0, SEEK_END);
long filesize = ftell(filehandle); // file could be 11000bytes
char *buffer = (char*)malloc(sizeof(char)*filesize); // char buffer with 11000 bytes to store the data from the file.
// then I call the send() function
rewind(filehandle); // go back to beginning
send(clientsocket, buffer, filesize, 0); // this is being sent perfectly, no errors because in the actual code, I am checking for errors
CLIENT
// here is where I don't understand how to dynamically allocate the 11000 bytes to store the data in a client buffer
// the filesize is not necessarily going to be 11000 so need to dynamically allocate
// I did the following:
#define BUFSIZE 10
FILE *filehandle = fopen("imagefile.jpg", "wb"); // image file created by client
char *buffer = (char*)malloc(sizeof(char)*BUFSIZE);
int bytesread = recv(buffer, 1, strlen(buffer), 0);
if( bytesread > 0 )
{
printf("Bytes read: %d\n", bytesread); // bytes read is 5
printf("Buffer: %s\n", buffer); // but buffer shows all the binary text like it normally would
// when I try to store buffer in a file, it doesn't put full buffer because only 5 characters are written
for( int i=0; i<bytesread; i++ )
{
fputc(buffer[i], filehandle); // this doesn't create full image
}
}
How can I dynamically allocate the 11000 bytes sent by the server?
You need to loop both the sending and receiving. Neither send() nor recv() are guaranteed to send/read as many bytes as you requested.
You also should send the file size before the file data so the receiver knows how many bytes to expect and when to stop reading.
Try something more like this:
SERVER
bool senddata(SOCKET sock, void *buf, int buflen)
{
unsigned char *pbuf = (unsigned char *) buf;
while (buflen > 0)
{
int num = send(sock, pbuf, buflen, 0);
if (num == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
// optional: use select() to check for timeout to fail the send
continue;
}
return false;
}
pbuf += num;
buflen -= num;
}
return true;
}
bool sendlong(SOCKET sock, long value)
{
value = htonl(value);
return senddata(sock, &value, sizeof(value));
}
bool sendfile(SOCKET sock, FILE *f)
{
fseek(f, 0, SEEK_END);
long filesize = ftell(f);
rewind(f);
if (filesize == EOF)
return false;
if (!sendlong(sock, filesize))
return false;
if (filesize > 0)
{
char buffer[1024];
do
{
size_t num = min(filesize, sizeof(buffer));
num = fread(buffer, 1, num, f);
if (num < 1)
return false;
if (!senddata(sock, buffer, num, 0))
return false;
filesize -= num;
}
while (filesize > 0);
}
return true;
}
FILE *filehandle = fopen("imagefile.jpg", "rb");
if (filehandle != NULL)
{
sendfile(clientsocket, filehandle);
fclose(filehandle);
}
CLIENT
bool readdata(SOCKET sock, void *buf, int buflen)
{
unsigned char *pbuf = (unsigned char *) buf;
while (buflen > 0)
{
int num = recv(sock, pbuf, buflen, 0);
if (num == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
// optional: use select() to check for timeout to fail the read
continue;
}
return false;
}
else if (num == 0)
return false;
pbuf += num;
buflen -= num;
}
return true;
}
bool readlong(SOCKET sock, long *value)
{
if (!readdata(sock, value, sizeof(value)))
return false;
*value = ntohl(*value);
return true;
}
bool readfile(SOCKET sock, FILE *f)
{
long filesize;
if (!readlong(sock, &filesize))
return false;
if (filesize > 0)
{
char buffer[1024];
do
{
int num = min(filesize, sizeof(buffer));
if (!readdata(sock, buffer, num))
return false;
int offset = 0;
do
{
size_t written = fwrite(&buffer[offset], 1, num-offset, f);
if (written < 1)
return false;
offset += written;
}
while (offset < num);
filesize -= num;
}
while (filesize > 0);
}
return true;
}
FILE *filehandle = fopen("imagefile.jpg", "wb");
if (filehandle != NULL)
{
bool ok = readfile(clientsocket, filehandle);
fclose(filehandle);
if (ok)
{
// use file as needed...
}
else
remove("imagefile.jpg");
}
We could avoid the header that contains the image size, but we just read to the end of the sent data. About the buffer size, we could use a fixed number such as 10 * 1024, when we received some data from the server, we just save it into a file according to the actual received data length.
// please open a file ...
FILE * fp;
// ...
const int LENGTH = 10 * 1024;
int len = 0;
char * buffer = (char *)malloc(LENGTH);
while ((len = recv(socket, buffer, LENGTH, 0)) > 0) {
fwrite(buffer, 1, len, fp);
}
free(buffer);
// close the file
#T.C: I guess we cannot allocate a buffer according to the size sent from the server in case the image is too large to save inside the client's memory. Not mention the server is fake, and intended to make any attack.
I'm a beginner C++/TCP programmer and I was doing a home assignment for file transfer using TCP in C++/VS2010/Windows 7.
I've created a client and a server that can listen to multiple clients on a network.
When I send a request to the server requesting a file, I receive the correct size of file, but when I send data from server to client I get garbage.
I'm pretty sure I've done a stupid mistake somewhere with casting, but can't identify where.
I am sending char* byte by byte and save them to a file on the client.
Any ideas what's wrong?
// client code:
unsigned int packlen = 0;
unsigned int flength = 0;
char* data = NULL;
if((packlen = recv(sock, (char*) &flength, sizeof(unsigned int), 0)) ==
sizeof(unsigned int))
{
flength = (unsigned int) flength;
data = new char[flength];
}
unsigned char current;
char* buffer;
unsigned int accumlength = 0;
for(unsigned int i = 0; i < flength; ++i)
{
if((packlen = recv(sock, (char*) ¤t , sizeof(unsigned int), 0))
!= sizeof(unsigned int))
{
err_sys("Receiving packet transfer error., exit");
}
data[i] = current;
}
ofstream output(argv[2], ios::binary);
output.write(data, flength);
if(data)
{
delete[] data;
data = NULL;
}
// Server code:
char* data = NULL;
unsigned long length;
string fname;
data = new char[stat_buf.st_size];
ifstream input(reqp->filename, ios::binary);
input.seekg(0, ios::end);
unsigned int length = input.tellg();
if(length != stat_buf.st_size)
{
err_sys("Problems with file size");
}
send(cs, (char*) &length, sizeof(unsigned int), 0);
Sleep(1000); // wait a bit
input.read(data, length); // read all file at once.
for(unsigned int i = 0; i < length; ++i)
{
unsigned char current = data[i];
send(cs, (char*) ¤t, sizeof(unsigned char), 0);
}
Thanks for your help.
It looks like you are discarding quite a few bytes during the read loop:
recv(sock, (char*) ¤t, sizeof(unsigned int), 0)
Where current is an unsigned char but you ask it to read sizeof(unsigned int) bytes which are more than that. So recv() will write its result somewhere else (because it will write 4 bytes in current)
You should probably write:
recv(sock, (char*) ¤t, 1, 0)
Even though that is terribly inefficient it should at least work.