|
|
|
|
| |
Credit:
The information has been provided by GomoR.
The original article can be found at: http://www.gomor.org/bin/view/GomorOrg/SslNetcat#Download
|
| |
You all know what is netcat (written by Hobbit in 1996), how to use it and that it should have been integrated in all UNIX systems a long time ago. netcat lacked some features, and I tried to add them in this Perl version. For example, SSL support, TCP and UDP proxying and IPv4/IPv6 proxying features.
Tool source:
#!/usr/bin/perl
#
# $Id: scnc 13 2008-04-27 10:53:59Z gomor $
#
our $SupportSsl = 0;
our $SupportTelnet = 0;
our $SupportIpv6 = 0;
package Scnc;
use warnings; use strict;
use Carp;
use IO::Socket::INET;
use IO::Select;
eval("use IO::Socket::SSL;");
$@ ? warn("*** IO::Socket::SSL module not found, SSL support disabled\n")
: $SupportSsl++;
eval("use Net::Telnet;");
$@ ? warn("*** Net::Telnet module not found, telnet negociation disabled\n")
: $SupportTelnet++;
eval("use IO::Socket::INET6;");
$@ ? warn("*** IO::Socket::INET6 module not found, IPv6 support disabled\n")
: $SupportIpv6++;
sub new {
my $self = shift;
bless({
_s => undef,
@_,
}, $self);
}
sub resolv {
my ($self) = @_;
if ($SupportIpv6 && $self->{6}) {
my @res = Socket6::getaddrinfo($self->{s}, 'ftp', AF_INET6, SOCK_STREAM);
if (@res >= 5) {
my $saddr = $res[3];
my ($ip) = Socket6::getnameinfo(
$res[3], Socket6::NI_NUMERICHOST()|Socket6::NI_NUMERICSERV());
return $ip;
}
die("resolv: unable to resolv host: ".$self->{s}." [$!]\n");
}
else {
my $saddr = gethostbyname($self->{s})
or die("resolv: unable to resolv host: ".$self->{s}." [$!]\n");
return inet_ntoa($saddr);
}
}
sub init {
my ($self) = @_;
$SIG{INT} = sub { $self->exit };
$SIG{CHLD} = 'IGNORE';
STDOUT->blocking(0);
STDOUT->autoflush(1);
STDIN->blocking(0);
STDIN->autoflush(1);
# Set default values
if (! defined($self->{e})) {
$self->{e} = '';
}
if (! defined($self->{s})) {
($SupportIpv6 && $self->{6}) ? do { $self->{s} = '::'; }
: do { $self->{s} = '0.0.0.0'; };
}
if (! defined($self->{p})) {
$self->{p} = 0;
}
$self->{s} = $self->resolv;
# Sanity checks
if ($self->{l} && $self->{r}) {
delete($self->{l});
}
if ($self->{u} && $self->{c}) {
die("init: SSL does not work over UDP\n");
}
}
sub client {
my ($self) = @_;
my $inet;
my %args = (
PeerHost => $self->{s},
PeerPort => $self->{p},
Proto => $self->{u} ? 'udp' : 'tcp',
);
if ($SupportIpv6 && $self->{6}) {
$inet = "IO::Socket::INET6";
$args{Domain} = AF_INET6;
}
elsif ($SupportSsl && $self->{c}) {
$inet = "IO::Socket::SSL";
$args{Domain} = ($SupportIpv6 && $self->{6}) ? AF_INET6 : AF_INET;
if ($self->{a} && $self->{f} && $self->{k}) {
$args{SSL_server} = 0;
$args{SSL_use_cert} = 1;
$args{SSL_ca_file} = $self->{a};
$args{SSL_cert_file} = $self->{f};
$args{SSL_key_file} = $self->{k};
}
}
else {
$inet = "IO::Socket::INET";
$args{Domain} = AF_INET;
}
my $s = $inet->new(%args);
if (! defined($s)) {
if ($SupportSsl && $self->{c}) {
die("client: SSL error connecting to: ".$self->{s}.":".$self->{p}.
" [".IO::Socket::SSL::errstr()."]");
}
else {
die("client: error connecting to: ".$self->{s}.":".$self->{p}.
" [$!]");
}
($SupportIpv6 && $self->{6}) ? print " (IPv6)\n" : print " (IPv4)\n";
}
$s->blocking(0);
$s->autoflush(1);
$self->{_s} = $s;
# We MUST use connected UDP, otherwise syswrite() and sysread() do not work
if ($self->{u}) {
if ($SupportIpv6 && $self->{6}) {
connect($s, Socket6::sockaddr_in6($self->{p},
Socket6::inet_pton(AF_INET6, $self->{s})))
or die("client: error doing connect() [$!]\n");
}
else {
connect($s, sockaddr_in($self->{p}, inet_aton($self->{s})))
or die("client: error doing connect() [$!]\n");
}
$s->syswrite("\0"); # So the server will know we are here,
# and will connect() the socket on its side.
}
if ($self->{v}) {
print "client: connected to: ".$self->{s}.":".$self->{p};
($SupportIpv6 && $self->{6}) ? print " (IPv6)\n" : print " (IPv4)\n";
if ($self->{c}) {
print "client: using cipher: ".$s->get_cipher."\n";
}
}
}
sub _rwLoop {
my ($self, $in, $out, $err) = @_;
my $s = IO::Select->new;
$s->add($self->{_s});
$s->add($in);
while (my @read = $s->can_read) {
my $toRead = 1024; # We read by chunk of 1024 bytes
for my $this (@read) {
if ($this == $self->{_s}) {
while (defined(my $r = $self->{_s}->sysread(my $buf, $toRead))) {
if ($r > 0) { print $out $buf; last if $r <= $toRead; }
else { return; }
}
}
elsif ($this == $in) {
my $toWrite = '';
while (defined(my $r = $in->sysread(my $buf, $toRead))) {
if ($r > 0) { $toWrite .= $buf; }
}
if (length($toWrite)) { $self->{_s}->syswrite($toWrite); }
}
}
}
}
sub clientLoop {
my ($self, $in, $out, $err) = @_;
if (length($self->{e})) {
socketpair(CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
or die("clientLoop: socketpair [$!]\n");
CHILD->blocking(0);
CHILD->autoflush(1);
PARENT->blocking(0);
PARENT->autoflush(1);
defined(my $pid = fork()) or die("clientLoop: fork [$!]\n");
if ($pid == 0) {
my $parent = fileno(PARENT);
open(STDIN, "<&=$parent");
open(STDOUT, ">&=$parent");
open(STDERR, ">&=$parent");
system($self->{e});
exit(0);
}
else {
my $child = fileno(CHILD);
open(STDIN, "<&=$child");
open(STDOUT, ">&=$child");
open(STDERR, ">&=$child");
$self->_rwLoop(\*STDIN, \*STDOUT, \*STDERR);
}
}
else {
$self->_rwLoop($in, $out, $err);
}
}
sub server {
my ($self) = @_;
my $inet;
my %args = (
LocalAddr => $self->{s},
LocalPort => $self->{p},
Proto => $self->{u} ? 'udp' : 'tcp',
Type => $self->{u} ? SOCK_DGRAM : SOCK_STREAM,
ReuseAddr => 1,
);
$args{Listen} = 10 if ! $self->{u};
if ($SupportIpv6 && $self->{6}) {
$inet = "IO::Socket::INET6";
$args{Domain} = AF_INET6;
}
elsif ($SupportSsl && $self->{c}) {
$inet = "IO::Socket::SSL";
$args{Domain} = ($SupportIpv6 && $self->{6}) ? AF_INET6 : AF_INET;
if ($self->{a} && $self->{f} && $self->{k}) {
$args{SSL_server} = 1;
$args{SSL_use_cert} = 1;
$args{SSL_ca_file} = $self->{a};
$args{SSL_cert_file} = $self->{f};
$args{SSL_key_file} = $self->{k};
}
}
else {
$inet = "IO::Socket::INET";
$args{Domain} = AF_INET;
}
my $s = $inet->new(%args);
if (! defined($s)) {
if ($SupportSsl && $self->{c}) {
die("server: SSL error listening on: ".$self->{s}.":".$self->{p}.
" [".IO::Socket::SSL::errstr()."]");
}
else {
die("server: error listening on: ".$self->{s}.":".$self->{p}.
" [$!]");
}
($SupportIpv6 && $self->{6}) ? print " (IPv6)\n" : print " (IPv4)\n";
}
$s->blocking(0);
$s->autoflush(1);
$self->{_s} = $s;
if ($self->{v}) {
if ($SupportSsl && $self->{c}) {
print("server: SSL listening on: ".$self->{s}.":".$self->{p});
}
else {
print("server: listening on: ".$self->{s}.":".$self->{p});
}
($SupportIpv6 && $self->{6}) ? print " (IPv6)\n" : print " (IPv4)\n";
}
}
sub serverLoop {
my ($self, $in, $out, $err) = @_;
my $s = IO::Select->new;
$s->add($self->{_s});
open(my $oldout, ">&STDOUT");
while (my @ready = $s->can_read) {
if ($self->{u}) {
if (my $new = $self->{_s}->recv(my $tmp, 0)) {
my ($port, $saddr, $ipaddr);
if ($SupportIpv6 && $self->{6}) {
($port, $saddr) = Socket6::sockaddr_in6($new);
$ipaddr = Socket6::inet_ntop(AF_INET6, $saddr);
}
else {
($port, $saddr) = sockaddr_in($new);
$ipaddr = inet_ntoa($saddr);
}
if ($self->{v}) {
print $oldout "serverLoop: connection from: $ipaddr:$port\n";
if ($self->{c}) {
print "serverLoop: using cipher: ".$new->get_cipher."\n";
}
}
# We MUST use connected UDP, otherwise syswrite() and sysread()
# do not work
if ($SupportIpv6 && $self->{6}) {
connect($self->{_s}, Socket6::sockaddr_in6($port, $saddr))
or die("serverLoop: error doing connect() [$!]\n");
}
else {
connect($self->{_s}, sockaddr_in($port, $saddr))
or die("serverLoop: error doing connect() [$!]\n");
}
$self->clientLoop($in, $out, $err);
}
}
elsif (my $new = $self->{_s}->accept) {
if ($self->{v}) {
print $oldout "serverLoop: connection from: ".$new->peerhost.":".
$new->peerport."\n";
if ($self->{c}) {
print "serverLoop: using cipher: ".$new->get_cipher."\n";
}
}
$new->blocking(0);
$new->autoflush(1);
defined(my $pid = fork()) or die("serverLoop: fork [$!]\n");
if ($pid == 0) { # Son process
$self->{_s} = $new;
$self->clientLoop($in, $out, $err);
if ($self->{v}) {
print $oldout "serverLoop: client ".$new->peerhost.":".
$new->peerport." disconnected\n";
}
exit(0);
}
}
}
}
sub proxy {
my ($self) = @_;
$self->server;
}
sub proxyLoop {
my ($self, $in, $out, $err) = @_;
my $s = IO::Select->new;
$s->add($self->{_s});
open(my $oldout, ">&STDOUT");
while (my @ready = $s->can_read) {
if ($self->{u}) {
if (my $new = $self->{_s}->recv(my $tmp, 0)) {
my ($port, $saddr, $ipaddr);
if ($SupportIpv6 && $self->{6}) {
($port, $saddr) = Socket6::sockaddr_in6($new);
$ipaddr = Socket6::inet_ntop(AF_INET6, $saddr);
}
else {
($port, $saddr) = sockaddr_in($new);
$ipaddr = inet_ntoa($saddr);
}
if ($self->{v}) {
print $oldout "proxyLoop: connection from: $ipaddr:$port\n";
if ($self->{c}) {
print "proxyLoop: using cipher: ".$new->get_cipher."\n";
}
}
# We MUST use connected UDP, otherwise syswrite() and sysread()
# do not work
if ($SupportIpv6 && $self->{6}) {
connect($self->{_s}, Socket6::sockaddr_in6($port, $saddr))
or die("proxyLoop: error doing connect() [$!]\n");
}
else {
connect($self->{_s}, sockaddr_in($port, $saddr))
or die("proxyLoop: error doing connect() [$!]\n");
}
my $new = $self->{_s};
my ($host, $dport, $v6, $ssl) = split(':', $self->{r});
$self->{6} = (defined($v6) && $v6 =~ /ipv6/i) ? 1 : 0;
$self->{c} = (defined($ssl)) ? 1 : 0;
$self->{s} = $host; $self->resolv;
$self->{p} = $dport;
$self->client;
$self->clientLoop($new, $new, $new);
}
}
elsif (my $new = $self->{_s}->accept) {
print $oldout "proxyLoop: connection from: ".$new->peerhost.":".
$new->peerport."\n"
if $self->{v};
$new->blocking(0);
$new->autoflush(1);
defined(my $pid = fork()) or die("proxyLoop: fork [$!]\n");
if ($pid == 0) { # Son process
my ($host, $port, $v6, $ssl) = split(':', $self->{r});
$self->{6} = (defined($v6) && $v6 =~ /ipv6/i) ? 1 : 0;
$self->{c} = (defined($ssl)) ? 1 : 0;
$self->{s} = $host; $self->resolv;
$self->{p} = $port;
$self->client;
$self->clientLoop($new, $new, $new);
print $oldout "proxyLoop: client ".$new->peerhost.":".
$new->peerport." disconnected\n"
if $self->{v};
exit(0);
}
}
}
}
sub run {
my ($self) = @_;
$self->init;
if ($self->{l}) {
$self->server;
$self->serverLoop(\*STDIN, \*STDOUT, \*STDERR);
}
elsif ($self->{r}) {
$self->proxy;
$self->proxyLoop(\*STDIN, \*STDOUT, \*STDERR);
}
else {
$self->client;
$self->clientLoop(\*STDIN, \*STDOUT, \*STDERR);
}
$self->exit;
}
sub exit {
my ($self) = @_;
$self->{_s}->close if defined($self->{_s});
exit;
}
1;
package main;
use warnings; use strict;
my $prog = "scnc";
my $progname = "SSL Capable NetCat 1.00";
use Getopt::Std;
my $opts = '';
my $usage = "$progname\n\nUsage: $prog [-options] target port\n\n";
if ($SupportSsl) {
$usage .= " -c use SSL (default to not)\n";
$usage .= " -a use SSL certificate authority file\n";
$usage .= " -f use SSL certificate file (PEM format)\n";
$usage .= " -k use SSL private key file (PEM format)\n";
$opts .= "ca:f:k:";
}
if ($SupportTelnet) {
$usage .= " -t do telnet negociation (default to not)\n";
$opts .= "t";
}
if ($SupportIpv6) {
$usage .= " -6 use IPv6 (default to not)\n";
$opts .= "6";
}
$usage .= " -e cmd command to execute\n";
$usage .= " -l listen for connections (default to not)\n";
$usage .= " -p port use local port number (default to random high)\n";
$usage .= " -s address use address for bindings (default to all addresses)\n";
$usage .= " -u use UDP socket (default to TCP)\n";
$usage .= " -v be verbose (default to not)\n";
$usage .= " -r host:port proxy connection to host:port\n";
$usage .= " -r host:port:ipv6 proxy connection to host:port using IPv6\n";
$usage .= " -r host:port::ssl proxy connection to host:port using SSL\n";
$usage .= " -r host:port:ipv6:ssl proxy connection to host:port using IPv6 and SSL\n";
$opts .= "e:lp:s:uvr:";
my %opts;
getopts($opts, \%opts);
$opts{p} = pop unless defined($opts{p});
$opts{s} = pop unless defined($opts{s});
if ((! $opts{l})
&& (! $opts{r})
&& (! defined($opts{s}) || ! defined($opts{p}))) {
print $usage and exit(0);
}
Scnc->new(%opts)->run;
|
|
|
|
|
|
|
|
|
|