Using CURLOPT_CONNECT_TO with an IPv6 address - libcurl

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.

Related

How do I get the client Remote Port number in a Django?

I know I can use request.META['REMOTE_ADDR'] to get the client's IP in my django view function.
However, I have no idea how to get the client remote port number.
For example, you can see your own remote port number on the site below:
https://www.myip.com/
Remote Port
here is sample of view.py:
if request.user.is_authenticated:
        gelenIleti = request.META.get('HTTP_X_FORWARDED_FOR')
        if gelenIleti:
            ip = gelenIleti.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
            portNumarasi = request.META['SERVER_PORT']
        logger.info(' ' + 'LOG KAYDI :' + ' ' + ' KULLANICI : ' + request.user.username + ' ' + ' IP : ' + ip + ' ' + ' SERVER PORT : ' + portNumarasi)
You can get the IP and PORT through WSGIRequest connected socket.
Django 1.11:
sock = request._stream.stream._sock
client_ip, port = sock.getpeername()
Django 2.1:
sock = request._stream.stream.raw._sock
client_ip, port = sock.getpeername()
Django 2.2 and 3.1:
sock = request._stream.stream.stream.raw._sock
client_ip, port = sock.getpeername()
UPDATE
if request.user.is_authenticated:
sock = request._stream.stream.stream.raw._sock
client_ip, port = sock.getpeername()
logger.info(' LOG KAYDI : KULLANICI : %s IP : %s SERVER PORT : %s' % (request.user.username, client_ip, port))

Secured SSL connection Error: unknown protocol | bad hostname lookup

Iam downloading RSS feed file (eg. https://tools.ietf.org/dailydose/dailydose_atom.xml) from server via HTTP.
First, i have to connect to the remote server via OpenSSL, as described here.
Unsecured version works just fine and i can connect and receive HTTP answer with feeds:
bio = BIO_new_connect("www.tools.ietf.org:80");
if(bio == NULL)
{
/* Handle the failure */
}
if(BIO_do_connect(bio) <= 0)
{
/* Handle failed connection */
}
Secured version:
BIO * m_bio;
SSL_CTX * m_ctx;
SSL * m_ssl;
SSL_library_init();
m_ctx = SSL_CTX_new(SSLv23_client_method());
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
SSL_CTX_set_default_verify_paths(m_ctx);
m_bio = BIO_new_ssl_connect(m_ctx);
BIO_get_ssl(m_bio, &m_ssl);
SSL_set_mode(m_ssl, SSL_MODE_AUTO_RETRY);
BIO_set_conn_hostname(m_bio, "www.tools.ietf.org:80");
if (BIO_do_connect(m_bio) <= 0)
{
printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
throw std::runtime_error("FEEDREADER: Connection failed.");
}
if(SSL_get_verify_result(m_ssl) != X509_V_OK)
{
throw std::runtime_error("FEEDREADER: Verification failed.");
}
Where do_connect fails with following error:
Error: unknown protocol
when i replace www.tools.ietf.org with http(s)://www.tools.ietf.org
another error appears:
Error: bad hostname lookup
But hostname & dns works well for unsecured version, so may somebody help me with this one ?
80 is the default HTTP port. 443 is the default HTTPS port.
bio = BIO_new_connect("www.tools.ietf.org:443");
BIO_set_conn_hostname(m_bio, "www.tools.ietf.org:443");

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.

Port range in the argument to PORT command

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

How can you force a web service client to use a specific port?

Is it possible to force an web service client to talk from a specific range of port E.g. 4900- 4999 to a web server in port 80?
I understand now that there is client and server port numbers and need to create a client application to send http statuses to a web server but firewall team only opens ports 4900 to 4999 in the client.
Any ideas?
If you are using a web browser to connect to your server then you may be out of luck but as you said in your question that you are creating a client application you can do this with the bind system call in both Windows and Linux (this code is in C):
struct sockaddr_in client;
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
memset(&client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_addr.s_addr = htonl(INADDR_ANY);
client.sin_port = htons(4901);
Then call bind:
res = bind(sock, (struct sockaddr *)&client, sizeof(client));
If this call is successful (res is 0) you can then connect your socket to the server and you will be connecting from port 4901.
In case someone else needs it, here is the Python code to do that:
#!/usr/bin/env python
from socket import *
import time
HOST = '**.**.**.**' # IP of the server
PORT = 8080
GET = '/hello/There'
BUFSIZ = 1024
ADDR = (HOST, PORT)
sock = socket(AF_INET, SOCK_STREAM)
sock.bind(('',4925))
sock.connect(ADDR)
print 'connected'
request = """GET %s HTTP/1.0\n
Host: %s\n
User-Agent: Python\n
\n""" % (GET, HOST)
sock.send(request)
data = sock.recv(1024)
string = ""
while len(data):
string = string + data
data = sock.recv(1024)
print string
sock.close()
This is working with a wsgi server running on the other side. Pretty straight forward. Thanks for the help.