IO::Socket::SSL - establishing client connection - perl

As per the documentation of IO::Socket::SSL (PERL) I understand that if I want to just send command to specific server-program that running on port 9999 on my host, Ican do it this way:
my $cl=IO::Socket::SSL->new("localhost:9999"); # locallost is my real case, anyway
if($cl) {
$cl->connect_SSL or die $#;
# Something about certificates?
$cl->syswrite("Command");
close($cl);
}
But the error I get is: "SSL connect attempt failed with unknown error error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol at blabla.cgi line 888"
The server initialized thi way (in another pl program):
use IO::Socket::SSL;
use Net::WebSocket::Server;
my $CON={};
my #crt=&SSLCerts(); die 'E101' if(#crt==0);
my $ssl=IO::Socket::SSL->new(
Listen => 10000,
Timeout => 45,
LocalPort => 9999,
Proto => 'tcp',
SSL_cert_file => "$EF::Base/ssl/certs/$crt[0][0].crt",
SSL_key_file => "$EF::Base/ssl/keys/$crt[1][0].key"
) or die "E102: $!";
Net::WebSocket::Server->new(
listen => $ssl,
silence_max=> 3600,
tick_period => 5,
on_tick => sub {
# Empty for now
},
on_connect => sub {
my($serv,$conn)=#_; my($cid,$sid);
$conn->on(
handshake => sub {
my($cnc,$hdk)=#_;
my $nsn=$hdk->req->resource_name;
$cid=substr($nsn,4,index($nsn,'&L=')-4);
$CON->{$cid}=$cnc; # Register incomming connection
},
binary => sub {
# Handle incomming message from the client
},
disconnect => sub {
delete $CON->{$cid};
}
);
}
)->start;
Typical Websocket client that is connecting from the browser via "wss://" connects without any trouble.... The server MUST be SSL...
Here I am just trying to do the same from within perl.
What I am doing wrong? Nothing mentioned about certificates in client, only in server - the server is working fine. Maybe configuration? I have purchased SSL certificates and I use them for the server that running ok on that port.
The host is Linux (CentOS - if it matters).

