Characters are deleted by retrieving files from the simple php service using WinHttpReadData

advertisements

I have a simple php service set up on a IIS web server. It is used by my client to retrieve files from the server. It looks like this:

<?php

if (isset($_GET['file']))
{
    $filepath = "C:\\files\\" . $_GET['file'];

    if (!strpos(pathinfo($filepath, PATHINFO_DIRNAME), "..") && file_exists($filepath) && !is_dir($filepath))
    {
        set_time_limit(0);
        $fp = @fopen($filepath, "rb");

        while(!feof($fp))
        {
            print(@fread($fp, 1024*8));
            ob_flush();
            flush();
        }
    }
    else
    {
        echo "ERROR at www.testserver.com\r\n";
    }

    exit;
}

?>

I retrieve the files using WinHttp's WinHttpReadData in C++.

EDIT #2: Here is the C++ code. This is not exactly how it appears in my program. I had to pull pieces from multiple classes, but the gist should be apparent.

session = WinHttpOpen(appName.c_str(), WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);

if (session) connection = WinHttpConnect(session, hostName.c_str(), INTERNET_DEFAULT_HTTP_PORT, 0);

if (connection) request = WinHttpOpenRequest(connection, NULL, requestString.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);

bool results = false;

if (request)
{
    results = (WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0) != FALSE);
}

if (results)
{
    results = (WinHttpReceiveResponse(request, NULL) != FALSE);
}

DWORD bytesCopied = 0;
DWORD size = 0;

if (results)
{
    do {
        results = (WinHttpQueryDataAvailable(request, &size) != FALSE);

        if (results)
        {
            // More available data?
            if (size > 0)
            {
                // Read the Data.
                size = min(bufferSize, size);

                ZeroMemory(buffer, size);

                results = (WinHttpReadData(request, (LPVOID)buffer, size, &bytesCopied) != FALSE);
            }
        }

        if (bytesCopied > 0 && !SharedShutDown.GetValue())
        {
            tempFile.write((PCHAR)RequestBuffer, bytesCopied);

            if (tempFile.fail())
            {
                tempFile.close();
                return false;
            }

            fileBytes += bytesCopied;
        }
    } while (bytesCopied > 0 && !SharedShutDown.GetValue());
}

Everything works fine when I test (thousands of files) over the local network using the server computer name from either a Windows 7 or Windows 10 machine. It also works fine when I access the service over the internet from a Windows 7 machine. However, when I run the client on a Windows 10 machine accessing over the internet, I get dropped characters. The interesting thing is that it is a specific set of characters that gets dropped every time from XML files. (Other, binary, files are affected as well, but I have not yet determined what changes in them.)

If the XML file contains an element starting with "<Style", that text disappears. So, this:

<Element1>blah blah</Element1>
<Style_Element>hoopa hoopa</Style_Element>
<Element2>bip bop bam</Element2>

becomes this:

<Element1>blah blah</Element1>
_Element>hoopa hoopa</Style_Element>
<Element2>bip bop bam</Element2>

Notice that the beginning of the style element is chopped off. This is the only element that is affected, and it seems to only affect the first one if there are more than one in the file.

What perplexes me is why this doesn't happen running the client from Windows 7.

EDIT: Some of the other files, binary and text, are missing from 1 to 3 characters each. It seems that a drop only happens once in a file. The rest of the contents of the file are identical to the source.


I can't make sense of the above read routine, it is also incomplete. Just keep it simple like the example below.

The fact that you are having problems with binary files suggest you are not opening the output tempFile in binary mode.

std::ofstream tempFile(filename, std::ios::binary);

while(WinHttpQueryDataAvailable(request, &size) && size)
{
    std::string buf(size, 0);
    WinHttpReadData(request, &buf[0], size, &bytesCopied);
    tempFile.write(buf.data(), bytesCopied);
}

Your php file can be simplified as follows:

<?php
readfile('whatever.bin');
?>