1

So I am attempting to upload files to a server from a windows 7 client using winsock. In order to do this I have setup the socket and then created an HTTP POST request that is supposed to send all of the data to a PHP file on the server which will then upload it. The code to create the request is...

#include <winsock2.h>
#include <windows.h>
#include <iostream>

using namespace std;
int main (int argc, char* argv[]){
   WSADATA wsaData;
   char* fileName = argv[1];
   WSAStartup(MAKEWORD(2,2), &wsaData);
   SOCKET Socket=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
   struct hostent *host;
   host = gethostbyname("localhost");
   SOCKADDR_IN SockAddr;
   SockAddr.sin_port=htons(80);
   SockAddr.sin_family=AF_INET;
   SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
   cout << "Connecting...\n";
   connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr);
   cout << "Connected.\n";

   char *header="POST /upload.php HTTP/1.1\r\n"
         "Host: localhost\r\n"
         "Content-Type: multipart/form-data; boundary=myboundary\r\n"
         "Connection: close\r\n"
         "\r\n--myboundary\r\n"
         "Content-Type: application/octet-stream\r\n"
         "Content-Disposition: form-data; name=\"myfile\"; filename=\"myfile.ext\"\r\n"
         "Content-Transfer-Encoding: 8bit\r\n"
         "\r\n";

    // Open the existing file.
   HANDLE hFile = CreateFile(fileName, GENERIC_READ, 0, NULL,
                             OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);

   int size = GetFileSize(hFile, NULL);

   send(Socket,header, strlen(header),0);
   //send the file to the server
   int bytesSent = 0;
   do{
      char buff[300];
      DWORD dwBytesRead;
      //Copy file into array buff
      if(!ReadFile(hFile, buff, 300, &dwBytesRead, NULL)){
         cout << GetLastError << endl;
         return 1;
      }
      bytesSent += send(Socket, buff, dwBytesRead, 0);
      cout << "Bytes Sent " << bytesSent << endl;
   }while(bytesSent < size);
   char *footer = "\r\n--myboundary--\r\n";
   send(Socket, footer, strlen(footer), 0);
   closesocket(Socket);
   WSACleanup();
   cout<<endl<<endl;
   return 0;
}

While the PHP code to receive the file is...

<?php
$target_dir = "recvFile/";
$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);
$uploadOk = 1;
$imageFileType = pathinfo($target_file,PATHINFO_EXTENSION);
// Check if file already exists
if (file_exists($target_file)) {
    echo "File already exists";
    $uploadOk = 0;
}
// Check file size
if ($_FILES["fileToUpload"]["size"] > 500000) {
    echo "File to large";
    $uploadOk = 0;
}
// Check if $uploadOk is set to 0 by an error
if ($uploadOk == 0) {
    echo "ERROR File not uploaded";
// if everything is ok, try to upload file
} else {
    if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
        echo "The file ". basename( $_FILES["fileToUpload"]["name"]). " has been uploaded.";
    } else {
        echo "ERROR uploading file";
    }
}
?>

But there is never anything within $_FILES and so the upload always fails. Did I create the HTTP request incorrectly? Do I need to specifically say that the file is after myboundary? Also if there is a resource that has examples of creating HTTP requests from scratch it would be really helpful because for some reason wireshark doesn't even see the request which makes it even harder to figure out what's going on.

PS This is my first time asking a question on StackOverflow so if there is some formatting or conduct that I messed up please let me know so I can fix it!

pchihak
  • 79
  • 1
  • 8
  • 1
    Questions seeking debugging help ("why isn't this code working?") must include the shortest code necessary to reproduce it in the question itself. Please read how to create a [MCVE], as your C++ code is not a MCVE. – tambre Oct 03 '17 at 17:31
  • 2
    Writing your own HTTP library or doing this all by hand is asking for trouble, so if your code's "not working" then you're on your own. This is why [libcurl](https://curl.haxx.se/libcurl/) and other libraries exist: They're tested, proven, and will work if used correctly. – tadman Oct 03 '17 at 17:47
  • If it's HTTP not HTTPS, you can use Fiddler2 or Wireshark or another packet sniffer to compare what you are sending against what a browser sends, to see what you are leaving out or getting wrong. – Dave S Oct 03 '17 at 17:59
  • 1
    How do you know if your server works Ok? Have you tested an upload using a browser (i.e a guaranteed working client)? – rustyx Oct 03 '17 at 18:13
  • @RusyX You were correct that there was an issue with the PHP code which I have no fixed and updated the post accordingly Thanks. – pchihak Oct 04 '17 at 16:22
  • You shoudn't close the connection when you are done sending data. Wait for the reply from the server. First the server might be aborting the request because it notices the connection is dropped, second the reply might give you information about what is wrong. – Eelke Oct 04 '17 at 18:13

1 Answers1

3

The issue that I was having was actually relatively simple to fix. First I needed to add a user-agent, second I needed to define a content-length which was the length of the ENTIRE http request (not just the data portion). My finished HTTP Request then appears as:

POST /upload.php HTTP/1.1
Host: localhost
Content-Length: 800
Content-Type: multipart/form-data; boundary=WrbKitFormBoundaryXtRo8bh7ZpnTrTwd
User-Agent: 1337
Connection: keep-alive

--WrbKitFormBoundaryXtRo8bh7ZpnTrTwd
Content-Disposition: form-data; name="fileToUpload"; filename="testMac.cpp"
Content-Type: application/octet-stream

//SEND RAW FILE DATA HERE

--WrbKitFormBoundaryXtRo8bh7ZpnTrTwd
pchihak
  • 79
  • 1
  • 8
  • 2
    Just to clarify for other people who may be trying to do something similar. Content-Length refers to only the size of the body of data and does not involve the header. So in my example Content-Length would only include any data after Connection: keep-alive. – pchihak Nov 05 '17 at 18:51