When file is sent via SMTP it lose some bytes. c++ - c++

i'm trying to send email via SMTP, i send multipart mail, with text part and application/octet-stream part. when i try to send not *.txt" files, for example .jpg or .docx it is got corrupted and some bytes are lost. For example, when i try to send file 123.docx, size of this file is 166 020 bytes. i recieve file in my email, but it has only 166 006 and i can't open it. Variable "total" shows correct number of bytes is sent.
My code is bellow:
#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
using namespace std;
int fileSize(char fileName[])
{
std::streampos fsize = 0;
std::ifstream myfile (fileName, ios::in); // File is of type const char*
fsize = myfile.tellg(); // The file pointer is currently at the beginning
myfile.seekg(0, ios::end); // Place the file pointer at the end of file
fsize = myfile.tellg() - fsize;
myfile.close();
cout << "size is: " << fsize << " bytes.\n";
return fsize;
}
int main() {
char text[2048];
//connecting to smtp server
//sending DATA
strcpy(text,"DATA\n");
send(s,text,strlen(text),0);
recv(s,text,sizeof(text),0);
cout<<"recv - "<<text<<endl;
//FROM field
strcpy(text,"FROM: laboratory4_mm#rambler.ru\n");
send(s,text,strlen(text),0);
//TO field
strcpy(text,"TO: ");
strcat(text,reciever);
strcat(text,"\n");
send(s,text,strlen(text),0);
// SUBJECT field
char subject[2048];
cout<<"Enter the theme of the letter"<<endl;
cin.getline(subject,2048);
strcpy(text,"SUBJECT: ");
strcat(text,subject);
strcat(text,"\n");
send(s,text,strlen(text),0);
// delimeter of multipart message
strcpy(text,"Content-Type: multipart/mixed; boundary=\"---nsabnqeaSA43ds2\"\n");
send(s,text,strlen(text),0);
//Text part
strcpy(text,"-----nsabnqeaSA43ds2\nContent-Type: text/plain; charset=utf8\nContent-Transfer-Encoding: 8bit\n\n");
send(s,text,strlen(text),0);
cout<<"Enter the text:"<<endl;
cin.getline(text,2048);
send(s,text,strlen(text),0);
//File part
char fileName[256];
cout<<"Enter file name: ";
cin.getline(fileName,255);
int size = fileSize(fileName);
char fileLength[1024];
itoa(size,fileLength,10);
cout<<fileLength<<endl;
strcpy(text,"\n-----nsabnqeaSA43ds2 \nContent-Type: application/octet-stream\nContent-Length: ");
strcat(text,fileLength);
strcat(text,"\nContent-Transfer-Encoding: binary\nContent-Disposition: attachment;\n\n");
send(s,text,strlen(text),0);
ifstream fin;
fin.open(fileName,ios::binary);
char *buf = new char[1024];
int readBytes;
int total =0;
while((readBytes = fin.read(buf,1024).gcount())>0) {
int sent= send(s,buf,readBytes,0);
total+=sent;
delete buf;
buf = new char[1024];
}
fin.close();
delete buf;
cout<< total<<endl;
strcpy(text,"\n-----nsabnqeaSA43ds2--\n");
send(s,text,strlen(text),0);
// telling that DATA is over
strcpy(text,"\n.\n");
send(s,text,strlen(text),0);
fout<<text;
recv(s,text,sizeof(text),0);
cout<<"recv - "<<text<<endl;
//Disconnecting from server
return 0;
}

