Port hopping on IO::SOCKET::INET - perl

i am try to get a better understanding on Perl and was stumble about sockets. i am try to understand a "simple" example i found on xmodulo. I was expecting that like in the example the port will be 7777. But it seems like the port used for this socket communication is somewhere above 35000 and on every call of the client script the port is incremented by +1. Why ist the port different to the 7777 and why it is incrementing on every call?
the server example looks like this:
use IO::Socket::INET;
# auto-flush on socket
$| = 1;
# creating a listening socket
my $socket = new IO::Socket::INET (
LocalHost => '0.0.0.0',
LocalPort => '7777',
Proto => 'tcp',
Listen => 5,
Reuse => 1
);
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();
# 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 $data = "";
$client_socket->recv($data, 1024);
print "received data: $data\n";
# write response data to the connected client
$data = "ok";
$client_socket->send($data);
# notify client that response has been sent
shutdown($client_socket, 1);
}
$socket->close();
the client example is:
use IO::Socket::INET;
# auto-flush on socket
$| = 1;
# create a connecting socket
my $socket = new IO::Socket::INET (
PeerHost => '192.168.1.10',
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 = 'hello world';
my $size = $socket->send($req);
print "sent data of length $size\n";
# notify server that request has been sent
shutdown($socket, 1);
# receive a response of up to 1024 characters from server
my $response = "";
$socket->recv($response, 1024);
print "received response: $response\n";
$socket->close();

A connection isn't defined by
local address
peer address
port
It's defined by
local address
local port
peer address
peer port
For example,
>netstat /a
Active Connections
Proto Local Address Foreign Address State
...
TCP 10.0.0.2:34208 stackoverflow:http ESTABLISHED
TCP 10.0.0.2:34212 stackoverflow:http ESTABLISHED
TCP 10.0.0.2:34213 stackoverflow:http ESTABLISHED
TCP 10.0.0.2:34224 stackoverflow:http ESTABLISHED
TCP 10.0.0.2:34226 stackoverflow:http ESTABLISHED
TCP 10.0.0.2:34227 stackoverflow:http ESTABLISHED
...
You didn't specify a local port for the client, so the system picked an available one. That's the right thing to do. There's no reason to limit the client to one port. It can even cause problems.
For example, let's say your web browser tried to bind its sockets to port 80 (the port on which web servers listen). Your web browser would only be able to have one request pending at a time. That would be bad. You want to be able to create multiple connections to the same service. This allows you to request two different images at the same time, this allows you to load two pages in two different tabs at the same time, and so on. In the example above, my machine had six connections to stackoverflow.com's port 80, but that wouldn't have been possible if the web browser has bound the socket to port 80.

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>;

IO::Socket::SSL - establishing client connection

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.

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.

perl tcp client does not display incoming data

I'm completely new to net programming and can't solve this issue
I have a server (some kind of LPT port to TCP/IP proxy) that automatically tries to establish connection at random interval. I need to listen on specific port and communicate with server. Thats the client. It works fine with perl tcp server.. But in this case it waits.... nothing happens
#!/usr/bin/perl -w
use IO::Socket;
my $sock = new IO::Socket::INET(
LocalHost => '192.168.1.1',
LocalPort => '7000',
Proto => 'tcp',
Listen => 1,
Reuse => 1,
);
die "Could not create socket: $!\n" unless $sock;
my $new_sock = $sock->accept();
while (<$new_sock>) {
print $_;
}
close($sock);
It works OK with my simple tcp server, but with this 'black-box' - nope
On client machine there are two connection:
tcp 0 0 192.168.1.1:7000 0.0.0.0:* LISTEN
tcp 0 0 192.168.1.1:7000 192.168.1.2:33822 ESTABLISHED
But if I try with
nc -l 192.168.1.1 7000
It works like a charm, data is flowing. And only one connection is present (like the second one)
tcpdump fragment of one symbol transmission captured on client. Seems to be OK
21:42:00.242172 IP (tos 0x0, ttl 64, id 30448, offset 0, flags [DF], proto TCP (6), length 53)
192.168.1.2.33837 > 192.168.1.1.7000: Flags [P.], cksum 0x5646 (correct), seq 3592562130:3592562131, ack 1351632513, win 92, options [nop,nop,TS val 140364552 ecr 92554083], length 1
I don't know what I'm doing wrong... More complicated examples are don't working for me as I don't know how to debug them...
<$new_sock>, short for readline($new_sock), only returns after a complete line is received, which is to say when newline is received.
sysread will return as soon as data is available, so you could use that.
my $rv = sysread($new_sock, my $buf, 64*1024);
Alternatively, since the lines you receive end with carriage return, you could communicate this to Perl.
$/ = "\r";

How can I tell Perl's IO::Socket::INET which interface to use?

I have two interfaces on my server, eth0 and eth0:0. Those are two different external IP addresses and obviously two different reverse domains.
When I open a IO::Socket::INET connection, Perl uses the eth0 interface by default. I would like to use the second interface (eth0:0) because this has a different IP and I dont want to use my main IP or domain.
I have absolutely no idea how to select which interface to connect through.
Here's the code I use to open a socket:
my $sock = new IO::Socket::INET(PeerAddr => $server,
PeerPort => $serverPort,
Proto => 'tcp') or
die "Can't connect to server: $!";
You have to give IO::Socket::INET the address of the interface you want to use as LocalAddr parameter. Imagine that 10.0.0.1 is the IP address of eth0 and 10.0.0.2 the IP address of eth0:0, then it would work like this.
my $sock = new IO::Socket::INET(PeerAddr => $server,
PeerPort => $serverPort,
Proto => 'tcp'
LocalAddr => '10.0.0.2') or
die "Can't connect to server: $!";