libcurl - "curl_easy_cleanup()" is causing malloc error (C++) - c++

When running my code (pertinent sections pasted below), I periodically get the following error:
program(34010,0x70000e58b000) malloc: *** error for object
0x7fc43d93fcf0: pointer being freed was not allocated set a breakpoint
in malloc_error_break to debug Signal: SIGABRT (signal SIGABRT)
I am running multi-threaded C++ code on a Macbook (OS-10.13) wherein different threads make use of the code in question simultaneously. To my knowledge, libcurl is indeed thread safe as long as I do not utilize the same "curl handle" (which I understand to be an instance of "CURL" aka "CURL *curl = curl_easy_init();") in two different threads at the same time. In my case, since each thread calls the function separately and initializes a new instance of a CURL object, I should be "safe", right? Hopefully there is something obvious that I'm missing that is causing me (or lib curl in this case) to attempt to free memory that has already been freed. If there is any more information I should have included (below) please don't hesitate to let me know.
The function that seg faults is
string http_lib::make_get_request(string url)
on the line that reads
curl_easy_cleanup(curl);
and sometimes (less often) on the line that reads
res = curl_easy_perform(curl);
Below is what I think would be the pertinent sections of my code:
size_t http_lib::CurlWrite_CallbackFunc_StdString(void *contents, size_t size, size_t nmemb, std::string *s)
{
size_t newLength = size*nmemb;
size_t oldLength = s->size();
try
{
s->resize(oldLength + newLength);
}
catch(std::bad_alloc &e)
{
//handle memory problem
return 0;
}
std::copy((char*)contents,(char*)contents+newLength,s->begin()+oldLength);
return size*nmemb;
}
string http_lib::make_post_request(string url, vector<string> headers, string post_params) {
CURL *curl;
CURLcode res;
curl = curl_easy_init();
string s;
if(curl)
{
struct curl_slist *chunk = NULL;
for(int i=0; i<headers.size(); i++){
/* Add a custom header */
chunk = curl_slist_append(chunk, headers[i].c_str());
}
/* set our custom set of headers */
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_params.c_str());
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); //only for https
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); //only for https
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
if(networking_debug){
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //verbose output
}
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
/* always cleanup */
curl_easy_cleanup(curl);
}
// Debug output
if (networking_debug){
cout<<"Response: " << s <<endl;
}
return s;
}
string http_lib::make_get_request(string url) {
//SslCurlWrapper sslObject;
CURL *curl;
CURLcode res;
curl = curl_easy_init();
string s;
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
//tell libcurl to follow redirection
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); //only for https
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); //only for https
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
if(networking_debug){
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //verbose output
}
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
if (networking_debug){
cout << "Response: " << s << endl;
}
return s;
}
In main() I have
int main(int argc, char *argv[]){
// Initialize http_lib (curl)
curl_global_init(CURL_GLOBAL_DEFAULT);
... spin up 10 or so threads that make get/post requests to https site (some requests utilize the make_post_request() function and others utilize make_get_requet() function).
}

CMAKE doesn't/didn't seem to want to use anything other than CURL_ROOT_DIR of "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/include" for libcurl (aka curl).
Thus it was using the curl lib that mac (and/or Xcode) ships with. I haven't figured out what version that is, but I can say that not using it and instead using CURL version 7.57 is what fixed my issue.
I used "brew" package manager to
brew install curl
Doing so created /usr/local/Cellar/curl/7.57.0 directory and put all libs/includes in there.
Then I added
-I/usr/local/Cellar/curl/7.57.0/include -L/usr/local/Cellar/curl/7.57.0/lib
to my CMAKE CMAKE_CXX_FLAGS.
TLDR; Solution was to ensure I was using the newest version of the curl lib. Now that I am, no problem.

Related

Creating a shared link for a Dropbox file using curl & C++

