Slow Performance with libcurl when changing Ip Address - c++

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.

Related

Winsock connect is slow

I have a program that uses Boost.Asio to connect to a server on localhost. Here is the relevant part of the code:
TcpClient::TcpClient(uint16_t port_number) : socket_(service_)
{
boost::asio::ip::tcp::resolver resolver(service_);
boost::asio::ip::tcp::resolver::query resolver_query("localhost", std::to_string(port_number));
auto endpoint_iterator = resolver.resolve(resolver_query);
boost::asio::connect(socket_, endpoint_iterator);
}
The code functions just fine. On Ubuntu, the connect function returns almost immediately. However, on Windows it takes over 2 seconds to complete.
Stepping through the boost code, I found the 2 seconds are spent on the Winsock connect function call.
Am I missing something (either in code, or in the environment) that can speed up this call?
Thank you!
If you have IPv6 enabled then resolver_query("localhost", std::to_string(port_number)); will return an IPv4 and IPv6 address (from experience with the IPv6 listed first). If your server isn't listening on IPv6 then boost::asio::connect will try IPv6 first, wait for it to fail and only then try IPv4.
Either get your server to listen on IPv6, use "127.0.0.1" instead of localhost or restrict the resolver to only return IPv4:
resolver_query(boost::asio::ip::tcp::v4(), "localhost", std::to_string(port_number));

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

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

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!

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.