Hi

I'm looking to upload files using http post using winsock and have a php script handle it. I've got it working fine for text files but with files that contain null characters such as exes it doesn't work. It is only copying the file buffer up to the null char into the http request. I'm not sure the correct way to do this. Help very much appreciated

Regards

Ricky

My code:

int HTTP_POST(char* UploadURL, char * PHP_Script)
{
		 WSADATA wsa;
		 struct hostent *remoteHost;

		 SOCKET sock;
		 struct sockaddr_in addr;
		 unsigned long win=0;
		 int Content_Length;
		 char Post_Request[998576];//998576
		 char Response[8576]; //2018
		 int iResult;
		 DWORD dwError;

		 //file handle
		 HANDLE hFile, hMapFile;
		 //something to contain the number of bytes read
		 DWORD dwNumRead = 0;
		 //a boolean test variable, to test for success of reads
		 BOOL bTest;
		 //a buffer… can actually be of any type


		 //Opening an existing file for reading:

                 char* file = "C:\\test.exe";
		 hFile = CreateFile(file, GENERIC_READ,FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
		 /* if there is a problem opening the file, a call to
		 GetLastError will tell you what it is */

		 //GetFileSize to determine size of buffer for ReadFile
	     DWORD BufferSize = GetFileSize(hFile,NULL);

		 cout << BufferSize <<endl;
	
		char* ReadBuffer = new char [BufferSize];// create a new char with the filesize to hold the buffer of file

		 
		 if(!ReadFile(hFile, ReadBuffer,BufferSize,&dwNumRead,NULL))
		 {
			 printf ("ReadFile Fail");
		 }
		 /* bTest will be TRUE if the read is successful.
		 If false, take a look at GetLastError */

		 //To close the file:

		 CloseHandle(hFile);

		// cout << hex <<ReadBuffer;
	   //  Sleep(5000);

		 string to_send = ReadBuffer;


		char*RemoteFileName = "test.exe";

		 string name = RemoteFileName;

		 //Initialise winsock

		 iResult = WSAStartup(MAKEWORD(2,2), &wsa);
		 if(iResult != 0)
		 {
			printf("WSAStartup failed with error: %d\n", iResult);
			WSACleanup();
			return 1;
		 }


		 remoteHost = gethostbyname(UploadURL);
		 if(remoteHost == NULL)
		 {
			 dwError = WSAGetLastError();
			 if (dwError != 0)
			 {
				 if(dwError == WSAHOST_NOT_FOUND) 
				 {
				     printf ("Host %s not found.\n", UploadURL);
					 WSACleanup();
					 Sleep(10000);
					 return 2;
				 }
				 else if (dwError == WSANO_DATA) 
				 {
					 printf ("No data record found.\n");
					 Sleep(10000);
					 return 2;
				 }
				 else
				 {
					 printf("Function failed with error: %ld\n", dwError);
					 Sleep(10000);
					 return 2;
				 }
			 }
		 }
		 else
		 {
			 printf ("Successfully connected to host: %s.\n", UploadURL);
			 Sleep(5000);
		 }

		 win = *(unsigned long*) remoteHost->h_addr;

		    // The sockaddr_in structure specifies the address family,
            // IP address, and port of the server to be connected to.

		 addr.sin_family=AF_INET;
		 addr.sin_port=htons(80);
		 addr.sin_addr.s_addr = win;

		 // Create a SOCKET for connecting to server
		 sock = socket(AF_INET, SOCK_STREAM, 0);
		 if( sock == INVALID_SOCKET)
		 {
			   printf("Server: Error at socket(), error code: %ld\n", WSAGetLastError());

          // Clean up
		  WSACleanup();
		  // and exit with error
		  return 2;
		 }

		 Content_Length = (to_send.size()+name.size()+287);


		 //HTTP POST Post_Request, constructed using a packet sniffer
		 sprintf(Post_Request, "POST %s HTTP/1.1\r\n", PHP_Script);
		 sprintf(Post_Request, "%sHost: %s\r\n", Post_Request, UploadURL);
		 sprintf(Post_Request, "%sContent-Type: multipart/form-data; boundary=---------------------------297771262821979\r\n", Post_Request);
		 sprintf(Post_Request, "%sContent-Length: %d\r\n", Post_Request, Content_Length);
		 sprintf(Post_Request, "%s\r\n", Post_Request);//blank line under content length
		 sprintf(Post_Request, "%s-----------------------------297771262821979\r\n", Post_Request);
		 sprintf(Post_Request, "%sContent-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n", Post_Request, name.c_str());
		 sprintf(Post_Request, "%sContent-Type: application/octet-stream\r\n", Post_Request);
		 sprintf(Post_Request, "%s\r\n", Post_Request);
		 sprintf(Post_Request, "%s%s\r\n", Post_Request, ReadBuffer);
		 sprintf(Post_Request, "%s-----------------------------297771262821979\r\n", Post_Request);
		 sprintf(Post_Request, "%sContent-Disposition: form-data; name=\"submit\"\r\n", Post_Request);
		 sprintf(Post_Request, "%s\r\n", Post_Request);
		 sprintf(Post_Request, "%sSubmit\r\n\r\n", Post_Request);
		 sprintf(Post_Request, "%s-----------------------------297771262821979--\r\n\r\n\0", Post_Request);      

		 //cout << Post_Request ;

		 // Connect to server.
		 iResult = connect(sock, (SOCKADDR*)&addr, sizeof(addr));
		 if(iResult == SOCKET_ERROR)
		 {
			 printf("connect failed with error: %d\n", WSAGetLastError());
			 closesocket(sock);
			 WSACleanup();
			 return 2;
		 }

		 // Send buffer
	 	iResult = send(sock, Post_Request, strlen(Post_Request), 0);

		 if(iResult == SOCKET_ERROR) 
		 {
			 printf("send() failed with error: %d\n", WSAGetLastError());
			 closesocket(sock);
			 WSACleanup();
			 return 2;
		 }
		 else
		 {
			  printf("Bytes sent: %d\n", iResult);
		 }

		 while (iResult > 0 )
		 {
			 iResult = recv(sock, Response, strlen(Response), 0);

			 if ( iResult > 0 )
			 {
				 printf("Bytes received: %d\n", iResult);
				printf("%s", Response);
				 Sleep(5000);
			 }
			 else if (iResult == 0)
			 {
				 printf("Connection closed\n");
				
				 	 Sleep(5000);
			 }
			 else
			 {
				 printf("recv failed with error: %d\n", WSAGetLastError());
				 	 Sleep(5000);
			 }
			//

		 }     

		 // cleanup

		 delete ReadBuffer;
		 closesocket(sock);
		 WSACleanup();
		 //cout << win << endl;
		 //cin.get();
		 return EXIT_SUCCESS;
	 }

Recommended Answers

All 15 Replies

It is only copying the file buffer up to the null char into the http request.

sprintf(Post_Request, "%s%s\r\n", Post_Request, ReadBuffer);

Sure. What else would you expect from "%s"?

Sure. What else would you expect from "%s"?

Of course it terminates at the null character, but what is the correct way of doing this?

memcpy() comes to mind..

The buffer holding the contents of the file to be uploaded needs encoding. It's not Base64 although I don't know if I could use that instead.

Here's some of the output of the exe file from a packet sniffer when I upload using a html form and Firefox.

MZ......................@.............................................	.!..L.!This program cannot be run in DOS mode. $.........2...\A..\A..\A...A..\A...A..\A...A..\A...A..\A...A..\A..]A..\A...A..\A...A..\ARich..\A........................PE..L...4K.K..............
..............`.......0....@.......................................@..................................P..<....@..............................................................................................................   .    .0..........................@....rsrc........@......................@....idata  .....P......................@...sodsaw .@...`...F
.................

