libCurl : curl_easy_setopt in one method and curl_easy_perform in another does not work - libcurl

I have a code, where, in one local function I use curl_easy_setopt to set the proxy URL. And in another local function I call curl_easy_perform. But when te control moves from one function to another, the proxy url set using local variable contains junk characters and the DNS query returns an error. The libcurl help page says that when we do setopt the string values is copied by the curl library. But I feel the library just referes to that value whenever it needs it. It doesn't copy the string. So if local variable is used to set proxy url, it will contain junk by the time I call curl_easy_perform.
Following is the example code snippet.
void funcSetOpt
{
char ProxyUrl[] = "someproxy";
curl_easy_setopt(curlHandle, CURLOPT_PROXY, ProxyUrl);
}
void funcPerform
{
curl_easy_perform(curlHandle);
}

That would imply that you're using a fairly old libcurl version and the following section from the curl_easy_setopt man page might affect you:
Before version 7.17.0, strings were not copied. Instead the user was
forced keep them available until libcurl no longer needed them.

Related

Joining a variable inside a JSON formatted post request C++

So I have this put request which submits to a service running on localhost.
Doing it the way below works just fine, note I have replaced the actual acc name with ACC_NAME and password with ACC_PASSWORD.
curl_easy_setopt(curl_account_login, CURLOPT_POSTFIELDS, "{\r\n \"username\": \ACC_NAME\,\r\n \"password\": \"ACC_PASSWORD\,\r\n \"persistLogin\": false\r\n}\r\n");
However when I wanted to pass in a variable containing the acc_name and acc_password, it does not work, I get an error response from server.
The below request is using the variable joined inside the JSON string, which gives me the error response.
curl_easy_setopt(curl_account_login, CURLOPT_POSTFIELDS, "{\r\n \"username\": \""+acc_name+"\,\r\n \"password\": \""+acc_password+"\,\r\n \"persistLogin\": false\r\n}\r\n");
I can't figure out what I am doing wrong when I am joining the string variable into the request.
It works just fine by plain, if I write the account credentials directly into the request and not in a variable.
Regards
C-style string literals (like "something") are of type const char [] (this is a null-terminated character array), which decays into a const char* (a pointer). Thus, + (as in "something" + "another") is just adding two addresses, resulting in an invalid pointer value. You cannot concatenate C-style strings by simply using + that way.
Assuming you are using C++ (not C), as indicated by your question being tagged c++, I suggest C++ string objects instead, this will allow easy concatenation.
Also, there's a note in curl documentation about CURLOPT_POSTFIELDS, mentioning that the data pointed to is not copied, thus requiring you to make sure the pointer remains valid until the associated transfer finishes. Because of that, I would prefer using CURLOPT_COPYPOSTFIELDS instead.
To sum up, do something like this:
#include <string>
// ...
std::string postfields
{
std::string{ "{" }
+ R"("username":")" + ACC_NAME
+ R"(","password":")" + ACC_PASSWORD
+ R"(","persistLogin":false})"
};
curl_easy_setopt(curl_account_login, CURLOPT_COPYPOSTFIELDS, postfields.data());

libcurl outputting extra trailing bytes when downloading file

I'm having a bit of a strange problem with libcurl. Essentially, while downloading a file from an HTTP server, it's outputting some garbage bytes at the end of the file. Whereas the file should be 1,710,017 bytes, the library instead writes 1,712,128, i.e. 2,111 more. I suspect it's some sort of buffering issue, as the latter number is a multiple of 2^12 (and 2^13, but it conforms to multiples of 2^12 in other cases). The extra data is either a respective number of bytes read from another part of the file (it only seems to read from one of 4 addresses each time, all towards the end), or in one case the byte CD repeated 2,111 times.
Relevant code:
std::string url; // defined elsewhere
FILE* data; // initialized elsewhere with option "wb"
CURL* query = curl_easy_init();
curl_easy_setopt(query, CURLOPT_WRITEDATA, data);
curl_easy_setopt(query, CURLOPT_URL, url);
curl_easy_setopt(query, CURLOPT_FOLLOWLOCATION, true);
curl_easy_setopt(query, CURLOPT_SSL_VERIFYPEER, false);
CURLcode res = curl_easy_perform(query);
Also: the same issue occurs when using a simple write callback, and the issue occurs with any given remote server, not just this particular one.
Edit #1: I can only replicate this on Windows (tested on two machines with the same library files). It works on Debian.
Edit #2: It also occurs when libcurl is built on my laptop. To provide additional context, I am building from Marc Hörsken's ZIP (available from the official curl downloads page) using a VC14 environment on Windows 10.
I'm chalking this up to some weird VC bug or otherwise anomalous behavior. Rewriting the problem code to use an fstream instead of a FILE did the trick.

