C++ libcurl: curl_easy_perform() returned CURLE_OK eventhough it received response code 226 - c++

I'm sending files from linux server to windows remote system using libcurl FTP.
Below is the code
curl_easy_setopt(CurlSessionHandle, CURLOPT_URL, remoteFileUrl);
curl_easy_setopt(CurlSessionHandle, CURLOPT_UPLOAD, ON);
// Set the input local file handle
curl_easy_setopt(CurlSessionHandle, CURLOPT_READDATA, localFileHandle);
// Set on/off all wanted options
// Enable ftp data connection
curl_easy_setopt(CurlSessionHandle, CURLOPT_NOBODY, OFF);
// Create missing directory into FTP path
curl_easy_setopt(CurlSessionHandle, CURLOPT_FTP_CREATE_MISSING_DIRS , ON) ;
// Set the progress function, in order to check the stop transfer request
curl_easy_setopt(CurlSessionHandle, CURLOPT_NOPROGRESS, OFF);
curl_easy_setopt(CurlSessionHandle, CURLOPT_PROGRESSFUNCTION, progressCb);
curl_easy_setopt(CurlSessionHandle, CURLOPT_PROGRESSDATA, this);
CURLcode Result = curl_easy_perform(CurlSessionHandle);
====================================================================
few files are not transferred to remote but i didn't receive any error from curl_easy_perform(). This is happening randomly.
I have collected the wireshark logs, trace shows [RST, ACK] was sent from our side to remote system don't know the reason and response code 226 was sent from remote system, i think i should receive some error code from curl_easy_perform()
instead of CURLE_OK. Please correct me if i'm wrong.
Please check the image which has wireshark traces.
Source IP: 82 is Linux server & Destination IP: 87 is Windows remote system
I would like to know why we are sending [RST, ACK] to remote and why libcurl is not returning error code. Can someone explain to me is there a way to handle this problem.
I have uploaded images of success and failure case. Please check and let me know
Failure case, Check the response arg

Related

Slow Performance with libcurl when changing Ip Address

First time developing with libcurl and got some unexpected results:
I'm trying to reuse the same CURL handle in order to reuse it's connections associated with it. However, I noticed a huge performance drop when I constantly change the Ip address of my request.
When I keep the IP address unchanged everything is fine, I'm pretty sure there's something I'm missing, but I don't know what and where to search for.
Here's my code:
// curl init
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &RestClientPool::WriteCallBack); // custom write function
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 200L);
// perform request, curl handle is stored and reused
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(curl, CURLOPT_URL, req_url.c_str()); // here req_url is something like http://123.123.123.123:8080/api/api
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
CURLcode res = curl_easy_perform(curl);
Problem:
say i have 3 hosts with ip: a, b and c, if i send requests to a,b,c,a,b,c,a... 100 times V.S. a,a,a,a,a,a... 100 times with the same handle, performance is around 9s to 1s (service on all hosts are identical, no host issues needs to be considered)
PS: I can tell that libcurl handle is reusing the connections by monitoring(and by verbose), because i monitored the servers. So, new connections when ip change shouldn't be the problem here.
Environment:
libcurl:
curl 7.86.0 (x86_64-pc-linux-gnu) libcurl/7.86.0 OpenSSL/1.0.2k-fips zlib/1.2.7
Release-Date: 2022-10-26
Protocols: dict file ftp ftps gophergophers http https imap imaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS HSTS HTTPS-proxy IPv6 Largefile libz NTLM NTLM_WB SSL UnixSockets
os:
CentOS 7.6.1810
What I tried:
1、I trie using CURLOPT_RESOLVE(cause i thought it might have something do with DNS cache), but nothing changed.
2、I made three handle and fired off 3 threads, where each handle would request to same ip(host A for example) repeatedly(no handle were shared across threads). And it worked fine.
However, when I change the ip for each handle(thread 1 would now repeatedly make request to host A, thread 2 to B and thread 3 to C), the performance drop appears again.

SSLv3 mutual authentication using libcurl and smart card

