libcurl in C++: converting URL to some weird symbols - c++

I have noticed that libcurl for C++ changes the URL provided to some weird symbols. Here is the code:
curl_global_init(CURL_GLOBAL_ALL);
curl_handle = curl_easy_init();
cout << "http://subdomain.mydomain.com/folder/check.php?key=" + key << endl;
curl_easy_setopt(curl_handle, CURLOPT_URL, "http://subdomain.mydomain.com/folder/check.php?key=" + addon_key);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, &writeCallback);
curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl_handle);
That's what I get in the console:
http://subdomain.mydomain.com/folder/check.php?key=tasdasm34234k23l423m4234mn23n4jk23bjk4b23nasdasdasdasdsdsd
* Rebuilt URL to: � ��g/
* IDN support not present, can't parse Unicode domains
* getaddrinfo(3) failed for � ��g:80
* Couldn't resolve host '� ��g'
* Closing connection 0
This code works perfectly fine when I build my project in Windows, but when I build it with Linux, this happens. If I just try to access "http://subdomain.mydomain.com/folder/check.php" with this code, it works, but as soon as I add the key, libcurl changes the whole URL.
Thanks in advance.

Like I said in my comment, the CURL library is a library of C functions, and C functions doesn't know anything about objects or classes from C++.
When you do "http://subdomain.mydomain.com/folder/check.php?key=" + addon_key the result is a (temporary) std::string object. Passing that to a C function will not work well, and I'm surprised that the compiler actually let you pass that argument without complaining. It should have been a compiler error I think, or at the very least should give you a stern warning.
You can solve this by creating another variable to store the string object, and the use the c_str member function to get a C-style string (a pointer to constant char):
std::string url = "http://subdomain.mydomain.com/folder/check.php?key=" + addon_key;
curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
I don't know if cURL copies the string, or if you need to keep the url variable alive until you're all done.
That it apparently work on Windows is nothing more than pure luck. Passing a C++ object to a function that does not expect it is undefined behavior.

Related

libcurl, how to use string as a password?