The null chars are being converted to full stops. I can't determine what encoding this is. What kind of encoding is this and how can I do this in c++?

A big thank you to somebody who can help.

The null chars are being converted to full stops.

Somehow I doubt that. Are you sure that it's not just the way the packets sniffer displays the data? It is quite common to display unprintable bytes as '.'.

char Post_Request[998576];//998576
char Response[8576]; //2018

I don't believe that putting over 1 million bytes onto the stack is a good idea. generally speaking best practice is to keep the data on the stack relatively small.

The method you are using to create your request with all those sprintfs lacks something too. It isn't terribly efficient because you keep copying lots of the data over and over, for example "POST %s HTTP/1.1\r\n" gets copied 14 times for 1 request.

I would have thought that using a std::vector<char> to hold the response, and use a std::string or std::stringstream to create the headers in would be better. You can then use the std::copy algorithm or std::vector::insert to copy each header into the response a single time and the same method can be used to copy your ReadBuffer into the vector too.

vector<char> Post_Request;
ostringstream ss;
string text;

ss << "POST " << PHP_Script << " HTTP/1.1\r\n";  // Don't use endl HTTP requires specific end line characters
text = ss.str();
Post_Request.insert(Post_Request.end(), text.begin(), text.end());

...

Post_Request.insert(Post_Request.end(), ReadBuffer, &ReadBuffer[BufferSize]);

That still invlodes copying "POST" 3 times but it only copies Readbuffer once instead of 5 times.

