so I have a class that makes an http call using curl in visual studios 2017 that was installed via vcpkg discussed: here, using curl_easy function calls:
string returnResponseAsString(string requestURL) {
CURL *curl_handle;
CURLcode res;
struct MemoryStruct chunk;
chunk.memory = (char *)malloc(1);
chunk.size = 0;
curl_global_init(CURL_GLOBAL_ALL);
/* init the curl session */
curl_handle = curl_easy_init();
/*Turn off SSL Verifcation*/
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, FALSE);
/* specify URL to get */
curl_easy_setopt(curl_handle, CURLOPT_URL, requestURL.c_str());
/* send all data to this function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
/* we pass our 'chunk' struct to the callback function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
/* some servers don't like requests that are made without a user-agent*/
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
/* get it! */
res = curl_easy_perform(curl_handle);
/* check for errors */
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
else {
printf("%lu bytes retrieved\n", (long)chunk.size);
}
string response = chunk.memory;
/* cleanup curl stuff */
curl_easy_cleanup(curl_handle);
free(chunk.memory);
/* we're done with libcurl, so clean it up */
curl_global_cleanup();
return response;
}
If I dont include this line curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, FALSE); that turns off SSL verification, I get the error: curl_easy_perform() failed: Peer certificate cannot be authenticated with given CA certificates. I tried to install the certificates as per these instructions: link, but is says to place the downloaded certs in the same folder as the curl.exe. As far as I can tell there is no curl.exe installed by the vcpkg. I looked for .crt and I have an image of the found certs under vcpkg. Where Should I place the .crt file for authentication to work with visual studios?
Related
`#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, "http://google.com");
/* example.com is redirected, so we tell libcurl to follow redirection */
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
/* 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);
}
return 0;
}`
i have linked libcurl.a file in the project and added inlude folder from the curl-7.87.0_1-win64-mingw folder, i have also copied the include in C:\Program Files (x86)\Embarcadero\Dev-Cpp\TDM-GCC-64\x86_64-w64-mingw32 folder and copied the libraries too (as suggested in a previous answer to a similar question here) but still it isnt working, the error im seeing is [Error] curl/curl.h: No such file or directory.
this is the code im trying to run
I have searched here, but did not find anything that matched. What I have tested with curl.exe and that works fine. I then tried a small test program. Created a commandline Win32 project in VS2017 and copied in the code from simplessl.c. Problem is that no matter what certificates I try I always get CURLE_SSL_CERTPROBLEM. This points to the client certificate being wrong. I even tried with the 2048 bit certificate and key from here: https://fm4dd.com/openssl/certexamples.shtm Not sure how to troubleshoot.
The test program I used:
#include "pch.h"
#include <iostream>
#include <curl/curl.h>
int main()
{
std::cout << "Hello World!\n";
CURL *curl;
CURLcode res;
FILE *headerfile;
const char *pPassphrase = NULL;
static const char *pCertFile = "C:\\Prog\\TestCurl\\2048b-rsa-example-cert.pem";
static const char *pCACertFile = "cacert.pem";
static const char *pHeaderFile = "C:\\Prog\\TestCurl\\requestWith.txt";
const char *pKeyName;
const char *pKeyType;
const char *pEngine;
#ifdef USE_ENGINE
pKeyName = "rsa_test";
pKeyType = "ENG";
pEngine = "chil"; /* for nChiper HSM... */
#else
pKeyName = "C:\\Prog\\TestCurl\\2048b-rsa-example-keypair.pem";
pKeyType = "PEM";
pEngine = NULL;
#endif
headerfile = fopen(pHeaderFile, "wb");
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl) {
/* what call to write: */
//curl_easy_setopt(curl, CURLOPT_URL, "https://pintatesti.vero.fi/FIS/Return/IIT/Test/GetWithholdingPercentage/v1");
curl_easy_setopt(curl, CURLOPT_URL, "https://www.pedago.fi");
curl_easy_setopt(curl, CURLOPT_HEADERDATA, headerfile);
do { /* dummy loop, just to break out from */
if (pEngine) {
/* use crypto engine */
if (curl_easy_setopt(curl, CURLOPT_SSLENGINE, pEngine) != CURLE_OK) {
/* load the crypto engine */
fprintf(stderr, "can't set crypto engine\n");
break;
}
if (curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L) != CURLE_OK) {
/* set the crypto engine as default */
/* only needed for the first time you load
a engine in a curl object... */
fprintf(stderr, "can't set crypto engine as default\n");
break;
}
}
/* cert is stored PEM coded in file... */
/* since PEM is default, we needn't set it for PEM */
curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM");
/* set the cert for client authentication */
curl_easy_setopt(curl, CURLOPT_SSLCERT, pCertFile);
/* sorry, for engine we must set the passphrase
(if the key has one...) */
if (pPassphrase)
curl_easy_setopt(curl, CURLOPT_KEYPASSWD, pPassphrase);
/* if we use a key stored in a crypto engine,
we must set the key type to "ENG" */
curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, pKeyType);
/* set the private key (file or ID in engine) */
curl_easy_setopt(curl, CURLOPT_SSLKEY, pKeyName);
/* set the file with the certs vaildating the server */
//curl_easy_setopt(curl, CURLOPT_CAINFO, pCACertFile);
/* disconnect if we can't validate server's cert */
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
/* 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));
/* we are done... */
} while (0);
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
Edit: Underlying error I get is " schannel: certificate format compatibility error" and only occurrence I can find is in schannel.c where it compares to a P12 type, but my certs are PEM?
Edit2: So it does work if I import the certificate into the local store, something I would definitely want to avoid! Also curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); appears to be avery good idea! Now getting further.
Edit3: Got it working. Possibly related to: Having trouble sending client certificate in libcurl ssl request, what am I missing? which is from 2019. I would very much prefer to have the certificates in a file though.
Edit4: "libcurl/7.79.1 Schannel WinIDN"
I solved this with a little help from my friends. Libcurl when compiled as default on Windows will default to schannel. To override that you need to build for OpenSSL, thusly: nmake /f Makefile.vc mode=static DEBUG=yes WITH_SSL=static WITH_DEVEL=C:\Prog\OpenSSL-Win32_111L\. The important keywords are WITH_SSL=static or dynamic and WITH_DEVEL= path to where your OpenSSL files live. When built this way you can give file paths for certificate and key, ie: curl_easy_setopt(curl, CURLOPT_SSLCERT, m_Certificate.GetString());
This frees you of the messy certificate handling in Windows and lets you keep the certificates with the app itself.
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.
I'm having an issue with a crash when uploading a file via the curl library in C++. I'm using the exact demo code from this location: https://curl.haxx.se/libcurl/c/fileupload.html
The only thing I change in the code is the upload location, to upload to a local wamp server on windows, and the file to upload, which I've verified that its opening ok.
I'm running through visual studio 2014, and building CURL through DLL
The output from the program is:
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> PUT /replayupload.php HTTP/1.1
Host: 127.0.0.1
Accept: */*
Content-Length: 43
Expect: 100-continue
< HTTP/1.1 100 Continue
*then I get a crash at line 66 in the program. It seems the line:
res = curl_easy_perform(curl);
is causing the problem with an invalid parameter. I have verified that the curl variable is not null, but I'm finding it very difficult to get any more debug info than that, the call stack just references a memory address within the DLL.
I'm able to run the demo to just upload post variables and get a page, this runs fine without a crash. The crash only occurs when uploading a file.
my exact code is:
int main(void)
{
CURL *curl;
CURLcode res;
struct stat file_info;
double speed_upload, total_time;
FILE *fd;
fd = fopen("E:\\testfiles\\test.txt", "rb"); /* open file to upload */
if (!fd)
return 1; /* can't continue */
/* to get the file size */
if (fstat(_fileno(fd), &file_info) != 0)
return 1; /* can't continue */
curl = curl_easy_init();
if (curl) {
/* upload to this place */
curl_easy_setopt(curl, CURLOPT_URL,
"http://127.0.0.1/testupload.php");
/* tell it to "upload" to the URL */
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
/* set where to read from (on Windows you need to use READFUNCTION too) */
curl_easy_setopt(curl, CURLOPT_READDATA, fd);
/* and give the size of the upload (optional) */
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE,
(curl_off_t)file_info.st_size);
/* enable verbose for easier tracing */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
/* Check for errors */
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
else {
/* now extract transfer info */
curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &speed_upload);
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time);
fprintf(stderr, "Speed: %.3f bytes/sec during %.3f seconds\n",
speed_upload, total_time);
}
/* always cleanup */
curl_easy_cleanup(curl);
}
fclose(fd);
return 0;
}
Thanks to Tkausl for spotting the line
/* set where to read from (on Windows you need to use READFUNCTION too) */
I added this line to my code
curl_easy_setopt(curl, CURLOPT_READFUNCTION, &fread);
And now everything seems to work.
I was asked for my library management c++ project to get access to library file only by its file:// included URL...
I used Libcurl because of same tag words of it when I was searching.
is downloading of a file through Libcurl (as it said it's compatible) ,a right way to do it?
command line tool and library
for transferring data with URLs
Supports...
DICT, FILE, ...
the libcurl code was
#include <curl/curl.h>
int downloadTO(const void* downloadURL, const void* OUTPUT_FILE_NAME)
{
CURL *curl_handle;
static const char *pagefilename = (char*)OUTPUT_FILE_NAME;
FILE *pagefile;
curl_global_init(CURL_GLOBAL_ALL);
/* init the curl session */
curl_handle = curl_easy_init();
/* set URL to get here */
curl_easy_setopt(curl_handle, CURLOPT_URL, (char*)downloadURL);
/* Switch on full protocol/debug output while testing */
curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
/* disable progress meter, set to 0L to enable and disable debug output */
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
/* send all data to this function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);
/* open the file */
pagefile = fopen(pagefilename, "wb");
if(pagefile)
{
/* write the page body to this file handle */
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile);
/* get it! */
curl_easy_perform(curl_handle);
/* close the header file */
fclose(pagefile);
}
/* cleanup curl stuff */
curl_easy_cleanup(curl_handle);
return 0;
}
can I also get access to the file with its file:// prepended URL with the sockets?