Following this example:
https://github.com/curl/curl/blob/master/docs/examples/smtp-tls.c
I managed to get it to work by setting the password as such:
curl_easy_setopt(curl, CURLOPT_PASSWORD, "password");
However, I want to set the password up as a string, i.e.:
string myPassword = "password";
curl_easy_setopt(curl, CURLOPT_PASSWORD, myPassword);
When I do this however, I get an error
curl_easy_perform() failed: Login denied
I double checked and have the program print the values of the original password and the string before it sends. They are exactly the same. I have two functions to test this on. They are almost entirely identical except that one uses a hardcoded password and the other uses the string. The hard coded one works fine, but the one that uses a string is having login issues. How could I fix this?
The CURLOPT_PASSWORD option expects a char*, not a std::string. You are passing the wrong type. The library is interpreting your data in the "wrong" way and getting nonsense.
This is not diagnosed because curl_easy_setopt uses varargs to take arbitrary parameters of varying types, depending upon the option being set. (Ideally your compiler would have rejected or at least warned about the program, but that's the cost of working with type-unsafe C libraries!)
You can pass the C-string version of your string using std::string::c_str(), like this:
curl_easy_setopt(curl, CURLOPT_PASSWORD, myPassword.c_str());
There exists a project named curlpp which purports to be a type-safe wrapper around libcurl, and this might be worth a look.
It fails because curl_easy_setopt() is expecting a C style string, (i.e. char *).
What you should do in this case is convert the std::string to a C style string using .c_str().
So you should pass password by doing:
std::string myPassword = "password";
curl_easy_setopt(curl, CURLOPT_PASSWORD, myPassword.c_str());

Pthreads & Libcurl - Segmentation Fault when using Authorization header

I have been tearing my hair out for the past 3 days, trying to figure out why my C++ code outputs a "Segmentation fault". Being new to C++ didn't help either. Nor did using pthreads help with narrowing down with the problem (If you aren't able to replicate this, try it with pthreads I guess)
But now, I believe I finally have my fingers on one of the reasons why. Here's the code:
static void* pull_one_url(void* arguments)
{
struct example_struct args = *((struct example_struct *)arguments);
CURL *curl;
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, ("https://example.com/" + (args).appendData).c_str());
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 30000L);
// Set Headers
struct curl_slist *header = NULL;
header = curl_slist_append(header, ("Authorization: Bearer " + (args).myBearerToken).c_str());
header = curl_slist_append(header, "User-Agent: MyCPPApp");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
CURLcode res = curl_easy_perform(curl);
cout << "Request sent." << endl;
return NULL;
}
Running that code in my case will throw a "Segmentation Error" and crash the app. Using any authorization bearer token seems to do that.
My question is - how do I bypass the segmentation fault? No doubt about it, my app will encounter bad auth tokens and I do NOT want it crashing because of that, especially considering that it's multithreaded and needs to continue it's work.
This may be happening because of the way I'm passing the token down from the casted pthread arguments, but I am at a loss when it comes to finding another way to do it, so if that's the case, kindly show me the right direction.
Oh, and by the way, if you know of a way to safely and easily print the output to the console right after the request in the thread finishes (it's multithreaded, without waiting for all threads to die), then please help me with that too. I am not 100% certain the code I am using right now is safe from these dreaded segmentation faults that seem to be haunting me, so I have excluded it from my testing and this post.

C++ libcurl headers - not recognised by server

I'm working in C++ with libcurl writing a program to interact with some APIs and I'm stuck when it comes to adding authentication info in the headers. I am new to libcurl and APIs with a basic knowledge of C++. Authentication requires an API key and a nonce hashed with HMAC_SHA256, each of which is then placed in the headers. A very simple JSON message is then sent. I've tried searching through this site but most examples seem to be in javascript or command line, and I don't see any relevant answers in them.
When I send my POST message to the server, I get a response 402 - Invalid ApiKey. My API key is 100% correct so I suspect it's something to do with the formatting or the way I've included it in the header. The site is BlinkTrade and their documentation is here, which gives some info about the header requirements.
Code snippet below:
char* message="{\"MsgType\": \"U2\",\"BalanceReqID\": 1}";
curl_easy_setopt(curl, CURLOPT_URL, "https://api.blinktrade.com/tapi/v1/message");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, pFile2);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, message);
struct curl_slist *header = NULL;
header = curl_slist_append(header, "APIKey:0000000000000000000000000000000000000000");
header = curl_slist_append(header, "Nonce:1");
header = curl_slist_append(header, "Signature:1");
header = curl_slist_append(header, "Content-Type:application/json");
transfer = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
cout << transfer << endl;
transfer = curl_easy_perform(curl);
cout << transfer << endl;
And I get the return code 0 for curl_easy_setopt and curl_easy_perform. I've swapped the actual characters from the API key for a load of 0's, but otherwise everything is the same in terms of formatting etc. I've not actually used a hashed signature, I'll do that after I can sort out this error. I've tried adding a space after the colon and enclosing string and/or value in double quotes but I get the same response. What am I doing wrong that means my headers aren't actually recognised by the server?
Solved: the Blinktrade server returns "Invalid APIKey" not when your API key is incorrect, but when the signature is incorrect. A rather annoying mislabelling.

How to pass variable with URL to libcurl