Reset curl headers with C++

I have this code
struct curl_slist *pCURL_List = NULL;
pCURL_List = curl_slist_append(pCURL_List, "Content-type: application/x-amf");
curl_easy_setopt(m_pCURL, CURLOPT_HTTPHEADER, pCURL_List);
curl_easy_perform(m_pCURL);
and right after curl_easy_perform(m_pCURL); I need to reset the headers (the first part of the code). If I do curl_slist_free_all(pCURL_List); the next curl_easy_perform(m_pCURL); crashes the program.
I sure have the other parameters, the program is quite big and everything works EXCEPT calls after these lines.
Also, when I said that I need to reset the headers, I mean that I'd like curl to set back the old default values of Content-type. If I do
pCURL_List = curl_slist_append(pCURL_List, "Content-type:");
afaik it will delete the "Content-type" header.
For the crash, did you set CURLOPT_HTTPHEADER back to null before calling curl_easy_perform the second time? I'm thinking the list itself was freed but the CURL handle still has a pointer to the now invalid memory.

Cannot Send Image File (image/jpg) Using Winsock WSABUF

I'm stuck and I need help.
I'm trying to write the correct code for sending back an image file so the web browser can render it. It can send back text/html just fine, but image/* is not working.
You can see the code and the URL is shown below.
https://github.com/MagnusTiberius/iocphttpd/blob/master/iocphttpl/SocketCompletionPortServer.cpp
What the browser is receiving is just a few bytes of image data.
I tried vector, std::string and const char* to set the values of WSABUF, but still the same few bytes are sent over.
Please let know what is the missing piece to make this one work.
Thanks in advance.
Here's your problem:
PerIoData->LPBuffer = _strdup(str.c_str());
The _strdup function only copies up until the first null, so it cannot be used to copy binary data. Consider using malloc and memcpy if you don't want to use the C++ library.
The alternate implementation (in the false branch) is also incorrect, because it saves the data in an object (vc) that goes out of scope before the I/O is completed. You could instead do something like
vector<char> * vc = new vector<char>;

CInternetSession::OpenURL exception if headers are defined. Works fine if not. Why?

Trying to log the application version details in our weblogs using the headers:
Should be a one liner..but for some reason whenever I specify anything but NULL for the headers it causes an exception (InternetOpenUrl() call returns NULL) - anyone able to explain why?
CInternetSession internet;
CHttpFile * pHttpFile;
CString headers;// = APPLICATION_SUITE_NAME;
DWORD dwHeadersLength = -1;
headers.Format("%s %s %s\n",APPLICATION_SUITE_NAME,SHORT_APPLICATION_VERSION,BUILDNO_STR);
pHttpFile =(CHttpFile *) internet.OpenURL(lpszURL, 1, INTERNET_FLAG_TRANSFER_ASCII|INTERNET_FLAG_DONT_CACHE, headers, dwHeadersLength);
Without the headers, dwHeadersLength parameter (eg. pass in NULL,-1) then it goes through fine and I see the request come through to our website. But why does it fail if I pass in custom headers?
Does your CString resolve to CStringA or CStringW? If the latter (i.e. wide-char), here's a bit from MSDN (http://msdn.microsoft.com/en-us/library/aa384247%28VS.85%29.aspx):
If
dwHeadersLength is -1L and lpszHeaders
is not NULL, the following will
happen: If HttpSendRequestA is called,
the function assumes that lpszHeaders
is zero-terminated (ASCIIZ), and the
length is calculated. If
HttpSendRequestW is called, the
function fails with
ERROR_INVALID_PARAMETER.
I'm mentioning HttpSendRequest() because, in fact, CInternetSession::OpenURL() calls InternetOpenUrl(), whose documentation for the lpszHeaders parameters sends you to the doc page for HttpSendRequest().
EDIT: Another possibility is that the call fails because the headers do not seem to be in canonical format: according to the HTTP 1.1 spec, they ought to be in "name: value" format, and each header should be separated from the next one by CRLF.