Port range in the argument to PORT command - c++

Suppose I want to connect to FTP server, then list root directory in active mode (via PORT command). For this I'm going to use cURL library.
Seems, like cURL provides such capability. There's CURLOPT_FTPPORT option and the official reference says, that the port may be optionally followed by a '-' to specify a port range.
if (CURL* curl = ::curl_easy_init())
{
guard.reset(curl, ::curl_easy_cleanup);
::curl_easy_setopt(curl, CURLOPT_URL, "ftp://10.0.0.162:21");
::curl_easy_setopt(curl, CURLOPT_FTPPORT, "localhost:37000-38000");
CURLcode retval = ::curl_easy_perform(curl);
if (retval != CURLE_OK)
{
throw NetworkError(retval, ::curl_easy_strerror(retval));
}
}
...
But here's a problem.
If the first port in the range is not available, curl_easy_perform fails with CURLE_FTP_PORT_FAILED.
EDIT
Output in verbose mode:
* Rebuilt URL to: ftp://10.0.0.162:21/
* timeout on name lookup is not supported
* Trying 10.0.0.162...
* Connected to 10.0.0.162 (10.0.0.162) port 21 (#0)
< 220 eugee-server / banner
> USER anonymous
< 331 Anonymous access allowed, send identity (e-mail name) as password.
> PASS ftp#example.com
< 230-Welcome to ftp.eugeeserver
< 230 User logged in.
> PWD
< 257 "/" is current directory.
* Entry path is '/'
* socket failure: Bad access
* Remembering we are in dir ""
* Connection #0 to host 10.0.0.162 left intact

Related

How to send JSON data to a REST API?

I'm sending data to a Wordpress site with the WooCommerce plugin installed using libcurl in C++ and the WooCommerce REST API. The data seems to get sent but the expected result is not shown on the website. The purpose of it is to update (modify) the product. My code is based on the WooCommerce documentation, found here.
I have managed to get the CURLOPT_VERBOSE text from the program in a separate txt file.
Here is my C++ code using cURL :
std::string URL = main_domain + "wp-json/wc/v3/products/" + product_id + "?consumer_key=" + consumer_key + "&consumer_secret=" + consumer_secret;
curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
headers = curl_slist_append(headers, "Accept:application/json");
headers = curl_slist_append(headers, "Content-Type:application/json");
headers = curl_slist_append(headers, "charsets: utf-8");
// log file
FILE* filep = fopen("logfichier.txt", "w");
std::string toUpdate = "{\"id\":\"" + product_id + ",\"name\":\"" + product_name + "\",\"description\":\"" + product_description + "\",\"price\":\"" + product_price + "\"}";
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
readBuffer = "";
curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, toUpdate.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, toUpdate.length());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
curl_easy_setopt(curl, CURLOPT_VERBOSE, true);
curl_easy_setopt(curl, CURLOPT_STDERR, filep);
res = curl_easy_perform(curl);
// Check for errors
if (res != CURLE_OK) {
// error handling and cleanup
}
else {
// code and cleanup
}
}
else {
// error handling and cleanup
}
I've literally put every header found on the internet that seemed relevant to what i'm trying to acomplish in my code.
Here is the returned debug text :
* STATE: INIT => CONNECT handle 0x10870278; line 1428 (connection #-5000)
* Added connection 0. The cache now contains 1 members
* STATE: CONNECT => WAITRESOLVE handle 0x10870278; line 1464 (connection #0)
* Trying 192.XX.XX.XX...
* TCP_NODELAY set
* STATE: WAITRESOLVE => WAITCONNECT handle 0x10870278; line 1545 (connection #0)
* Connected to mywebsite.com (192.XX.XX.XX) port 443 (#0)
* STATE: WAITCONNECT => SENDPROTOCONNECT handle 0x10870278; line 1599 (connection #0)
* Marked for [keep alive]: HTTP default
* schannel: SSL/TLS connection with mywebsite.com port 443 (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 176 bytes...
// (here was just a bunch of connexion attemps log text...)
* schannel: SSL/TLS handshake complete
* schannel: SSL/TLS connection with mywebsite.com port 443 (step 3/3)
* schannel: stored credential handle in session cache
* STATE: PROTOCONNECT => DO handle 0x10870278; line 1634 (connection #0)
> PUT /wp-json/wc/v3/products/111867?consumer_key=(the actual key)&consumer_secret=(the actual secret) HTTP/1.1
Host: mywebsite.com
Transfer-Encoding: chunked
Accept:application/json
Content-Type:application/json
charsets: utf-8
4b
* upload completely sent off: 82 out of 75 bytes
* STATE: DO => DO_DONE handle 0x10870278; line 1696 (connection #0)
* STATE: DO_DONE => WAITPERFORM handle 0x10870278; line 1823 (connection #0)
* STATE: WAITPERFORM => PERFORM handle 0x10870278; line 1838 (connection #0)
* schannel: client wants to read 16384 bytes
* schannel: encdata_buffer resized 17408
* schannel: encrypted data buffer: offset 0 length 17408
// (a few decrypting data attempts...)
* schannel: decrypted data returned 536
* schannel: decrypted data buffer: offset 0 length 16384
* HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 200 OK
< Date: Tue, 18 Jun 2019 15:27:42 GMT
* Server Apache is not blacklisted
< Server: Apache
< X-Robots-Tag: noindex
< Link: <https://mywebsite.com/wp-json/>; rel="https://api.w.org/"
< X-Content-Type-Options: nosniff
< Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages
< Access-Control-Allow-Headers: Authorization, Content-Type
< Expires: Wed, 11 Jan 1984 05:00:00 GMT
< Cache-Control: no-transform, no-cache, must-revalidate, max-age=0
< Allow: GET, POST, PUT, PATCH, DELETE
< Transfer-Encoding: chunked
< Content-Type: application/json; charset=UTF-8
<
* schannel: client wants to read 16384 bytes
* schannel: encrypted data buffer: offset 835 length 17408
// (a few decrypting data attempts...)
* schannel: decrypted data returned 1986
* schannel: decrypted data buffer: offset 0 length 16384
* STATE: PERFORM => DONE handle 0x10870278; line 2011 (connection #0)
* multi_done
* Connection #0 to host axanti.info left intact
I took out a few redundant part from the original text and kept what I think is the main piece of information. It seems that my JSON data is actually sent to the server but the intended result doesn't show up on my website (a product should be modified but it's actually not).
Is there any way this code could be wrong ? Or is the problem on the server side ? Because I litteraly apply the same stuff that is mentioned in the official documentation.
Looks like your payload if off. The id portion is redundant, as you're already specifying the product to update via the URL, so you can drop that. Additionally, you're attempting to set the price incorrectly. Per the REST docs, you need to use the regular_price attribute instead of price (price is read only). The proper payload should look like this:
{
name: 'My product name',
description: 'my product description',
regular_price' : '3.50',
}

Using CURLOPT_CONNECT_TO with an IPv6 address

I am attempting to use curl's CURLOPT_CONNECT_TO option to connect to a specific address (rather than the result of the DNS lookup of the host part of the url):
CURL * r_curl = NULL;
struct curl_slist * r_connect = NULL;
char connectStr[128];
if (af == AF_INET) {
sprintf(connectStr, "::%s:", ipAddrString);
} else if (af == AF_INET6) {
/* in [] per https://curl.haxx.se/libcurl/c/CURLOPT_CONNECT_TO.html */
sprintf(connectStr, "::[%s]:", ipAddrString);
}
fprintf(stderr, "DEBUG: connect '%s', url %s\n", connectStr, url);
r_curl = curl_easy_init();
...
r_connect = curl_slist_append(r_connect, connectStr);
curl_easy_setopt(r_curl, CURLOPT_CONNECT_TO, r_connect);
curl_easy_setopt(r_curl, CURLOPT_URL, url);
curl_easy_perform(r_curl);
When af is AF_INET and ipAddrSring is an IPv4 address this works perfectly. When af is AF_INET6 and ipAddrSring is an IPv6 address, curl looks like it is trying to do a DNS host lookup on the IPv6 address:
DEBUG: connect '::129.186.23.166:', url http://www.iastate.edu/
* Connecting to hostname: 129.186.23.166
* Trying 129.186.23.166...
* TCP_NODELAY set
* Connected to 129.186.23.166 (129.186.23.166) port 80 (#0)
vs
DEBUG: connect '::[2610:130:101:104::2]:', url http://www.iastate.edu/
* Connecting to hostname: 2610:130:101:104::2
* Could not resolve host: 2610:130:101:104::2
What I am doing wrong here?
(Curl is version 7.56.1)
There was a bug in libcurl (before 7.58.0) which made it take IPv6 addresses and attempt to use them for CURLOPT_CONNECT_TO, even if it was built without support for IPv6!
This was addressed in curl 7.58.0 and from then on it makes libcurl return an error if this is attempted!
Answer: Curl Library was built w/o IPv6 support.
I'm thinking that maybe that should result in a more meaningful error message.

Smtp client hangs after sending data

I want to build an stmp client using c++ for learning purposes.
After I managed to implement the initial connection + auth login I am stuck on sending the message after using the data command.
Here is my code
void sendmail()
{
write_command("MAIL FROM: <foo#bar.de>");
write_command("RCPT TO: <bar.foo#baz.de>");
write_command("DATA");
write_command("Subject: testmail"); // HANGS here after data command
write_command("BlaBlub");
write_command(" ");
write_command(".");
write_command("QUIT");
}
void write_command(std::string command)
{
ssize_t n;
empty_buffer();
command += '\r';
command += '\n';
char command_buffer[255];
strcpy(command_buffer, command.c_str());
n = write(sockfd,command_buffer,strlen(command_buffer));
if (n < 0){
error("ERROR writing to socket");
}
n = read_to_buffer();
if (n < 0) {
error("ERROR reading from socket");
}
printf("%s\n",this->buffer);
}
I'm using smtp.mailtrap.io on port 25.
Here is a gist with the full class https://gist.github.com/xhallix/7f2d87a8b2eab4953d161059c2482b37
Here is the server output
Starting smpt client
220 mailtrap.io ESMTP ready
250-mailtrap.io
250-SIZE 5242880
250-PIPELINING
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-AUTH PLAIN LOGIN CRAM-MD5
250 STARTTLS
334 VXNlcm5hbWU6
334 UGFzc3dvcmQ6
235 2.0.0 OK
250 2.1.0 Ok
250 2.1.0 Ok
354 Go ahead
(HANGS HERE)
Thanks for helping me out
DATA command expects the whole mail message, as shown here. The write_command() sends a message by lines and expects response after each line. Since the server returns the response once the mail message is finished (after empty line and dot), it stays in the hanging mode after the first message line. This code snippet can be helpful for your case.
BTW, you should put an empty line between the mail header and body, which I guess is after the subject line. Also, it might happen that the server rejects the message without the From and To headers.

gSOAP SSL - converting existing code to ssl

The earlier Version of this question got no Response, so I'm updating the entire Thing:
I have a test gSOAP Client and server on my machine. The client does an MTOM upload of various files to the server.
When attempting to convert the code to ssl I get the following error:
The server reports:
"SSL_ERROR_SSL
error:1408A0C1:SSL routines:ssl3_get_client_hello:no shared cipher"
The client reports:
An SSL error occured
SOAP 1.2 fault SOAP-ENV:Receiver [no subcode]
"SSL_ERROR_SSL
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure"
Detail: SSL_connect error in tcp_connect()
it runs without the "SSL" option. Can someone advise me as to what I'm doing wrong?
Applicable client code -
if(fSSL)
soap_ssl_init();
. . .
soap_init1(&my_soap, SOAP_ENC_MTOM); /* Enable MTOM */
. . .
if(fSSL)
{
if (soap_ssl_client_context(&my_soap,
SOAP_SSL_NO_AUTHENTICATION + SOAP_TLSv1_2,
NULL, // client keyfile
NULL, // passphrase for keyfile
NULL, // certified authority certificate
NULL, // directory for trusted certificates
NULL))// random data for seed
{
soap_print_fault(&my_soap, stderr);
...
}
}
...
long gsoap_status = soap_call___ns1__upload(&my_soap, endpoint.c_str(), NULL, &upload_parms, &upload_response);
Applicable server code -
if(fSSL)
soap_ssl_init();
. . .
soap_init1(&my_soap, SOAP_ENC_MTOM); /* Enable MTOM */
. . .
if(fSSL)
{
if (soap_ssl_server_context(&my_soap,
SOAP_SSL_NO_AUTHENTICATION + SOAP_TLSv1_2, // per EMAIL - option 1
NULL, // Keyfile - required for authentication
NULL, // passphrase
NULL, // password to read Keyfile
NULL, // optional cacert file
NULL, // DH Filename or DH key len bits
NULL, // Randfile
NULL)) // optional server identification (enable SSL session cache)
{
soap_print_fault(&my_soap, stderr);
exit(0);
}
}
. . .
my_soap.connect_timeout = 20;
my_soap.send_timeout = 60;
my_soap.recv_timeout = 60;
if(!soap_valid_socket(soap_bind(&my_soap, NULL, port, 100)))
{
soap_print_fault(&my_soap, stderr);
exit(1);
}
fprintf(stderr, "Bind to port %d successful\n", port);
// server loop starts
for (;;)
printf("server loop sta\n");
int t_socket = soap_accept(&my_soap);
struct soap* t_soap = 0;
t_soap = soap_copy(&my_soap);
if(fSSL)
{
if(soap_ssl_accept(&my_soap)) <------ FAILS HERE
{
printf("NOT Accepting (ssl) socket=%d connection from IP: %d.%d.%d.%d ...", t_socket,
(int)my_soap.ip>>24&0xFF,
(int)my_soap.ip>>16&0xFF,
(int)my_soap.ip>>8&0xFF,
(int)my_soap.ip&0xFF);
soap_print_fault(&my_soap, stderr);
soap_destroy(&my_soap);
soap_end(&my_soap);
continue;
}
}
. . .
if(soap_serve(&my_soap))
...
Server Console output:
Bind to port 8080 successful
server loop sta
NOT Accepting (ssl) socket=364 connection from IP: 127.0.0.1 ...Error 30 fault is internal [no subcode]
"SSL_ERROR_SSL
error:1408A0C1:SSL routines:ssl3_get_client_hello:no shared cipher"
Detail: SSL_accept() failed in soap_ssl_accept()
I'm working on this now. I think the errors that you are seeing are because most/all distributions of openSSL do not support anonymous authentication any longer due to man in the middle attacks. A self-signed certificate on the server-side may be the only way to make these examples work.

Connection Timeout Disconnects using Zehon FTP

I am using the Zehon FTP utility on a ColdFusion 9 server. When I am FTP'ing files, it creates one directory, transfers about 16 files, then gives the message :
com.zehon.exception.FileTransferException: org.apache.commons.vfs.FileSystemException: Could not connect to FTP server on "ftpservername.com".
Here's the code:
<cfscript>
//FTP server information
host = "#getSiteList.TMS_FTPADDRESS#";
username = "#getSiteList.TMS_USERNAME#";
password = "#getSiteList.TMS_PASSWORD#";
/* sendingFolder = Folder whose content is to be uploaded recursively
* to the FTP server.
*/
sendingFolder = "#local_Folder#";
/* Forward slash / = root dir of FTP server.
* if you wish to FTP to privateDir under the root, for example,
* then set destFolder to "/privateDir"
*/
destFolder = "/#parent_Folder#";
FTP = createObject("java", "com.zehon.ftp.FTP");
thisBatchTransferProgressDefault= createObject("java", "com.zehon.BatchTransferProgressDefault").init();
FileTransferStatus = createObject("java", "com.zehon.FileTransferStatus");
try {
status = FTP.sendFolder(sendingFolder, destFolder, thisBatchTransferProgressDefault, host, username, password);
if(FileTransferStatus.SUCCESS is status){
writeOutput(sendingFolder & " got ftp-ed successfully to folder " & destFolder);
}
else if(FileTransferStatus.FAILURE is status){
writeOutput("Failed to ftp to folder " & destFolder);
}
} catch (any e) {
writeOutput(e.message);
}
</cfscript>
I tried this on two different servers and the same issue. If I use FileZilla or CFFTP, I can transfer all my files (CFFTP is having issues creating subfolders, which is why I moved away from that, and we want our customers to use their web app to FTP files, not a client). Has anyone else experience this? If so, was a solution discovered? Thanks