I want to create a shared link for a dropbox file using curl & C++ on a windows 10 desktop.
I've already manage to upload the file to a dropbox folder using curl & C++.
When I try to create the link with command line it works with
curl -X POST https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings --header "Authorization: Bearer <TOKEN>" --header "Content-Type: application/json" --data "{\"path\":\"path_of_the_file\"}"
but when I use this code to do the same in C++ it hangs at < HTTP/1.1 100 Continue
Here is my code :
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
string readBuffer;
printf("Running curl test get shared link.\n");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE); //no ssl
struct curl_slist *headers = NULL; // init to NULL is important
headers = curl_slist_append(headers, "Authorization: Bearer <TOKEN>");
headers = curl_slist_append(headers, "Content-Type: ");
headers = curl_slist_append(headers, "Dropbox-API-Arg: {\"path\":\"path_of_file\"}");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POST, true);
curl_easy_setopt(curl, CURLOPT_VERBOSE, true);
curl_easy_setopt(curl, CURLOPT_URL, "https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings");
// Perform the request, res will get the return code
res = curl_easy_perform(curl);
// Check for errors
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
// always cleanup
curl_easy_cleanup(curl);
cout << readBuffer << endl;
printf("\nFinished curl test.\n");
}
curl_global_cleanup();
printf("Done get shared link!\n");
I've tried with content-type : application/json and adding fields but I can't reproduce what I'm doing with the command line
There are errors in the code, causing undefined behaviour.
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_easy_setopt(curl, CURLOPT_POST, true);
curl_easy_setopt(curl, CURLOPT_VERBOSE, true);
cUrl has a C API, C does not have overloads. Thus curl_easy_setopt is a multi-arg function, and the third argument type depends on the second argument value. CURLOPT_SSL_VERIFYPEER and CURLOPT_VERBOSE require long values, you pass the values as int. The size of the third argument is important exactly like this is in *printf functions. The proper calls must be
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
The second. 100 Continue means you have not passed the data for the POST request. The server received request and waits for further data. See
libcurl example - http-post.c.
I can't reproduce what I'm doing with the command line
Add the --libcurl to the command line for getting a C code performing the same actions as in the command line.
Thank you S.M. Here is the code that works
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
string readBuffer;
printf("Running curl test get shared link.\n");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); //no ssl
struct curl_slist *headers = NULL; // init to NULL is important
headers = curl_slist_append(headers, "Authorization: Bearer <TOKEN>");
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"path\":\"path_to_the_file"}");
curl_easy_setopt(curl, CURLOPT_URL, "https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings");
// Perform the request, res will get the return code
res = curl_easy_perform(curl);
// Check for errors
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
// always cleanup
curl_easy_cleanup(curl);
cout << readBuffer << endl;
printf("\nFinished curl test.\n");
}
curl_global_cleanup();
printf("Done get shared link!\n");

How to login on a page and read url content from it?

I have a problem.
I wanted to login e.g. in ogame and read the url source code from it.
But everytime I start my code, my file returns the login page and not the second page after login.
#include <stdio.h>
#include <curl/curl.h>
FILE *fptr;
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
fptr = fopen("ogametest.html", "w");
fputs((const char*)contents, (FILE*)fptr);
fclose (fptr);
//printf("%s", (char *) contents);
return size * nmemb;
}
int main(int argc, char **argv){
CURL *curl;
CURLcode res;
char strlist[16000]="";
char* str= &strlist[0];
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://de.ogame.gameforge.com/");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "usernameLogin=myLogin&passwordLogin=myPassw&serverLogin=s146-de.ogame.gameforge.com");
res = curl_easy_perform(curl);
//curl_easy_reset(curl);
curl_easy_setopt(curl, CURLOPT_URL, "https://s146-de.ogame.gameforge.com/game/index.php?page=overview&relogin=1");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, str);
res = curl_easy_perform(curl);
fptr = fopen("ogametest.html", "r");
fgets(str, 16000, fptr);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
It would be fine if you can help me.
Have a good day
There are several issues with your code.
The url is wrong for login. The right url is "https://de.ogame.gameforge.com/main/login"
The post message is not the right format.
You need to handle the cookies. The cookies are used to save the login session. Without the cookies you'll be redirected to main page everytime you make any request at ogame.
Here is some curl code that works to login ogame
CURL* curl;
//Local initializing of curl object
curl = curl_easy_init();
//Defining parameters
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //print html header request/responses in console
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE);//Follow redirects
curl_easy_setopt(curl, CURLOPT_URL, "https://de.ogame.gameforge.com/main/login");//Url
curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "cookie.txt");//Save cookies here
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "cookie.txt");//Load cookies here
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "kid=&login=my%40email.here&pass=mypassword&uni=s148-de.ogame.gameforge.com");//Post message
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback);//html code response function
curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30");//Setting agent. Just do this
//Executing curl object
curl_easy_perform(curl);
where
string htmlcode;
size_t writeCallback(char* buf, size_t size, size_t nmemb, void* up){
for (int c = 0; c<size*nmemb; c++)
{
htmldata.push_back(buf[c]);
}
return size*nmemb;}
is globally defined. So in this code the html code will be store in the string htmlcode.
Now the "my%40email.here" should be changed to your email. The %40 is #. "mypassword" should be changed to your password. The "s148-de.ogame.gameforge.com" depends the universe you play on. 148 is Virgo.
I don't know what you're trying to do, but I suggest you use a program to intercept the html requests/responses. In chrome you can go to DevTools Overview (shortcut f12). Under network you can track the exchange between the browser and server, which you can then try to mimic.

Using CURL in function in c++

