1

I'm trying to upload a file to anonfile in C using wininet libraries.

According to their API, we need to send a POST HTTP request specifying the file to the following URL:

https://api.anonfile.com/upload

Additionally, I've uploaded one file with CURL to see the headers generated: https://pastebin.com/VZfhaxEy

Thus, my function looks like this one (based on this post). Note that it doesn't still handles the server response, just the sending:

#include <windows.h>
#include <wininet.h>
#include <stdio.h>

#pragma comment(lib,"Wininet.lib") //Library
#define ERROR_OPEN_FILE       10
#define ERROR_MEMORY          11
#define ERROR_SIZE            12
#define ERROR_INTERNET_OPEN   13
#define ERROR_INTERNET_CONN   14
#define ERROR_INTERNET_REQ    15
#define ERROR_INTERNET_SEND   16

int main(int argc, char* argv[])
{
    // Local variables
    char* filename = "C:\\test.zip";   //Filename to be loaded
    char* type = "binary";
    char boundary[] = "pippo";            //Header boundary
    char nameForm[] = "upload";     //Input form name
    char iaddr[] = "api.anonfile.com";        //IP address
    char url[] = "upload";         //URL

    char hdrs[1024];                  //Headers
    char* buffer;                   //Buffer containing file + headers
    char* content;                  //Buffer containing file
    FILE* pFile;                    //File pointer
    long lSize;                      //File size
    HRESULT result;

    // Open file
    fopen_s(&pFile, filename, "rb");
    if (pFile == NULL) return ERROR_OPEN_FILE;

    // obtain file size:
    fseek(pFile, 0, SEEK_END);
    lSize = ftell(pFile);
    rewind(pFile);

    // allocate memory to contain the whole file:
    content = (char*)malloc(sizeof(char) * lSize);
    if (content == NULL) return ERROR_MEMORY;

    // copy the file into the buffer:
    result = fread(content, 1, lSize, pFile);
    if (result != lSize) return ERROR_SIZE;

    // terminate
    fclose(pFile);

    //allocate memory to contain the whole file + HEADER
    buffer = (char*)malloc(sizeof(char) * lSize + 2048);

    //print header
    sprintf_s(hdrs,
        1024,
        "POST /upload HTTP/2\r\n"
        "Host: api.anonfile.com\r\n"
        "User-Agent: curl/7.60.0"
        "cache-control: no-cache\r\n"
        "content-type: multipart/form-data\r\n"
        "name=\"%s\"; filename=\"%s\"\r\n"
        "Content-Length: %i\r\n"
        "Accept: */*\r\n"
        "\n", nameForm, filename, lSize);

    //Open internet connection
    HINTERNET hSession = InternetOpen("WinSock", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    if (hSession == NULL) return ERROR_INTERNET_OPEN;

    HINTERNET hConnect = InternetConnect(hSession, iaddr, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
    if (hConnect == NULL) {
        printf("%d", GetLastError());
        return ERROR_INTERNET_CONN;
    }

    HINTERNET hRequest = HttpOpenRequest(hConnect, (const char*)"POST", url, NULL, NULL, (const char**)"*/*\0", 0, 1);
    if (hRequest == NULL) {
        printf("%d", GetLastError());
        return ERROR_INTERNET_REQ;
    } 

    BOOL sent = HttpSendRequest(hRequest, hdrs, strlen(hdrs), buffer, strlen(buffer));
    if (!sent) {
        printf("%d", GetLastError());
            return ERROR_INTERNET_SEND;
    } 

    //close any valid internet-handles
    InternetCloseHandle(hSession);
    InternetCloseHandle(hConnect);
    InternetCloseHandle(hRequest);

    return 0;
}

But somehow, it's not able to perform the connection:

123 
(process 7652) exited with code 14. (ERROR_INTERNET_CONN).

What am I missing?

Here is the only anonfile file uploading example I've found, but it uses curl and it's in C++.

mllamazares
  • 7,876
  • 17
  • 61
  • 89
  • [InternetConnect](https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetconnecta): *"Returns a valid handle to the session if the connection is successful, or NULL otherwise. **To retrieve extended error information, call GetLastError**. An application can also use InternetGetLastResponseInfo to determine why access to the service was denied."* – IInspectable Mar 07 '20 at 17:09
  • `buffer` doesn't contain anything. You want to send `content` and `lSize` (although this will not fix your main problem) – Barmak Shemirani Mar 07 '20 at 19:10

2 Answers2

2

When accessing a secure site, change to INTERNET_DEFAULT_HTTPS_PORT as mentioned by RemyLebeau.

Also use INTERNET_FLAG_SECURE in HttpOpenRequest

To upload data with "content-type: multipart/form-data", it is required to wrap the data with str_open and str_close as shown in the code below:

Note, "$$ABCXYZ$$" is some random text. Change it to something more random which does not exit in the target file.

int main()
{
    const char* str_header = "Content-Type: multipart/form-data; boundary=----$$ABCXYZ$$";
    const char* str_open = "------$$ABCXYZ$$\r\n"
        "Content-Disposition: form-data; name=\"file\"; filename=\"filename.txt\"\r\n"
        "Content-Type: application/octet-stream\r\n"
        "\r\n";
    const char *str_close = "\r\n------$$ABCXYZ$$--\r\n";

    FILE *fp = NULL;
    fopen_s(&fp, "filename.txt", "rb");
    if(!fp)
        return 0;

    fseek(fp, 0, SEEK_END);
    long filesize = ftell(fp);
    rewind(fp);
    int datalen = filesize + strlen(str_open) + strlen(str_close);
    char *data = malloc(datalen);

    strcpy_s(data, datalen, str_open);
    fread(data + strlen(str_open), 1, filesize, fp);
    memcpy(data + strlen(str_open) + filesize, str_close, strlen(str_close));

    HINTERNET hsession = NULL, hconnect = NULL, hrequest = NULL;

    hsession = InternetOpenA("myname", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    if(!hsession)
        goto cleanup;

    hconnect = InternetConnectA(hsession, "api.anonfile.com",
        INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
    if(!hconnect)
        goto cleanup;

    hrequest = HttpOpenRequestA(hconnect, "POST", "/upload",
        NULL, NULL, NULL, INTERNET_FLAG_SECURE, 0);
    if(!hrequest)
        goto cleanup;

    if(!HttpSendRequestA(hrequest, str_header, (DWORD)-1, data, datalen))
        goto cleanup;

    DWORD received;
    BYTE buf[1024];
    while(InternetReadFile(hrequest, buf, sizeof(buf), &received) && received)
    {
        buf[received] = 0;
        printf("%s\n", buf);
    }

cleanup:
    fclose(fp);
    if(hsession)InternetCloseHandle(hsession);
    if(hconnect)InternetCloseHandle(hconnect);
    if(hrequest)InternetCloseHandle(hrequest);

    return 0;
}

Edit: changed datalen, changed strlen(data) to datalen for binary file compatibility

Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • Thanks! But I get an error in InternetConnect(), the GetLastError() function retrieves the value `123`. I've just changed the path of the file in `fopen_s` and in the `filename` variable of the header. See https://pastebin.com/HreKwnN9 Any clue of what's happening? – mllamazares Mar 08 '20 at 01:14
  • 1
    Did you get any compilation warning? I was able to duplicate the 123 error by setting Unicode and supplying the wrong ANSI input. Try the modified code with ANSI `Internet***A` functions. – Barmak Shemirani Mar 08 '20 at 02:07
  • That was it! Thank you so much, mate! :) – mllamazares Mar 08 '20 at 02:16
1

Your error message implies that the code is failing on InternetConnect().

Error code 123 is ERROR_INVALID_NAME: "The filename, directory name, or volume label syntax is incorrect".

The API requires you to post via HTTPS, but you are trying to connect to HTTP port 80 rather than HTTPS port 443.

You need to change INTERNET_DEFAULT_HTTP_PORT to INTERNET_DEFAULT_HTTPS_PORT.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770