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.
Related
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 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.
As part of a project, I need to implement libcurl on a Custom WindowsCE device that then talks to a server over wired LAN and GETs, POSTs and DELETEs data ( The programming language I am using is C). This is as far as I've progressed:
I found a couple of tutorials to re-implement the library for my CE device.
( http://stasyan.wordpress.com/2009/12/08/libcurl-for-windows-mobile-and-windows-ce/ , and
http://rxwen.blogspot.com/2012/05/port-libcurl-to-wince.html do a really good job.)
I was able to build a DLL and a Static libcurl library, which I then linked to my Visual Studio 2005 application.
I connected my custom CE device and the server to a switch. I am able to ping the server from the CE device and vice versa, so I know that they are both able to send and receive data to each other.
My server (Linux) uses a lighttpd web-server which works perfectly fine when I request the URL (shown in code, below) from my Windows PC which is also connected to the N/W switch.
This is the code I used for my test app:
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
printf("STARTING APP...\n");
CURL *curl;
CURLcode res;
printf("curl = curl_easy_init();\n");
curl = curl_easy_init();
if(curl)
{
printf("Enabling followlocation...\n");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1l);
printf("Setting Verbose...\n");
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
printf("Setting Connect time out...\n");
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5);
printf("Setting Server time out...\n");
curl_easy_setopt(curl, CURLOPT_ACCEPTTIMEOUT_MS , 5000);
printf("Setting URL...\n");
curl_easy_setopt(curl, CURLOPT_URL, "http://165.26.79.10:8080");
/* Perform the request, res will get the return code */
printf("Perform(curl)...;\n");
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
printf("\nGET sucessful.\n");
/* always cleanup */
curl_easy_cleanup(curl);
}
getchar();
return 0;
}
getchar();
return 0;
}
My problem is that the app never returns. It doesn't even progress past curl_easy_perform(curl).
This is the output I get (Google Drive link): [http://goo.gl/IKgiqW]
What could I possibly be doing wrong??
I managed to find out what the problem was.
The curl_easy_perform(...) command goes into an infinite loop caused due to a bug in one of the files in the compatibility library. It is referenced in a cURL development forum (google it), but I've summarized it below:
The WCE compatibility library I used (https://github.com/mauricek/wcecompat/) has a file called wce211_string.c. A function within the file, _CRTIMP char* __cdecl strrchr(const char* s, int c), uses a const char* p pointer that isn't decremented. To fix this bug, add the line: p-=1; after the line 'return (char*)p;', so the function will read as follows:
_CRTIMP char* __cdecl strrchr(const char* s, int c)
{
const char* p = s + strlen(s) - 1;
while (p >= s)
{
if (*p == c)
return (char*)p;
p -= 1;
}
return NULL;
}
This fixes the infinite looping problem.
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.
I have developed a ‘C’ application on a Linux box using the libcurl example http://curl.askapache.com/c/ftpupload.html I work fine. I have been asked to use SSL for both “data encryption for both the control channel and data channel.” I am not been able to find an example of adding SSL to the example I followed. Here is the core of the FTP program:
// get a FILE * of the same file
hd_src = fopen(local_file, "rb");
// curl init
curl_global_init(CURL_GLOBAL_ALL);
// get a curl handle
curl = curl_easy_init();
if (curl) { // build a list of commands to pass to libcurl
headerlist = curl_slist_append(headerlist, buf_1);
#ifdef DEBUG
// we want to use our own read function
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
#endif
// enable uploading
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
// specify target
curl_easy_setopt(curl, CURLOPT_URL, ftp_url);
curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
curl_easy_setopt(curl, CURLOPT_PORT, 21);
// pass in that last of FTP commands to run after the transfer
curl_easy_setopt(curl, CURLOPT_POSTQUOTE, headerlist);
// now specify which file to upload
curl_easy_setopt(curl, CURLOPT_READDATA, hd_src);
// Set the size of the file to upload
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) fsize);
// Now run off and do what you've been told!
res = curl_easy_perform(curl);
// Check for errors
if (res != CURLE_OK) {
char *s;
s = malloc((sizeof(char) * 100) + 1);
sprintf(s, "curl_easy_perform() failed: %s - Error Number: %d\n",
curl_easy_strerror(res), res);
returnResults->error = true;
returnResults->errorMessage = s;
return returnResults;
}
// clean up the FTP commands list
curl_slist_free_all(headerlist);
// always cleanup
curl_easy_cleanup(curl);
}
fclose(hd_src); // close the local file
curl_global_cleanup();
There's however an existing example code on the libcurl site showing how you do FTP-SSL to download a file: ftpsget.c - it shows the very little SSL magic you need to add. That magic is the same for uploads:
/* We activate SSL and we require it for both control and data */
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
In fact, you can add this single line into the FTP upload example. and it'll do FTPS uploads for you.