I am currently working on a project that requires using C to make an http get request. I am trying to do this using curl. However, I get a response that says
error: unable to request data from https://coinex.pw/api/v2/currencies:
Unsupported protocol
I am not sure if the error is coming from curl or from the server. Here is my code, borrowed from example code:
#include <curl/curl.h>
static char *request(const char *url)
{
CURL *curl = NULL;
CURLcode status;
struct curl_slist *headers = NULL;
char *data = NULL;
long code;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(!curl)
goto error;
data = malloc(BUFFER_SIZE);
if(!data)
goto error;
struct write_result write_result = {
.data = data,
.pos = 0
};
curl_easy_setopt(curl, CURLOPT_URL, url);
headers = curl_slist_append(headers, "Content-type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result);
status = curl_easy_perform(curl);
if(status != 0)
{
fprintf(stderr, "error: unable to request data from %s:\n", url);
fprintf(stderr, "%s\n", curl_easy_strerror(status));
goto error;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
if(code != 200)
{
fprintf(stderr, "error: server responded with code %ld\n", code);
goto error;
}
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
curl_global_cleanup();
/* zero-terminate the result */
data[write_result.pos] = '\0';
return data;
error:
if(data)
free(data);
if(curl)
curl_easy_cleanup(curl);
if(headers)
curl_slist_free_all(headers);
curl_global_cleanup();
return NULL;
}
Any tips / hints are welcome.
curl (and libcurl) gives an unsupported protocol error when they can't interpret the protocol part of the URL. In your case that means https:, which is a bit odd.
First check you can you use the curl tool from the command line to retrieve the URL.
curl -V will give you a list of the protocols curl (and thus libcurl) will support:
$ curl -V
curl 7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap pop3 pop3s rtmp rtsp smtp smtps telnet tftp
Features: GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP
Check that https is there. It may be that your libcurl is not built against an SSL library or if it is that the SSL library is not installed.
Finally on this page: http://curl.haxx.se/libcurl/c/example.html you will note that is a simple https example (second one down). Please confirm that works with your libcurl. If so, I'd find out what your program is doing different. If not, I would find out what's wrong with your libcurl installation.
try to use "http" but "https" in your URL.
Related
I based my code on Download file using libcurl in C/C++ but I'm getting an ERROR: "Unsupported protocol" for SFTP. However, for the same SFTP, file download and upload works with the command line.
Code:
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
FILE *fp;
CURLcode res;
//char *url = "http://stackoverflow.com";
char *url = "sftp://dheerajr#10.4.1.156/home/dheerajr/temp/download.txt";
char outfilename[FILENAME_MAX] = "temp.txt";
curl = curl_easy_init();
if (curl)
{
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
if(res == CURLE_OK)
printf("Download Successfull\n");
else
printf("ERROR: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}
checked with curl -V command which supports SFTP. curl -V output:
curl 7.72.0-DEV (x86_64-pc-linux-gnu) libcurl/7.72.0-DEV OpenSSL/1.1.1 zlib/1.2.11 libidn2/2.0.4 libpsl/0.19.1 (+libidn2/2.0.4) libssh2/1.8.2 nghttp2/1.30.0 librtmp/2.3
Release-Date: [unreleased]
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: AsynchDNS HTTP2 HTTPS-proxy IDN IPv6 Largefile libz NTLM NTLM_WB PSL SSL TLS-SRP UnixSockets
Can anyone suggest a solution for this?
You need to compile ssh2 library first and that reconfigure curl library with --with-libssh2="path" and recompile. then you will have the ssh function call working with curl api's.
Good Luck ;-)
I have libcurl built with OpenSSL backend. I want to set SNI to some specified string. the way that I could find is using the function SSL_set_tlsext_host_name which takes the SSL * instance and a string and then sets it. (see https://stackoverflow.com/a/5113466/3754125)
However curl_easy does not have a call back to retrieve SSL* instance. Is there an alternate way to do so?
Some more context:
In my environment, I have to use CURLOPT_RESOLVE to resolve the FQDN to IPv4.
There is the FQDN: const char *fqdn
IPv4 which fqdn should resolve to: uint32_t ipv4
fake SNI: const char *sni
The gist looks something like:
CURL *ez;
char buf[ENOUGH];
struct curl_slist *resolver;
/* ... */
snprintf(buf, sizeof(buf), "%s:%d:%d.%d.%d.%d", fqdn, port, IP(IPv4));
resolver = curl_slist_append(NULL, buf);
curl_easy_setopt(ez, CURLOPT_RESOLVE, resolver);
After this I need to set the SNI to the fake SNI without touching the resolver.
If you want to "fake" the SNI then CURLOPT_RESOLVE or CURLOPT_CONNECT_TO are available options to reach the same end goal.
CURLOPT_RESOLVE example
Run a HTTPS server on 127.0.0.1 but make curl think it is example.com when it connects to it (so it sends that as SNI and in the Host: header)
CURL *curl;
struct curl_slist *host = NULL;
host = curl_slist_append(NULL, "example.com:443:127.0.0.1");
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_slist_free_all(host);
CURLOPT_CONNECT_TO example
Run a dev HTTPS server on the host name server1.example.com but you want curl to connect to it thinking it is the www.example.org server.
CURL *curl;
struct curl_slist *connect_to = NULL;
connect_to = curl_slist_append(NULL, "www.example.org::server1.example.com:");
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.org");
curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_slist_free_all(connect_to);
i tried to calling DynamoDB low-level API's from C code. This is my code
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
struct curl_slist *chunk = NULL;
chunk = curl_slist_append(chunk, "Host: dynamodb.us-east-1.amazonaws.com;");
chunk = curl_slist_append(chunk, "Accept-Encoding: identity;");
chunk = curl_slist_append(chunk, "Content-Length: 53;");
chunk = curl_slist_append(chunk, "User-Agent: CustomApp42;");
chunk = curl_slist_append(chunk, "Content-Type: application/x-amz-json-1.0;");
chunk = curl_slist_append(chunk, "Authorization: AWS4-HMAC-SHA256 Credential=<Credential>, SignedHeaders=<Headers>, Signature=<signature>;");
chunk = curl_slist_append(chunk, "X-Amz-Date: 4.4.2016 ;");
chunk = curl_slist_append(chunk, "X-Amz-Target: DynamoDB_20120810.GetItem;");
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
curl_easy_setopt(curl, CURLOPT_URL, "dynamodb.us-east-1.amazonaws.com");
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"TableName\":\"Pets\",\"Key\":{\"AnimalType\":{\"S\": \"Dog\"},\"Name\": {\"S\": \"Fido\"}}}");
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);
/* free the custom headers */
curl_slist_free_all(chunk);
}
return 0;
}
But it produce error while running like
HTTP/1.1 400 Bad Request
I faced mainly two issues.
i have aws_access_key_id and aws_secret_access_key. How to create Authorization (SignedHeaders & Signature) using these two credentials?
How to modify "X-Amz-Target" for dynamo query method?
Are you able to use the AWS SDK for C++? It will do all this heavy lifting and more for you. Actually, even if you are making a C program, you can expose an extern C library structure to the AWS SDK calls that you need. So, either way, I recommend coding against the AWS SDK for C++, and create an extern C library wrapper in case you need to compile your main program as a C program.
I am trying to connect to server which demands client authentication. I am doing it in C with libcurl. The problem is when I try to connect I get:
curl_easy_perform() failed: SSL connect error
I read that I should add server certificate to ca-bundle.crt; however server's certificate is self signed so when I add it to ca-bundle I got SSL peer certificate or SSH remote key was not OK. After that I tried do set CURLOPT_SSL_VERIFYPEER to false; but I got the first error curl_easy_perform() failed: SSL connect error
This is my current code:
#define SKIP_HOSTNAME_VERIFICATION
#define SKIP_PEER_VERIFICATION
int authenticate(CURL *curl) {
char* pathToCert = "sslCert.pem";
char* pathToKey = "privateKey.pem";
curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM");
int res = curl_easy_setopt(curl, CURLOPT_SSLCERT, pathToCert);
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_easy_setopt(curl, CURLOPT_SSLKEY, pathToKey);
}
int main(int argc, char **argv) {
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:8443/RemSig/status");
authenticate(curl);
#ifdef SKIP_PEER_VERIFICATION
/*
* If you want to connect to a site who isn't using a certificate that is
* signed by one of the certs in the CA bundle you have, you can skip the
* verification of the server's certificate. This makes the connection
* A LOT LESS SECURE.
*
* If you have a CA cert for the server stored someplace else than in the
* default bundle, then the CURLOPT_CAPATH option might come handy for
* you.
*/
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
#endif
#ifdef SKIP_HOSTNAME_VERIFICATION
/*
* If the site you're connecting to uses a different host name that what
* they have mentioned in their server certificate's commonName (or
* subjectAltName) fields, libcurl will refuse to connect. You can skip
* this check, but this will make the connection less secure.
*/
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
#endif
/* 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);
}
curl_global_cleanup();
return 0;
}
Does someone know where could be problem? The server is running and can be accessed from different client and browser.
EDIT - SOLUTION
After adding curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); to my code I find out that the problem was in certificate path. In path to certificate ./ should be added, otherwise libcurl can not find the certificate.
I want to check FTP server connectivity using curl library in c program. Can anyone tell me how to do that without using any data transfer means i don't want to transfer any file to check that. I want is like CURLOPT_CONNECT_ONLY option which is available for only HTTP, SMTP and POP3 protocols not for FTP.
Curl version : 7.24
Requirement : FTP server connectivity test.
Here in below example, Only connect request will be delivered to FTP server and if server is pingable then it will give CURLE_OK return code other wise give failure response after specific timeout(60 sec). Other options you can set as per your requirement from http://curl.haxx.se/libcurl/c/ .
...
snprintf(ftp_url, BUF_LEN_512, "ftp://%s:%s#%s", uploadConf->username, uploadConf->password, uploadConf->ip);
// Reset curl lib
curl_easy_reset(curl);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, throw_away);
if (CURLE_OK != (res = curl_easy_setopt(curl, CURLOPT_URL, ftp_url)))
{
printf("Failed to check ftp url, Error : %s : %d\n", curl_easy_strerror(res), res);
}
curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
// Connection establishment timeout
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 60);
if (CURLE_OK != (res = curl_easy_perform(curl)))
{
/* If fail to connect */
}
else
{
/* If connected succesfully */
}
static size_t throw_away(void *ptr, size_t size, size_t nmemb, void *data)
{
size_t res;
res = (size_t)(size * nmemb);
/* we are not interested in the headers itself, so we only return the size we would have saved ... */
return res;
}
Hope it will help you all to test connectivity to FTP server using libcurl in c.