I need some help to establish a communication channel to consume a webservice using SSLv3 with mutual authentication, libcurl and a smart card, which will store the client certificate, the key pair and will be responsible for signatures, encryptions, etc.
For testing purposes, without using the smart card, the following brief code runs smoothly:
....
(SOAP message configurations)
....
curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
curl_easy_setopt (curl, CURLOPT_SSLVERSION, 3);
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2);
curl_easy_setopt (curl, CURLOPT_SSLCERT, "clientCertificateFile");
curl_easy_setopt (curl, CURLOPT_KEYPASSWD, "testPrivateKeyPass");
curl_easy_setopt (curl, CURLOPT_SSLKEY, "testPrivateKeyFile");
curl_easy_setopt (curl, CURLOPT_CAINFO, "CAcertificateFile");
curl_easy_setopt (curl, CURLOPT_URL, "URLtoWebService");
curl_easy_perform (curl);
After running this code, I can establish communication using SSLv3 and get the expected response from the webservice. However, I need to establish this secure channel using the smart card. I have free access to the following information:
"clientCertificateFile" - stored on the smart card, in PEM format. It can be read through access function.
"CAcertificateFile" - stored on disk, in PEM format. It can be read through access function.
"URLtoWebService" - stored in the application.
"testPrivateKeyFile" and "testPrivateKeyPass" are temporary files used only for testing and should be replaced by the smart card.
My doubt is: How to use the smart card to replace the private key and password in the libcurl setup process?
The smart card does not allow access to private key, only to the public key, as expected. I can send to the smart card data to be signed or encrypted, using the private key, and get the resulting buffer.
Is there any way to redirect part of the SSLv3 connection handshake to use my smart card? Some setting in lib ssl, or libcurl?
Thanks for listening!

Send Close File Signal to FTP Server

I am implementing FTP Client in C++ using Windows Sockets. I have successfully connected to Server on Port 21, and transmitted the file in PASV mode using "STOR Sample.txt" command on the data port. The problem is that I am unable to tell the server about transfer completion (I want to send the signal to close data connection) so that i can receive the "226 Transfer OK" from the server on my control connection.
Further, I am not receiving anything from the server via recv(). I think that is because server is still listening on the data connection.
Have a look at this:
proper user of STOR command
You get back the port to connect to when you send PASV and then you tell the server where to store the data using STOR and the you connect to the port the PASV command returned and send the data - when you are done you close this second socket and continue sending commands with the original one.

How to know if a server is online or not, if we only know the IP?

How can that be done, the server is not an HTTP server, its a ArmA game server.
I tried to achieve it using CURL in the following code, but it didn't work, it always show Offline.
IsOnline( "xx.xxx.xx.xxx" );
bool IsOnline( string url )
{
CURL *curl;
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, url.c_str() );
CURLcode result = curl_easy_perform(curl);
if ( result != CURLE_OK )
{
error_string = curl_easy_strerror( result );
return false;
}
curl_easy_cleanup(curl);
curl_global_cleanup();
return true;
}
curl_easy_cleanup(curl);
curl_global_cleanup();
return false;
}
What you're doing in your code is trying to send an HTTP request. The reason it fails is because the game server machine isn't running a HTTP server.
The usual way to find out whether a machine is online or not is by pinging it. Ping uses the ICMP protocol. Here is a tutorial explaining how to ping from a C++ program (it's Windows-specific): http://www.developerfusion.com/article/4628/how-to-ping/
However, the admins of the server might have disabled ICMP, in which case the machine won't respond to pings even if it's online. Moreover, you can get "false positives" if the machine is online and responding to pings, but the game server software itself isn't running.
So I think your best bet would be to find out which ports the game server itself listens to and attempt a connection on those ports.

Does setting CURLOPT_URL force to create a second FTP connection?

I want to open a connection with an FTP server and download 2 different files. Names are totally different and I cannot use wildcards.
I expected I could set the hostname and the file, then call curl_easy_perform, then set the file again and call curl_easy_perform one last time.
However it seems I have to use the CURLOPT_URL which includes both the hostname and the filename.
My fear is that the following code (lacks the error checking just to be short here):
...
curl_easy_setopt(handle, CURLOPT_URL, "ftp://myserver//foo.dat");
curl_easy_perform(handle);
curl_easy_setopt(handle, CURLOPT_URL, "ftp://myserver//bar.png");
curl_easy_perform(handle);
opens the FTP connection twice, giving a lot of avoidable overhead.
So am I missing something here? Will libcurl notice that the hostname part is the same, thus avoiding to open the same connection twice? If not how can I open the connection only once?
Enabling CURLOPT_VERSBOSE showed that:
* Connection #0 to host 127.0.0.1 left intact
* Re-using existing connection! (#0) with host 127.0.0.1
* Connected to 127.0.0.1 (127.0.0.1) port 21 (#0)
* Request has same path as previous transfer
Also, wireshark showed that connection to port 21 is made only once and lasts throughout the whole transfer (including the two files).
However one connection per-file is made on another port because of the ftp passive mode, but I think this is not curl's fault.