There are several fundamental bugs in the MIME encoding, here:
First, a blank line is supposed to separate headers from content. The blank line is missing:
strcpy(text,"Content-Type: multipart/mixed; boundary=\"---nsabnqeaSA43ds2\"\n");
One newline generated here.
strcpy(text,"-----nsabnqeaSA43ds2\nContent-Type: text/plain; charset=utf8\nContent-Transfer-Encoding: 8bit\n\n");
Mail content begins here, without a preceding blank line.
Additionally, the newline that precedes a boundary delimiter is a logical part of the boundary delimiter, so that newline is missing as well. See MIME documentation for more information.
strcpy(text,"\n-----nsabnqeaSA43ds2 \nContent-Type: application/octet-stream\nContent-Length: ");
Note that here you are explicitly sending a newline before the boundary delimiter, so you must be aware of this requirement.
Secondly:
while((readBytes = fin.read(buf,1024).gcount())>0) {
int sent= send(s,buf,readBytes,0);
Sending the contents of the binary file, as is? That's not going to fly. Even though it may or may not be correct MIME encoding, SMTP is still a plain text-based transmission protocol. There is an extension for transmission of binary data, but the shown code does not use it.
As is, this is the SMTP version of undefined behavior. No guaranteed results. If you need to attach this file reliably, you must base64-encode it.

Related

Load shellcode from file to char* comes strange characters in end of text

I have a char array[] and is like following:
// MessageBox
char xcode[] = "\x31\xc9\x64\x8b\x41\x30\x8b\x40\xc\x8b\x70\x14\xad\x96\xad\x8b\x58\x10\x8b\x53\x3c\x1\xda\x8b\x52\x78\x1\xda\x8b\x72\x20\x1\xde\x31\xc9\x41\xad\x1\xd8\x81\x38\x47\x65\x74\x50\x75\xf4\x81\x78\x4\x72\x6f\x63\x41\x75\xeb\x81\x78\x8\x64\x64\x72\x65\x75\xe2\x8b\x72\x24\x1\xde\x66\x8b\xc\x4e\x49\x8b\x72\x1c\x1\xde\x8b\x14\x8e\x1\xda\x31\xc9\x53\x52\x51\x68\x61\x72\x79\x41\x68\x4c\x69\x62\x72\x68\x4c\x6f\x61\x64\x54\x53\xff\xd2\x83\xc4\xc\x59\x50\x51\x66\xb9\x6c\x6c\x51\x68\x33\x32\x2e\x64\x68\x75\x73\x65\x72\x54\xff\xd0\x83\xc4\x10\x8b\x54\x24\x4\xb9\x6f\x78\x41\x0\x51\x68\x61\x67\x65\x42\x68\x4d\x65\x73\x73\x54\x50\xff\xd2\x83\xc4\x10\x68\x61\x62\x63\x64\x83\x6c\x24\x3\x64\x89\xe6\x31\xc9\x51\x56\x56\x51\xff\xd0";
Then i had inserted all this content of variable above into a file (file with UTF-8 format and content without the "") and tried load this way:
ifstream infile;
infile.open("shellcode.bin", std::ios::in | std::ios::binary);
infile.seekg(0, std::ios::end);
size_t file_size_in_byte = infile.tellg();
char* xcode = (char*)malloc(sizeof(char) * file_size_in_byte);
infile.seekg(0, std::ios::beg);
infile.read(xcode, file_size_in_byte);
printf("%s\n", xcode); // << prints content of xcode after load from file
if (infile.eof()) {
size_t bytes_really_read = infile.gcount();
}
else if (infile.fail()) {
}
infile.close();
I'm seeing some strange characters in end of text see:
What is need to fix it?
The issue is that the printf format specifier "%s" requires that the string is null-terminated. In your case, the null-terminator just happens to be after those characters you're seeing, but nothing guarantees where the null is unless you put one there.
Since you're using C++, one way to print the characters is to use the write() function available for streams:
#include <iostream>
//...
std::cout.write(xcode, file_size_in_bytes);
The overall point is this -- if you have a character array that is not null-terminated and contains data, you must either:
Put the null in the right place before using the array in functions that look for the null-terminator or
Use functions that state how many characters to process from the character array.
The answer above uses item 2.

Remove string from read() function - socket programming [duplicate]

i want to know, is there a possibility to find out where in the response Stream the header ends?
The background of the question is as following, i am using sockets in c to get content from a website, the content is encoded in gzip. I would like to read the content directly from stream and encode the gzip content with zlib. But how do i know the gzip content started and the http header is finished.
I roughly tried two ways which are giving me some, in my opinion, strange results. First, i read in the whole stream, and print it out in terminal, my http header ends with "\r\n\r\n" like i expected, but the secound time, i just retrieve the response once to get the header and then read the content with while loop, here the header ends without "\r\n\r\n".
Why? And which way is the right way to read in the content?
I'll just give you the code so you could see how i'm getting the response from server.
//first way (gives rnrn)
char *output, *output_header, *output_content, **output_result;
size_t size;
FILE *stream;
stream = open_memstream (&output, &size);
char BUF[BUFSIZ];
while(recv(socket_desc, BUF, (BUFSIZ - 1), 0) > 0)
{
fprintf (stream, "%s", BUF);
}
fflush(stream);
fclose(stream);
output_result = str_split(output, "\r\n\r\n");
output_header = output_result[0];
output_content = output_result[1];
printf("Header:\n%s\n", output_header);
printf("Content:\n%s\n", output_content);
.
//second way (doesnt give rnrn)
char *content, *output_header;
size_t size;
FILE *stream;
stream = open_memstream (&content, &size);
char BUF[BUFSIZ];
if((recv(socket_desc, BUF, (BUFSIZ - 1), 0) > 0)
{
output_header = BUF;
}
while(recv(socket_desc, BUF, (BUFSIZ - 1), 0) > 0)
{
fprintf (stream, "%s", BUF); //i would just use this as input stream to zlib
}
fflush(stream);
fclose(stream);
printf("Header:\n%s\n", output_header);
printf("Content:\n%s\n", content);
Both give the same result printing them to terminal, but the secound one should print out some more breaks, at least i expect, because they get lost splitting the string.
I am new to c, so i might just oversee some easy stuff.
You are calling recv() in a loop until the socket disconnects or fails (and writing the received data to your stream the wrong way), storing all of the raw data into your char* buffer. That is not the correct way to read an HTTP response, especially if HTTP keep-alives are used (in which case no disconnect will occur at the end of the response). You must follow the rules outlined in RFC 2616. Namely:
Read until the "\r\n\r\n" sequence is encountered. This terminates the response headers. Do not read any more bytes past that yet.
Analyze the received headers, per the rules in RFC 2616 Section 4.4. They tell you the actual format of the remaining response data.
Read the remaining data, if any, per the format discovered in #2.
Check the received headers for the presence of a Connection: close header if the response is using HTTP 1.1, or the lack of a Connection: keep-alive header if the response is using HTTP 0.9 or 1.0. If detected, close your end of the socket connection because the server is closing its end. Otherwise, keep the connection open and re-use it for subsequent requests (unless you are done using the connection, in which case do close it).
Process the received data as needed.
In short, you need to do something more like this instead (pseudo code):
string headers[];
byte data[];
string statusLine = read a CRLF-delimited line;
int statusCode = extract from status line;
string responseVersion = extract from status line;
do
{
string header = read a CRLF-delimited line;
if (header == "") break;
add header to headers list;
}
while (true);
if ( !((statusCode in [1xx, 204, 304]) || (request was "HEAD")) )
{
if (headers["Transfer-Encoding"] ends with "chunked")
{
do
{
string chunk = read a CRLF delimited line;
int chunkSize = extract from chunk line;
if (chunkSize == 0) break;
read exactly chunkSize number of bytes into data storage;
read and discard until a CRLF has been read;
}
while (true);
do
{
string header = read a CRLF-delimited line;
if (header == "") break;
add header to headers list;
}
while (true);
}
else if (headers["Content-Length"] is present)
{
read exactly Content-Length number of bytes into data storage;
}
else if (headers["Content-Type"] begins with "multipart/")
{
string boundary = extract from Content-Type header;
read into data storage until terminating boundary has been read;
}
else
{
read bytes into data storage until disconnected;
}
}
if (!disconnected)
{
if (responseVersion == "HTTP/1.1")
{
if (headers["Connection"] == "close")
close connection;
}
else
{
if (headers["Connection"] != "keep-alive")
close connection;
}
}
check statusCode for errors;
process data contents, per info in headers list;

Serving images in C++ server

I have a C++ linux server, with basic server sockets. Here's what i'm using to server png images:
string ms = "HTTP/1.0 200 OK\r\nContent-type: image/png\r\n\r\n";
ifstream myfile("xampl.png", ios::in|ios::binary|ios::ate);
string line;
char* memblock;
streampos size;
if(myfile.is_open()){
size = myfile.tellg();
memblock = new char [size];
myfile.seekg (0, ios::beg);
myfile.read (memblock, size);
myfile.close();
}
ms.append(string(memblock));
cout << "\"" << ms << "\"" << endl;
char* msg = new char[ms.size()+1];
copy(ms.begin(), ms.end(), msg);
msg[ms.size()] = '\0';
int len;
ssize_t bytes_sent;
len = strlen(msg);
bytes_sent = send(new_sd, msg, len, 0);
I know i'm trying to read the png file as a binary file, but i have no idea what else to do. When i telnet to this server, i get a response with weird characters which make me believe that i have served the file, but when i check it out in my browser, i get the image not found icon, in all browsers. Please help...
OK, let's start with the equivalent PHP code to get rid of the mess it is to do the same thing in C++
// send required headers as plain text
header("Content-type: image/png");
// read the image as a binary block
$img_data = file_get_contents("xample.png");
// send it
echo $img_data;
C++ equivalent:
string headers = "HTTP/1.0 200 OK\r\nContent-type: image/png\r\n\r\n";
send (new_sd, headers.data(), headers.length(), 0);
ifstream f("xampl.png", ios::in|ios::binary|ios::ate);
if(!f.is_open()) error ("bloody file is nowhere to be found. Call the cops");
streampos size = f.tellg();
char* image = new char [size];
f.seekg (0, ios::beg);
f.read (image, size);
f.close();
send (new_sd, image, size, 0);
Converting memblock to a string is not going to work if it has embedded null characters, which it almost certainly does, and you don't pass the length. I'm not sure why you're doing all that faffing about with char*s and std::strings, but once you have your memblock and size, use them for send. If you want to prefix the response string, just send it first.

Read a binary file and print its content (Web Server)

i am working on a web server
and i have created a function which sends the file and the http headers
when accessing the http://127.0.0.1:8080 page
the problem is that its not copyig the file correctly.. its throwing some grabage at the end.. (i am trying to pass a binary files thats why im using std::ios::binary)
example.bin
My Example bin file
:)
Downloaded file:
My Example bin file
:)ýýýýÝÝÝÝÝÝÝhjß/ÝÝ
my code:
// download file http headers
message_ = "HTTP/1.0 200 OK\r\n"
"Cache-Control: public\r\n"
"Content-Description: File Transfer\r\n"
"Content-Disposition: attachment; filename=example.bin\r\n"
"Content-Type: application/zip\r\n"
"Content-Transfer-Encoding: binary\r\n\r\n";
std::filebuf *pbuf;
std::ifstream sourcestr;
long size;
char * buffer;
sourcestr.open("example.bin",std::ios::in | std::ios::binary);
pbuf=sourcestr.rdbuf();
size=pbuf->pubseekoff (0,std::ios::end,std::ios::in);
pbuf->pubseekpos (0,std::ios::in);
buffer=new char[size];
// get file data
pbuf->sgetn (buffer,size);
message_ += buffer;
I think the problem is that buffer is not null-terminated after the 'pbuf->sgetn()' call. Try:
// EDIT: this won't work for reason stated by #Joachim in his answer.
buffer = new char[size + 1];
pbuf->sgetn(buffer, size);
*(buffer + size) = 0;
message_ += buffer;
If message_ is a std::string an alternative without NULL termination would be:
message_.append(buffer, size);
Hope that helps.
The big problem is that you are appending the contents of a binary buffer to a string. A string in C++ is terminated by the character '\0', which means that if the binary file contains that character in the middle, the "string" will be terminated. And if the binary file does not contain any '\0' character, appending it to a string will append memory until the terminator is found, which is the reason you get extra garbage. The line message_ += buffer simply means to add the memory pointed to by buffer until string terminator is found.
You must send a file in two parts: First the header, then the file data.