I'm trying tu use curl in c++ using function.
Example:
#define ...
...
...
/* CURL parameters */
CURL *curl = curl_easy_init();
CURLcode res;
string readBuffer;
void setHeader(){
if(curl) {
/* Headers */
struct curl_slist *chunk = NULL;
chunk = NULL;
chunk = curl_slist_append(chunk, "Connection:keep-alive");
..
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
}
}
void myFunction1(){
setHeader();
curl_easy_setopt(curl, CURLOPT_URL, "....");
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookiePath);
curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookiePath);
res = curl_easy_perform(curl);
cout << readBuffer;
curl_easy_cleanup(curl);
}
void myFunction2(){
setHeader();
curl_easy_setopt(curl, CURLOPT_URL, "....");
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookiePath);
curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookiePath);
res = curl_easy_perform(curl);
cout << readBuffer;
curl_easy_cleanup(curl);
}
In the main function i call myFunction1 or myFunction2;
I don't know if is right to use libcurl in this way, but I'm having a problem.
I use this to login in my site, so I save the cookie whit this code:
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookiePath);
curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookiePath);
int main(){
curl_global_init(CURL_GLOBAL_ALL);
myFunction1();
myFunction2();
curl_global_cleanup();
}
Suppose i login and save the cookie in myFunction1.
When I'm trying to set in the myFunction2 it crash on the CURLOPT_COOKIEFILE row.
I don't know why the cookie is saved in the file, but it can't be used beacuse when I run the program it crash on that row.
Sorry for my English
Thank's
The reason of my problem is that when I use the curl_easy_cleanup(curl) and then curl_easy_init I change the sessionID, so the cookie that I've saved are no longer good. So I use at the first curl_easy_init, then I make all my request in my functions, and at the end of my program I use curl_easy_cleanup. To clean the CURL OPTION you can use: curl_easy_reset(). I've update this post for people that can have my same problem.

curl_easy_getinfo undefined symbol error

I am using asterisk version 13.5.0. In utils directory, I created a new module called curl_util with source code as below:
#include "asterisk/curl_utils.h"
HttpResponse * httpResponseNew()
{
HttpResponse * response = calloc(1, sizeof(HttpResponse));
return response;
error:
return NULL;
}
HttpResponse * httpRequestPost(char *url, char *post_body, size_t size)
{
struct MemoryStruct chunk;
CURL *curl = curl_easy_init();
chunk.memory = malloc(1);
chunk.size = 0;
HttpResponse *response = httpResponseNew();
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_body);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, size);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HttpResponseMemoryWriter);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
response->res = curl_easy_perform(curl);
if (response->res != CURLE_OK)
{
response->OK = 0;
}
else
{
response->OK = 1;
response->size = chunk.size;
response->body = chunk.memory;
}
curl_easy_cleanup(curl);
return response;
error:
free(chunk.memory);
if (curl) curl_easy_cleanup(curl);
return response;
}
In chan_sip.c, I call above mentioned httpRequestPost function.
Executing make and make install are OK.
But, when starting dahdi and asterisk, I got the following error:
Error loading module chan_sip.so: undefined symbol: httpRequestPost
loader.c: module chan_sip.so could not be loaded.
Could you please give hints or advices to fix this error?
Thank you in advance.
Doesn't need to edit the makefile. You just need to use the ASTLDFLAGS compiler option like :
./configure
make ASTLDFLAGS="-lcurl"
make install
make samples
make config
I fixed the error by doing followings:
Remove libcurl4-openssl-dev
Download CURL from http://curl.haxx.se/, unzip, run commands ./configure, make, make install
Edit Makefile of asterisk, adding -lcurl for compile flags.

Curl - checking connection

I used Curl 7.2.9 and checked connection this way:
Here's example:
curl = curl_easy_init();
bool result = false;
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, m_checkConnectionUrl);
CURLcode res = curl_easy_perform(curl);
}
if(res != CURLE_OK)
{
}
else
{
// connection is available
}
Now I switched to curl-7.33.0 and got *CURLE_WRITE_ERROR* error,
and to make it work I must code it like
std::string output;
char* encodedUrl = curl_easy_escape(curl, m_checkConnectionUrl, 0);
curl_easy_setopt(curl, CURLOPT_POST, 0);
curl_easy_setopt(curl, CURLOPT_URL, encodedUrl);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeMemoryCurlCallbackStub);
CURLcode res = curl_easy_perform(curl);
But I don't need to write anything. Any ideas?
Manily the Curl option *CURLOPT_WRITEFUNCTION* is used to have a certain amount of data periodically(at the callback functoin) to handle a large file download. I don't see any reason to use this with your curl purpose, regardless the version.
Remove the *CURLOPT_POST*(by default its 0) and *CURLOPT_WRITEFUNCTION* from the code and it should work. If it doesn't, then you are doing something wrong at other places in your code!
Also, if you are checking whether the URL is ok or not, then using CURL is ok. But to only check for connection, you can only check whether the port 80 of the domain is on or not.
You need to write a writecallback as well
size_t CurlWriteCallback(char* buf, size_t size, size_t nmemb, void* up)
{
TRACE("CURL - Response received:\n%s", buf);
TRACE("CURL - Response handled %d bytes:\n%s", size*nmemb);
// tell curl how many bytes we handled
return size*nmemb;
}
if(curl)
{
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlWriteCallback);
curl_easy_setopt(curl, CURLOPT_URL, m_checkConnectionUrl);
CURLcode res = curl_easy_perform(curl);
}
Old question, but I have just encountered a similar problem. After some more googling this is the solution:
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L);
curl_easy_perform(curl);
// OK, now we are connected (if nothing bad happened),
// but it would be nice to communicate with the server:
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 0L);
//now we can do the actual communication
I used this to separate authentication from the actual emails sending.