Also it will automatically allocate data to hold everything (although you could make things more efficient by setting the vectors capacity before you use it based on BufferSize and the size of various other string parameters. And it puts that data on the heap rather than the stack which for large temporary buffers is probably a better place to put them.

Somehow I doubt that. Are you sure that it's not just the way the packets sniffer displays the data? It is quite common to display unprintable bytes as '.'.

I must be mistaken, this is why I couldn't find out what encoding it was.

char Post_Request[998576];//998576
char Response[8576]; //2018

I don't believe that putting over 1 million bytes onto the stack is a good idea. generally speaking best practice is to keep the data on the stack relatively small.

The method you are using to create your request with all those sprintfs lacks something too. It isn't terribly efficient because you keep copying lots of the data over and over, for example "POST %s HTTP/1.1\r\n" gets copied 14 times for 1 request.

I would have thought that using a std::vector<char> to hold the response, and use a std::string or std::stringstream to create the headers in would be better. You can then use the std::copy algorithm or std::vector::insert to copy each header into the response a single time and the same method can be used to copy your ReadBuffer into the vector too.

vector<char> Post_Request;
ostringstream ss;
string text;

ss << "POST " << PHP_Script << " HTTP/1.1\r\n";  // Don't use endl HTTP requires specific end line characters
text = ss.str();
Post_Request.insert(Post_Request.end(), text.begin(), text.end());

...

Post_Request.insert(Post_Request.end(), ReadBuffer, &ReadBuffer[BufferSize]);

That still invlodes copying "POST" 3 times but it only copies Readbuffer once instead of 5 times.

Also it will automatically allocate data to hold everything (although you could make things more efficient by setting the vectors capacity before you use it based on BufferSize and the size of various other string parameters. And it puts that data on the heap rather than the stack which for large temporary buffers is probably a better place to put them.

I must admit, I didn't think that allocating those high stacks was a very intuitive idea. I wanted to do it dynamically as you have suggested. I also thought that repeating sprintf was poor. I'll have a go at doing it with vectors and will get back to you.

Thanks for your help.

I've recoded it using string.append and copying the file into a vector but it's still not reading past the null characters. Advice appreciated.

You need to stop treating it as a string!

It is not a string it is binary data and since '\0' is seen as a terminator in strings but is just another value that commonly occurs in binary data you will continue having trouble until you do.

You need to stop treating it as a string!

It is not a string it is binary data and since '\0' is seen as a terminator in strings but is just another value that commonly occurs in binary data you will continue having trouble until you do.

How should I tread it then?

As an array or vector of bytes

Here's my current code for reading the file into a vector but how do I then append the vector into the "Post_Request" string? Also, this way I assume it will neglect any null characters in the file to be uploaded?

void GetFileContents(string filename, vector<char> *contents) 
	 {
		 ifstream file;
		 file.open(filename.c_str(), ios::binary);

		 if (file.is_open()) 
		 {
			 file.seekg(0, ios::end);
			 int length = file.tellg();
			 contents->resize(length);
			 if (length != 0) 
			 {
				 file.seekg(0, ios::beg);
				 file.read(&((*contents)[0]), length);
			 }
			 file.close();
		 } else 
		 {
			contents->clear();
		 }
	 }

....

vector<char> contents;
                GetFileContents(File, &contents);

		 string Post_Request;

		 //HTTP POST Post_Request, constructed using a packet sniffer
		 		
		 Post_Request = "POST " + string(PHP_Script) +" HTTP/1.1\r\n"
			 		 "Host: " + string(UploadURL) + "\r\n"
					 "Content-Type: multipart/form-data; boundary=---------------------------297771262821979\r\n"
					 "Content-Length: 40334\r\n\r\n"
					 "-----------------------------297771262821979\r\n"
					 "Content-Disposition: form-data; name=\"file\"; filename=\"" + name + "\r\n"
					 "Content-Type: application/octet-stream\r\n\r\n" + contents  //doesn't work lol
+ 
					 "\r\n-----------------------------297771262821979\r\n"
					 "Content-Disposition: form-data; name=\"submit\"\r\n\r\n"
					 "Submit\r\n\r\n"
					 "-----------------------------297771262821979--\r\n\r\n\0";

...

Just extract the bytes from the string and then append the file contents. One way to do that is to create another vector and copy the header and contents there:

#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
#include <iomanip>

void HexDump( char ch ) { std::cout << std::setw(2) << std::setfill('0') << (short)ch << " "; }

int main()
{
	// ---- Prepare POST header and file data

	char randomBytes[] = { 0x67, 0x0, 0x45, 0x3, 0x0 , 0x3, 0x65, 0x32 };
	std::vector< char > vFileData( randomBytes, randomBytes + _countof(randomBytes) );

	std::string PHP_Script	= "/";

	std::string Post_Request =
		"POST " + std::string( PHP_Script ) + " HTTP/1.1\r\n"; // etc., etc.

	// ---- Allocate a buffer large enough for the whole request
	std::vector< char > vRequestBytes( Post_Request.size() + vFileData.size() );

	// ---- Copy header into final buffer
	std::vector< char >::iterator it = std::copy( Post_Request.begin(), Post_Request.end(), vRequestBytes.begin() );

	// ---- Copy content into final buffer
	std::copy( vFileData.begin(), vFileData.end(), it );

	// ---- Dump buffer
	std::cout << std::hex << std::setiosflags( std::ios_base::uppercase );
	std::for_each( vRequestBytes.begin(), vRequestBytes.end(), HexDump );
	std::cout << std::endl;

	return 0;
}

If you don't want to create another buffer, you can assemble the header into a char array(vector), get the size of the contents file, allocate a buffer large enough for the array and the file, then copy the header in the beginning of the buffer and fread the file data immediately after it.

Generally, you should avoid using std::string for assembling a HTTP request in favor of simple memory copies, unless you need some special string processing.

hi am looking for c++ file upload via socket
Ricky65, could you share the full code please??!

just i want simple exmple to upload txt file like this

thnx

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.