How to send a file with http with c++

I want to write a server side code. It should work with popular browsers and wget. My server check that file exists or not, if exists then browser can download it. But I have some problems.
Honestly, I read lots of question-answer (for example: Send binary file in HTTP response using C sockets) but I didn't find out. My browser (Chrome) can get text. But I cannot send any binary data or images etc. I am changing header according to downloading files. But I cannot send a downloadable files yet.
I have some questions.
void *clientWorker(void * acceptSocket) {
int newSocket = (int) acceptSocket;
char okStatus[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n"
"Content-Length: 20\r\n"
"\r\n"
"s";
writeLn(newSocket, okStatus);
const char * fileName = "/home/tyra/Desktop/example.txt";
sendF(newSocket, fileName);
}
1- If I wouldn't write "s" or something else inokStatus, my message cannot send. I don't understand anything of this.
This is writeLn function :
void writeLn(int acceptSocket, const char * buffer) {
int n = write(acceptSocket, buffer, strlen(buffer) - 1);
if (n < 0) {
error("Error while writing");
}
}
This is sendF function :
string buffer;
string line;
ifstream myfile(fileName);
struct stat filestatus;
stat(fileName, &filestatus);
int fsize = filestatus.st_size;
if (myfile.is_open()) {
while (myfile.good()) {
getline(myfile, line);
buffer.append(line);
}
cout << buffer << endl;
}
writeLn(acceptSocket, buffer.c_str());
cout << fsize << " bytes\n";
A little messy. I haven't used file size yet. If I send a file, then I rearrange these things.
2- I can send text and browser demonstrates it but browser didn't understand new lines.
If text file contains (123\n456\n789), browser demonstrates (123456789). I think I should change Content-Type header, but I didn't find out.
I don't want that browser demonstrates text files. Browser should download it. How can I send downloadable files?
Sorry, I explain everything pretty complicated.
As to your first question, you should find out the exact size of the file and specify it in your "Content-Length: xxxx\r\n" header. And, of course, you should ensure that the data is sent completely out.
Indeed, in your writeF function you use a std::string as a buffer:
string buffer;
this is not appropriate for binary data. You should allocate a raw char array of the right size:
int fsize = file.tellg();
char* buffer = new char[fsize];
file.seekg (0, ios::beg);
file.read (buffer, size);
file.close();
As to the second point, when your data is not HTML, specify as Content-Type: text/plain;
otherwise, your carriage return should be represented by <br> instead of "\r\n".
In case of binary downloads, to have the data download as a file (and not shown in the browser), you should specify
Content-Type: application/octet-stream
The issue is strlen here. strlen terminates when it gets a '\0' character. In binary file you will have a number of '\0' characters.
While reading the file you should find out the file size. This size should be used in int n = write(acceptSocket, buffer, strlen(buffer) - 1); in place of strlen
Change the writeLn(acceptSocket, buffer.c_str()); to writeLn(acceptSocket, buffer.c_str(), buffer.size()); and try...
For the case of 123\n456\n789 you need to send <PRE>123\n456\n789</PRE> as browser will parse this text as html and not like the OS parses and shows the output. The other way you can do is replace all \n with <BR> ...
Regarding question 1 - if you don't want to send any content back then remove the s from the end of okStatus and specify Content-Length: 0\r\n in the header