I have this line:
curl_easy_setopt(curl, CURLOPT_URL, "https://bk-bestgamer.ru/get.php?get=hello");
it works fine. i send request and receive a line that contains text "hello" from my site.
but when i do like this
std::string url;
std::cin >> url;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());`
after that, when i use
curl_easy_perform(curl);`
i have an error
"protocol https not supported or disabled in libcurl".
i tried to do following
char url[] = "https://bk-bestgamer.ru/get.php?get=hello";
curl_easy_setopt(curl, CURLOPT_URL, url);`
it doesn't work with the same error.
i really do not know what to do.
i found some recommendations there
URL Variable passing into Curl
you can see, i tried that, but it doesn't succeed.
i've tried to compile the same code in ubuntu, where i have libcurl with ssl. but it doesn't work with the same error. from linux command line it works correctly.

How do I tell if libcurl has processed my SSL files correctly

I am trying to login to betfair via the betfair api using curl. I have already succeeded in doing this via the command line version of curl from a bash script but I now want to do this from my C++ code directly with libcurl. Sadly libcurl is not supported by betfair so there is no direct documentation - but if command line curl works then it should all be doable.
My libcurl is failing straight away during an attempt to login (which should get a "session token" as a response). I tell curl about my certificate and key with the lines:
curl_easy_setopt(curl, CURLOPT_SSLCERT,"client-2048.crt");
curl_easy_setopt(curl, CURLOPT_SSLKEY,"client-2048.key");
Later on I call curl_easy_perform(curl); I get a response of:
{"loginStatus":"CERT_AUTH_REQUIRED"}
According to betfair documentation this means: "Certificate required or certificate present but could not authenticate with it"
So I guess that somehow the SSL authentication failed. As an experiment I tried deliberatly putting garbage certificate and key file names (e.g. "client-2048_XXX.crt") but saw no difference in the response from betfair or any of the curl diagnostics (set up via curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); and curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);). So I have no way of knowing for sure the libcurl has processed my cert and key files properly. Indeed I am even suspecting that perhaps it is not processing them at all - perhaps I'm missing some other option along the lines of CURLOPT_PLEASE_USE_SSL_AUTHENTICATION which is resulting in the files being ignored?
EDIT: So for the record, the full sequence looks like this (some text hidden):
curl_easy_setopt(curl, CURLOPT_URL, "https://identitysso.betfair.com/api/certlogin");
curl_easy_setopt(curl, CURLOPT_SSLCERT,"client-2048.crt");
curl_easy_setopt(curl, CURLOPT_SSLKEY,"client-2048.key");
curl_easy_setopt(curl, CURLOPT_POST, 1);
struct curl_slist *chunk = NULL;
chunk = curl_slist_append(chunk, "X-Application: _HIDDEN_");
chunk = curl_slist_append(chunk,"Content-Type: application/x-www-form-urlencoded");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS,"username=_HIDDEN_&password=_HIDDEN_");
curl_easy_perform(curl);
EDIT: I thought that perhaps I somehow compiled my libcurl without SSL. To check this I called curl_version_info, which amongst other things gives you:
char *ssl_version; /* human readable string */
Printing this out gave the string "WinSSL" which I assume means that SSL is included.
EDIT: FYI, I have four "client-2048" files, a .crt, .pem, .key and .csr. They all sit on the same directory as my exe file. I also print out the current working directory (as reported by _getcwd()) at run time just do double check that my exe is running from the right location.
EDIT: Here is my working login.sh (please excuse it's inelegance, I was hacking with cut and paste code and scarcely knew what I was doing). getlog.exe was a tiny utility I wrote to do some string manipulation on some JSON.
#!/bin/bash
# uses rescript
APP_KEY=_HIDDEN_
#SESSION_TOKEN=$2
HOST=https://api.betfair.com/exchange/betting
AUTHURL=https://identitysso.betfair.com/api/certlogin
CURL=curl
function bflogin()
{
echo "bflogin()"
OUT=$($CURL -s -k --cert client-2048.crt --key client-2048.key --data "username=_HIDDEN_&password=_HIDDEN_" -H "X-Application: $APP_KEY" -H "Content-Type: application/x-www-form-urlencoded" $AUTHURL)
echo $OUT
SESSION_TOKEN=$(echo $OUT | getlog.exe)
echo
echo "Here -> "$SESSION_TOKEN
echo $SESSION_TOKEN > st.txt
}
Double check the paths to you crt and key files.
Also, since you haven't specified a key encoding type, PEM is the default. You can change the encoding type with the CURLOPT_SSLCERTTYPE option, or try converting your keys to PEM.