... SSL23_GET_SERVER_HELLO:unknown protocol
This is the kind of error you get if the client tries to do a SSL handshake and the server responds with something which is not SSL. Typically this happens if you connect to a server which is not (properly) SSL enabled (at least not on this port) or requires some plain text to upgrade to SSL (like in case of SMTP STARTTLS and similar).
This kind of error has usually nothing to do with certificates.
This kind of error can only happen if you try to do an SSL handshake on an already established SSL socket but the server does not expect this. This seems to happen in your case:
my $cl=IO::Socket::SSL->new("localhost:9999"); # locallost is my real case, anyway
if($cl) {
$cl->connect_SSL or die $#;
...
The SSL connection is already being established by IO::Socket::SSL->new. This is the same with IO::Socket::INET or similar: if the target is given it will already connect inside new and an additional connect or connect_SSL should not be done. This is also reflected in the documentation and examples.
Since version 2.045 (released 02/2017) IO::Socket::SSL will simply ignore a connect_SSL if the SSL handshake is already done. You are likely using an older version of IO::Socket::SSL where connect_SSL will start a new handshake even if the TLS handshake was already finished. This handshake inside the TLS connection will result in the strange error you see.

Related

Using IO::Socket::SSL over an IO::Socket::SOCKS proxy

I'd like to mix SSL and SOCKS sockets. In other words, I want to send TLS encrypted data over a SOCKS proxy and I'd like to use IO::Socket::SSL and IO::Socket::Socks
The only option I can think of is to duplicate IO::Socket::SSL into an IO::Socket::SocksSSL class that inherit from IO::Socket::Socks. That's the only way to have the right order for (eg send). I need first to have the SSL method invoked and then it will invoke the parent (IO::Socket::Socks) send. Same for connect where the SSL connect would invoke the Socks connect and then start the TLS negotiation.
Unfortunately, the IO::Socket::SSL does not have a $SOCKET_CLASS var that would allow a subclass to easily decide what it inherits from, so I to change that one line in SSL.pm and duplicate all the code
I'm probably missing something here
Using a SOCKS proxy for TCP (and thus SSL) essentially means to first create a TCP socket, do some initial SOCKS handshake and then continue to work with the socket like with a normal TCP socket. IO::Socket::Socks->new(...) does this initial SOCKS handshake and returns a normal TCP socket. IO::Socket::SSL->start_SSL($socket,...) then does the TLS handshake on this socket. Combining both essentially does the same as IO::Socket::SSL->new(...), only using a SOCKS proxy instead of a direct connection.
Thus the code might look something like this:
use strict;
use warnings;
use IO::Socket::Socks;
use IO::Socket::SSL;
# establish TCP connection to target via SOCKS proxy
my $cl = IO::Socket::Socks->new(
ProxyAddr => '127.0.0.1',
ProxyPort => 1234,
ConnectAddr => 'whatsmyip.net',
ConnectPort => 443
) or die $!;
# upgrade the TCP socket to TLS
IO::Socket::SSL->start_SSL($cl,
# hostname is needed for SNI and certificate validation
SSL_hostname => 'whatsmyip.net'
) or die $SSL_ERROR;
# do a simple HTTP request on it
print $cl "GET / HTTP/1.0\r\nHost: whatsmyip.net\r\n\r\n";
print <$cl>;

Perl: forking an WSS connection results in closure of the socket when child exits (doesn't happen with WS)

(Edited to provide a reduced test case as per comments below)
I'm facing an odd situation, where if I fork a "WSS" connection to send a message, the socket gets closed when the child exits. However, when I fork to process a "WS" connection, the connection remains open when the child exits.
Server Details: Perl 5.26, Ubuntu 16
Client Details: Perl 5.18, OSX
Server code:
use Net::WebSocket::Server;
use IO::Socket::SSL;
$SIG{CHLD}='IGNORE';
my $enable_ssl = 1; # If you make this one the problem reveals itself
# you need to point this to your own certs
my $ssl_cert_file = "/etc/letsencrypt/live/mydomain/fullchain.pem";
my $ssl_key_file = "/etc/letsencrypt/live/mydomain/privkey.pem";
# To show the problem, all I'm doing is I'm forking and sending current time
sub process {
my $serv = shift;
my $pid = fork();
if ($pid == 0 ) {
print ("fork start\n");
$_->send_utf8(time) for $serv->connections;
print ("fork end\n");
exit 0;
}
}
my $ssl_server;
if ($ssl_enable) {
$ssl_server = IO::Socket::SSL->new(
Listen => 10,
LocalPort => 9000,
Proto => 'tcp',
Reuse => 1,
ReuseAddr => 1,
SSL_cert_file => $ssl_cert_file,
SSL_key_file => $ssl_key_file
);
}
Net::WebSocket::Server->new(
listen => $enable_ssl? $ssl_server: 9000,
tick_period=>5,
on_tick=> sub {
my ($serv) = #_;
process($serv);
#$_->send_utf8(time) for $serv->connections;
},
)->start;
Here is the client code:
my $client = AnyEvent::WebSocket::Client->new;
# replace with your server
$client->connect("wss://myserver:9000")->cb(sub {
our $connection = eval { shift->recv };
if($#) {
print ("connection error");
warn $#;
return;
}
# recieve message from the websocket...
$connection->on(each_message => sub {
my($connection, $message) = #_;
my $msg = $message->body;
print ("GOT $msg\n");
});
});
AnyEvent->condvar->recv;
Expected behavior
The client will keep displaying timestamps
Observed behavior
The client gets the very first message and prints it.
When the server exits its fork, the client stops getting any more messages and the connection terminates
How to make it work
We have two options:
Don't fork in server. Send the message directly in process sub
Don't use SSL
Therefore, my conclusion is SSL+fork == problem.
Thoughts?
Therefore, my conclusion is SSL+fork == problem.
Yes, the problem is first doing the SSL handshake and then forking. This way a user space SSL state will be created in the parent and with the fork duplicated in the child and these two SSL states get out of sync on the first SSL data send or received. This means it is not possible to deal with the same SSL socket from two processes.
If it is really necessary that both parent and child process use the same SSL connection to the peer than the child must use the parent as a "proxy", i.e. the child does not communicate directly with the SSL peer but the child needs to communicate in plain with the parent (for example by using a socketpair) which then can forward the communication to the SSL peer. This way the SSL state is only maintained in the parent process.
But given that only a single message should be handled at a time for a single connection it might be possible instead to not fork for a single tick but fork a child for each connection which handles then all messages in this connection. In this case the SSL handshake can be done in full in the child by listening in the parent to a TCP and not SSL socket, forking on_connect and then upgrading the connection to SSL in the client using IO::Socket::start_SSL. This would also have the advantage that the blocking SSL handshake (which involves several round trips and thus takes some time) would be done in the forked child and would not make the parent block.

Override DNS For Specific Domains Like A Hosts File, But Without Using Hosts file

I need to issue a series of parallelized web requests from the same server to a specific domain, but control what IP address these requests actually go to. Originally, I came up with a scheme where I would request the IP I wanted specifically, and then manually set the Host: www.example.com header on the request, and use a series of handlers to make sure that redirects issued followed the same pattern.
This seemed to work for some time, but lately I've been having trouble with redirects to HTTPS. The handshake will fail, and the request in turn. I have tried disabling SSL verification in a variety of ways, including:
local $ENV{ PERL_LWP_SSL_VERIFY_HOSTNAME } = 0;
local $ENV{ HTTPS_DEBUG } = 1;
$ua->ssl_opts(
SSL_ca_file => Mozilla::CA::SSL_ca_file(),
verify_hostname => 0,
SSL_verify_mode => 0x00,
);
IO::Socket::SSL::set_ctx_defaults(
SSL_verifycn_scheme => 'www',
SSL_verify_mode => 0,
);
I have also tried using LWP::UserAgent::DNS::Hosts to solve the problem, but it persists.
<edit>I should note that the reason why turning off peer validation for SSL is not solving the problem is likely because for some reason requesting this way is actually causing the handshake to fail, not failing on a validation point.</edit>
One thing that works is making an entry in /etc/hosts to point the domain at the appropriate IP, however this is not practical, because I may need to run tens, or hundreds, of tests, in parallel, on the same domain.
Is there a way to emulate the functionality of adding an entry to /etc/hosts that does not involve requesting the IP specifically and overriding the Host: ... HTTP header?
EDIT: SSL Debug Info
DEBUG: .../IO/Socket/SSL.pm:1914: new ctx 140288835318480
DEBUG: .../IO/Socket/SSL.pm:402: socket not yet connected
DEBUG: .../IO/Socket/SSL.pm:404: socket connected
DEBUG: .../IO/Socket/SSL.pm:422: ssl handshake not started
DEBUG: .../IO/Socket/SSL.pm:455: not using SNI because hostname is unknown
DEBUG: .../IO/Socket/SSL.pm:478: set socket to non-blocking to enforce timeout=180
DEBUG: .../IO/Socket/SSL.pm:491: Net::SSLeay::connect -> -1
DEBUG: .../IO/Socket/SSL.pm:501: ssl handshake in progress
DEBUG: .../IO/Socket/SSL.pm:511: waiting for fd to become ready: SSL wants a read first
DEBUG: .../IO/Socket/SSL.pm:531: socket ready, retrying connect
DEBUG: .../IO/Socket/SSL.pm:491: Net::SSLeay::connect -> -1
DEBUG: .../IO/Socket/SSL.pm:1388: SSL connect attempt failed with unknown error
DEBUG: .../IO/Socket/SSL.pm:497: fatal SSL error: SSL connect attempt failed with unknown error error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
DEBUG: .../IO/Socket/SSL.pm:1948: free ctx 140288835318480 open=140288835318480
DEBUG: .../IO/Socket/SSL.pm:1953: free ctx 140288835318480 callback
DEBUG: .../IO/Socket/SSL.pm:1956: OK free ctx 140288835318480
And in the response I get:
Can't connect to redacted.org:443
SSL connect attempt failed with unknown error error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure at /System/Library/Perl/Extras/5.18/LWP/Protocol/http.pm line 51.
It fails equally well on our server (using an older legacy version of Perl, which I will not disclose here as it seems irrelevant).
The server initially responds to a non-HTTPS request with a 301 redirect to the HTTPS site. Then the failure occurs. I will post reproducing code with the specific details of my request removed, but any site which redirects non-HTTPS traffic to HTTPS should suffice.
use IO::Socket::SSL qw/ debug4 /;
use LWP::UserAgent;
use LWP::UserAgent::DNS::Hosts;
use HTTP::Request;
use Mozilla::CA;
use Data::Dumper;
LWP::UserAgent::DNS::Hosts->register_hosts(
'recacted.org' => '127.0.0.1', # no I am not redirecting to loopback in reality, this is anonymized
'www.redacted.org' => '127.0.0.1',
);
LWP::UserAgent::DNS::Hosts->enable_override;
my $ua = LWP::UserAgent->new;
$ua->ssl_opts( SSL_ca_file => Mozilla::CA::SSL_ca_file() );
my $request = HTTP::Request->new(GET => 'http://redacted.org/');
my $response = $ua->request($request);
print $response->content; #Dumper ( $response->is_success ? $response->headers : $response );
Again, that is not the production code, just enough code to reproduce the issue. It doesn't seem to have anything to do with SSL verification, but moreover an inability to negotiate the request, presumably because LWP::UserAgent::DNS::Hosts is doing exactly what I was doing: changing the request target to the desired IP, and then writing the Host: ... header manually. Why this causes the SSL handshake to fail, I do not know.
On my local machine debugging
openssl version -a: 1.0.2j 26 Sep 2016
IO::Socket::SSL->VERSION == 1.966
Net::SSLeay->VERSION == 1.72
On a server of ours
openssl version -a: 1.0.1t 3 May 2016
IO::Socket::SSL->VERSION == 1.76
Net::SSLeay->VERSION == 1.48
Given that it works with an explicit /etc/hosts file but not with just replacing PeerAddr or using LWP::UserAgent::DNS::Hosts this looks like a problem with the SNI extension. This TLS extension is used to provide the TLS server with the requested hostname (similar to the HTTP Host header) so that it can choose the appropriate certificate. If this SNI extension is missing some servers return a default certificate while others throw an error, like in this case.
The fix is to provide the hostname using SSL_hostname in ssl_opts. Such fix could probably also help with LWP::UserAgent::DNS::Hosts, i.e in LWP/Protocol/https/hosts.pm:
12 if (my $peer_addr = LWP::UserAgent::DNS::Hosts->_registered_peer_addr($host)) {
13 push #opts, (
14 PeerAddr => $peer_addr,
15 Host => $host,
16 SSL_verifycn_name => $host,
NEW SSL_hostname => $host, # for SNI
17 );
18 }

Answer Browser Request with IO::Socket::SSL

Question formulated in the end, first detailed description of the problem and what I have tested already
I'm writing some code to showcase some basic principles to others. The code will never be productive and simplification is intended.
My goal (appart from others) is to write a simple application which uses a Web-Certificat to encrypt its network traffic.
The starting point is an application which does not encrypt:
#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket::INET;
# auto-flush on socket
$| = 1;
# creating a listening socket
my $socket = new IO::Socket::INET (
LocalAddr => '0.0.0.0', # local server address
LocalPort => '7777', # local server port
Listen => 5, # queue size for connections
Proto => 'tcp', # protocol used
);
die "cannot create socket $!\n" unless $socket;
print "server waiting for client connection on port 7777\n";
while(1)
{
# waiting for a new client connection
my $client_socket = $socket->accept() or die "socket accept failed $!";
# get information about a newly connected client
my $client_address = $client_socket->peerhost();
my $client_port = $client_socket->peerport();
print "connection from $client_address:$client_port\n";
# read up to 1024 characters from the connected client
my $client_data = '';
sysread( $client_socket, $client_data, 1024);
print "received data: $client_data\n";
# write response data to the connected client
print $client_socket "Hey $client_data!";
# notify client that response has been sent
shutdown($client_socket, 1);
}
END {
$socket->close();
}
This Server-Application can be called with this Client:
#!/usr/bin/env perl
use IO::Socket::INET;
# auto-flush on socket
$| = 1;
# create a connecting socket
my $socket = new IO::Socket::INET (
PeerHost => '127.0.0.1',
PeerPort => '7777',
Proto => 'tcp',
);
die "cannot connect to the server $!\n" unless $socket;
print "connected to the server\n";
# data to send to a server
my $req = $ARGV[0] . '';
print $socket $req;
# notify server that request has been sent
shutdown($socket, 1);
# receive a response of up to 1024 characters from server
my $response = '';
sysread( $socket, $response,1024);
print "received response: $response\n";
$socket->close();
An interaction of Client and Server could look like this:
inet$ perl server.pl
server waiting for client connection on port 7777
connection from 127.0.0.1:40028
received data: Herbert
inet$ perl client.pl "Herbert"
connected to the server
received response: Hey Herbert!
The cool thing is: The server can also be called from a browser:
So the first conclusion: The code works and is good to showcase basic functionality of a simple Client-Server interaction.
Now the Programs should use SSL to communicate
The Server and Client are written so, that SSL-Ability can be achieved by just adding a few lines in the code:
$ diff inet/server.pl ssl/server.pl
7c7
< use IO::Socket::INET;
---
> use IO::Socket::SSL 'inet4';
13c13
< my $socket = new IO::Socket::INET (
---
> my $socket = IO::Socket::SSL->new (
17a18,19
> SSL_cert_file => 'cert.pem', # SSL certificate
> SSL_key_file => 'key.pem', # SSL certificate key
$ diff inet/client.pl ssl/client.pl
5c5
< use IO::Socket::INET;
---
> use IO::Socket::SSL 'inet4';
11c11
< my $socket = new IO::Socket::INET (
---
> my $socket = new IO::Socket::SSL (
14a15
> SSL_ca_file => 'cert.pem',
So the new SSL enabled code is:
#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket::SSL 'inet4';
# auto-flush on socket
$| = 1;
# creating a listening socket
my $socket = IO::Socket::SSL->new (
LocalAddr => '0.0.0.0', # local server address
LocalPort => '7777', # local server port
Listen => 5, # queue size for connections
Proto => 'tcp', # protocol used
SSL_cert_file => 'cert.pem', # SSL certificate
SSL_key_file => 'key.pem', # SSL certificate key
);
die "cannot create socket $!\n" unless $socket;
print "server waiting for client connection on port 7777\n";
while(1)
{
# waiting for a new client connection
my $client_socket = $socket->accept() or die "socket accept failed $!";
# get information about a newly connected client
my $client_address = $client_socket->peerhost();
my $client_port = $client_socket->peerport();
print "connection from $client_address:$client_port\n";
# read up to 1024 characters from the connected client
my $client_data = '';
sysread( $client_socket, $client_data, 1024);
print "received data: $client_data\n";
# write response data to the connected client
print $client_socket "Hey $client_data!";
# notify client that response has been sent
shutdown($client_socket, 1);
}
END {
$socket->close();
}
and
#!/usr/bin/env perl
use IO::Socket::SSL 'inet4';
# auto-flush on socket
$| = 1;
# create a connecting socket
my $socket = new IO::Socket::SSL (
PeerHost => '127.0.0.1',
PeerPort => '7777',
Proto => 'tcp',
SSL_ca_file => 'cert.pem',
);
die "cannot connect to the server $!\n" unless $socket;
print "connected to the server\n";
# data to send to a server
my $req = $ARGV[0] . '';
print $socket $req;
# notify server that request has been sent
shutdown($socket, 1);
# receive a response of up to 1024 characters from server
my $response = '';
sysread( $socket, $response,1024);
print "received response: $response\n";
$socket->close();
To run the code, a certificate has to be created first:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
Now Server and Client can be started and interact nicely:
ssl$ perl server.pl
Enter PEM pass phrase:
server waiting for client connection on port 7777
connection from 127.0.0.1:40041
received data: Sabine
ssl$ perl client.pl "Sabine"
connected to the server
received response: Hey Sabine!
But what does not work is a connection from a browser like Firefox or Chrome, even though I converted the certificate using:
openssl pkcs12 -export -in cert.pem -inkey key.pem -out webcert.p12
I also imported the newly created certificate into the browser over its import menu.
The connection just gets refused. The browser can't connect and the server fails at $socket->accept() without any helpful message.
UPDATE: There is a message in the exported variable $SSL_ERROR:
SSL accept attempt failed error:1407609C:SSL routines:SSL23_GET_CLIENT_HELLO:http request
I did some testing with the tool analyze ssl
p5-ssl-tools-master$ perl analyze-ssl.pl --show-chain --all-ciphers -v3 127.0.0.1:7777
+ checking host=127.0.0.1(127.0.0.1) port=7777
* version SSLv23 no verification, ciphers= -> TLSv1_2,ECDHE-RSA-AES128-GCM-SHA256
* version SSLv23 no verification, ciphers=HIGH:ALL -> TLSv1_2,ECDHE-RSA-AES128-GCM-SHA256
* version TLSv1_2 no verification, ciphers= -> TLSv1_2,ECDHE-RSA-AES128-GCM-SHA256
* version TLSv1_2 no verification, ciphers=HIGH:ALL -> TLSv1_2,ECDHE-RSA-AES128-GCM-SHA256
* version TLSv1_1 no verification, ciphers= -> TLSv1_1,ECDHE-RSA-AES256-SHA
* version TLSv1_1 no verification, ciphers=HIGH:ALL -> TLSv1_1,ECDHE-RSA-AES256-SHA
* version TLSv1 no verification, ciphers= -> TLSv1,ECDHE-RSA-AES256-SHA
* version TLSv1 no verification, ciphers=HIGH:ALL -> TLSv1,ECDHE-RSA-AES256-SHA
* version SSLv3, no verification, ciphers= -> FAIL! SSL connect attempt failed because of handshake problems error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
+ 127.0.0.1 failed permanently 'tcp connect: Verbindungsaufbau abgelehnt', no more IP to try
tcp connect: Verbindungsaufbau abgelehnt
The server seems to accept certain requests and fails at the end, maybe in the same way as with the browser?
ssl$ perl server.pl
Enter PEM pass phrase:
server waiting for client connection on port 7777
connection from 127.0.0.1:40042
received data:
connection from 127.0.0.1:40043
received data:
connection from 127.0.0.1:40044
received data:
connection from 127.0.0.1:40045
received data:
connection from 127.0.0.1:40046
received data:
connection from 127.0.0.1:40047
received data:
connection from 127.0.0.1:40048
received data:
connection from 127.0.0.1:40049
received data:
socket accept failed at server.pl line 27.
My Question
Why is the request with the browser not working?
The code should support a browser request in general since it works without SSL.
It seems to be a browser specific SSL setting since the SSL-Perl-Client has no problem.
Or is the import of the certificate into the browser not working as intended?
Can anyone give me a hint or solution in this matter?
UPDATE: When I look at the error message "SSL23" in $SSL_ERROR and the error message in the client test "SSL3" there seems to be a compatibility problem with SSL versions? Do I need to specify explicitly which version I want? (On the other hand the Client-Test for "SSL23" seems to run through successfully...)
Thanks a lot.
UPDATE: Output with $IO::Socket::SSL::DEBUG = 3;
ssl$ perl server.pl
Enter PEM pass phrase:
DEBUG: .../IO/Socket/SSL.pm:2554: new ctx 42708208
server waiting for client connection on port 7777
DEBUG: .../IO/Socket/SSL.pm:799: no socket yet
DEBUG: .../IO/Socket/SSL.pm:801: accept created normal socket IO::Socket::SSL=GLOB(0x28ac158)
DEBUG: .../IO/Socket/SSL.pm:829: starting sslifying
DEBUG: .../IO/Socket/SSL.pm:873: Net::SSLeay::accept -> -1
DEBUG: .../IO/Socket/SSL.pm:1779: SSL accept attempt failed
DEBUG: .../IO/Socket/SSL.pm:1784: SSL accept attempt failed error:1407609C:SSL routines:SSL23_GET_CLIENT_HELLO:http request
socket accept failed: SSL accept attempt failed error:1407609C:SSL routines:SSL23_GET_CLIENT_HELLO:http request at server.pl line 28.
DEBUG: .../IO/Socket/SSL.pm:2587: free ctx 42708208 open=42708208
DEBUG: .../IO/Socket/SSL.pm:2599: OK free ctx 42708208
... SSL accept attempt failed error:1407609C:SSL routines:SSL23_GET_CLIENT_HELLO:http request
My guess is that you still use a http:// URL to access the server even though you would need to use a https:// URL. This means that the browser sends a HTTP request to the server instead of first doing the TLS handshake and only sending the HTTP request after the successful handshake.
Apart from that you count on the browser understanding the old and long deprecated HTTP 0.9 protocol which consists of only the response body without any HTTP header. Not all browser understand this.

Trust self-signed certificate for Email::Sender::Transport::SMTPS in perl

Trying to use Email::Sender::Transport::SMTPS for sending email. My transport is:
my $transport = Email::Sender::Transport::SMTPS->new({
host => $smtpserver,
ssl => 'starttls',
sasl_username => $smtpuser,
sasl_password => $smtppassword,
debug => 1,
});
When trying send the email, the debug says:
Net::SMTPS=GLOB(0x7f893b2b00f0)<<< 250-SIZE 52428800
Net::SMTPS=GLOB(0x7f893b2b00f0)<<< 250-8BITMIME
Net::SMTPS=GLOB(0x7f893b2b00f0)<<< 250-PIPELINING
Net::SMTPS=GLOB(0x7f893b2b00f0)<<< 250-STARTTLS
Net::SMTPS=GLOB(0x7f893b2b00f0)<<< 250 HELP
Net::SMTPS=GLOB(0x7f893b2b00f0)>>> STARTTLS
Net::SMTPS=GLOB(0x7f893b2b00f0)<<< 220 TLS go ahead
DEBUG: .../IO/Socket/SSL.pm:735: local error: SSL connect attempt failed error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed
DEBUG: .../IO/Socket/SSL.pm:738: fatal SSL error: SSL connect attempt failed error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed
unable to establish SMTP connection
Probably because the server using self-signed certificate. (When using the same setting in the Thunderbird it is needed to add the "trust this certificate" setting.)
The question is: How to add the "trust this certificate" for the Email::Sender::Transport::SMTPS - thus allow sending email.
There is no direct way to give Email::Sender::Transport::SMTPS SSL specific arguments. But, since ultimately IO:Socket::SSL is used you can hack your way around this limitation. From the man page:
This is a list of typical errors seen with the use of IO::Socket::SSL:. ....
Make SSL settings inacessible by the user, together with bad builtin settings.
Some modules use IO::Socket::SSL, but don't make the SSL settings available to the user. This is often combined with bad builtin settings or defaults (like switching verification off). Thus the user needs to hack around these restrictions by using set_args_filter_hack or similar.
Thus what you could do is
IO::Socket::SSL::set_args_filter_hack( sub {
my ($is_server,$args) = #_;
$args->{SSL_fingerprint} = 'sha1$437104....'
});
With this option you could make certificate pinning trust a certificate as long as it's fingerprint matches the given one. You can get the fingerprint for instance with:
openssl s_client -connect mail.example.org:25 -starttls smtp |\
openssl x509 -fingerprint -noout
Alternative ways would be to use the certificate as trusted with the SSL_ca_file option. You could also set SSL_verify_mode to 0 (SSL_VERFY_NONE) but since this disables any kind of validation you should only use this for testing.
Please note that set_args_filter_hack is global, that is it affects all IO::Socket::SSL objects in your program. Thus depending on the program you should only set it directly before you establish the connection with Email::Sender::Transport::SMTPS and reset it immediately afterwards.
For more information about these options please read the documentation of IO::Socket::SSL.
You can control the SSL usage with the options of new() constructor method
"SSL_verify_mode"=>'SSL_VERIFY_NONE'
IN Email/Sender/Transport/SMTPS.pm
# compatible
my $ssl = $self->ssl;
$ssl = 'ssl' if $self->ssl and $self->ssl ne 'starttls';
return (
$self->host,
Port => $self->port,
Timeout => $self->timeout,
defined $ssl ? (doSSL => $ssl) : (),
defined $self->helo ? (Hello => $self->helo) : (),
defined $self->localaddr ? (LocalAddr => $self->localaddr) : (),
defined $self->localport ? (LocalPort => $self->localport) : (),
defined $self->debug ? (Debug => $self->debug) : (),
"SSL_verify_mode"=>'SSL_VERIFY_NONE',#ADDED LINE
);
EDIT: This is really bad, because as per comments you trust everything. Instead if on linux/*nix
cat tobetrusted.crt >> /etc/ssl/cert.pem