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.
Related
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.
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.
I am trying to download file using FTP, and in between if connection terminates then it should resume from where it was stop. My problem is that using following code snippet I able to continue download if I close connection and then again connect it, but if I am do so at server site then I am not able to resume download, and program goes into infinites state.
#include <stdio.h>
#include <curl/curl.h>
/*
* This is an example showing how to get a single file from an FTP server.
* It delays the actual destination file creation until the first write
* callback so that it won't create an empty file in case the remote file
* doesn't exist or something else fails.
*/
struct FtpFile {
const char *filename;
FILE *stream;
};
static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
{
struct FtpFile *out=(struct FtpFile *)stream;
if(out && !out->stream) {
/* open file for writing */
out->stream=fopen(out->filename, "wb");
if(!out->stream)
return -1; /* failure, can't open file to write */
}
return fwrite(buffer, size, nmemb, out->stream);
}
int main(void)
{
CURL *curl;
CURLcode res;
struct FtpFile ftpfile={
"dev.zip", /* name to store the file as if succesful */
NULL
};
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
/*
* You better replace the URL with one that works!
*/
curl_easy_setopt(curl, CURLOPT_URL,
"ftp://root:password#192.168.10.1/dev.zip");
/* Define our callback to get called when there's data to be written */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
/* Set a pointer to our struct to pass to the callback */
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile);
/* Switch on full protocol/debug output */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
if(CURLE_OK != res) {
/* we failed */
fprintf(stderr, "curl told us %d\n", res);
}
}
if(ftpfile.stream)
fclose(ftpfile.stream); /* close the local file */
curl_global_cleanup();
return 0;
}
Could any one tell me that how can I resume download if connection is closed by remote site.
Any help would be appreciated
Thanks,
Yuvi
Add a varaible to the ftpfile struct to aware your write function of the need to appeand and tell libcurl to resume the download from the end of the destination file by setting CURLOPT_RESUME_FROM to the number of bytes downloaded already:
struct FtpFile
{
const char *pcInfFil;
FILE *pFd;
int iAppend;
};
In main, if you want to resume:
curl_easy_setopt(curl, CURLOPT_RESUME_FROM , numberOfBytesToSkip);
If you the file doesn't already exist, or it is not a resumed download but a new download, be sure to set CURLOPT_RESUME_FROM back to 0.
In my_fwrite:
out->stream=fopen(out->filename, out->iAppend ? "ab":"wb");
P.S. if where you need to resume the file is larger than a long (2GB), look into CURLOPT_RESUME_FROM_LARGE and CURL_OFF_T_C()
In response to comment requesting additional information on how to know when a transfer failed:
After you call curl easy perform call:
CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... );
Retrieve from the curl context:
CURLINFO_HEADER_SIZE
CURLINFO_CONTENT_LENGTH_DOWNLOAD
Add them together and make sure they equal
CURLINFO_SIZE_DOWNLOAD
If not try to reperform the context.
And be sure to use the latest version of curl, it should timeout after 60 seconds of not hearing from the FTP server it is downloading from.
You may use the CURLOPT_CONNECTTIMEOUT and CURLOPT_TIMEOUT parameters to specify a connect timeout and maximum execution time on a per-handle basis.
The other way (that only works if you're using the easy interface, not the multi one) is to use a socket option callback, that you can set using CURLOPT_SOCKOPTFUNCTION. In it, you must call setsockopt() for the SO_RCVTIMEO parameter to the maximum amount of time a connection can be idle before it should be dropped. i.e. if no bytes have been received in the last 5 seconds, then drop the connection.