I am trying to create a remote-login script with perl. I am currently getting input data using
$var = <$client>;
chomp $var;
However, I am trying to have the client input a password and I want to hide the password in the linux fashion with the client by not echoing what is typed. Is there any way I can do this?
EDIT:
$serv = IO::Socket::INET->new (
Proto => 'tcp',
LocalPort => $port,
Listen => 10,
Reuse => 1)
|| die "Can't create server: $!";
while ($client = $serv->accept()) {
eval {
$client->autoflush(1); # Always remember to flush!
$who = $client->peerhost;
print STDERR "Connection from $who\n";
print $client hostname . " login: ";
$usr = <$client>;
chomp $usr;
$usr =~ s/\W//g;
print STDERR "User $usr\n";
die unless (length $usr < 20 && length $usr > 1);
print $client "Encrypted Password: ";
$pass = <$client>;
chomp $pass;
die unless (length $pass < 20 && length $pass > 1);
print STDERR "$who: Pass $pass\n";
};
close $client;
}
This is local console echo, nothing to do with your socket.
There are many ways to turn off console echo using Perl, but my favourite is IO::Termios (perhaps I'm biased because I wrote it ;) )
use IO::Termios;
my $stdin = IO::Termios->new(\*STDIN);
$stdin->setflag_echo(0);
Related
use strict;
use IO::Socket;
#initial variables to work with my server
my $host, $port, $request, $proto = 'tcp';
my $connectresponses = 2; #my ftp server responds with 2 lines when you connect.
print "What hostname do you want to connect to? ";
chomp( $host = <STDIN> );
print "What port do you want to use? ";
chomp( $port = <STDIN> );
my $sock = IO::Socket::INET->new(
PeerAddr => $host,
PeerPort => $port,
Proto => $proto
) || die "Failure: $!";
print "Connection to $host on port $port successful!\n";
unless ( $port == 80 ) {
for ( $i = 0; $i < $connectresponses; $i++ ) {
$_ = <$sock>;
print;
}
}
print "Type commands (solely press enter to exit)\n";
&Terminal;
close($sock);
print "\nConnection to $host on port $port was closed successfully!\n";
exit;
#sub to emulate a terminal
sub Terminal {
while (1) {
$request = "";
chomp( $request = <STDIN> );
print $sock "$request\n";
if ( $port == 80 ) {
while (<$sock>) {
print;
}
last;
} else {
unless ($request) {
last;
}
$_ = <$sock>;
print;
}
}
}
source http://www.perlmonks.org/?abspart=1;displaytype=displaycode;node_id=202955;part=4
Error:
parenthesis missing arounf "my" list at test.pl line 7
print (..) interpreted as function at test.pl line 37
Global symbol "$port" requires explicit package name at test.pl line 7
Global symbol "$request" requires explicit package name at test.pl line 7
Global symbol "$proto" requires explicit package name at test.pl line 7
Global symbol "$port" requires explicit package name at test.pl line 13
Global symbol "$port" requires explicit package name at test.pl line 15
Global symbol "$proto" requires explicit package name at test.pl line 7
test.pl has too many errors.
When you omit parens around my's arguments, it acts as unary operator. That means
my $host, $port, $request, $proto = 'tcp';
is the same as
(my $host), $port, $request, $proto = 'tcp';
You could use
my ($host, $port, $request, $proto);
$proto = 'tcp';
or
my ($host, $port, $request);
my $proto = 'tcp';
You could also use
my $host, my $port, my $request, my $proto = 'tcp';
but that's just a weird way of writing
my $host; my $port; my $request; my $proto = 'tcp';
Replace your 7th line by:
my ($host, $port, $request, $proto);
$proto = 'tcp';
Also this works (like #mpapec says):
my ($proto, $host, $port, $request) = 'tcp';
I currently have some code which returns a sites header content back:
#!/usr/bin/perl
use strict;
require IO::Socket;
my #header;
my $host = shift;
my $socket = new IO::Socket::INET(
PeerAddr => $host,
PeerPort => 80,
Proto => 'tcp') || die "Could not Connect $!\n";
print "Connected.\n";
print "Getting Header\n";
print $socket "GET / HTTP/1.0\n\n";
my $i = 0;
while (<$socket>) {
#header[$i] = $_;
$i++;
}
$i = 0;
print "--------------------------------------\n";
while ($i <= 8) {
print "#header[$i++]";
}
print "-------------------------------------\n";
print "Finished $host\n";
What I would like to do, is to be able to read from a file open (FILE, '<', shift); and then every IP in the file, to pass into a the header retrieve loop, which saves me from manually doing one by one.
What I mean by this is to have a file containing (example ips): 1.1.1.1 2.2.2.2 on each line and then parsing all of them through the get header function.
Replace
my #header;
my $host = shift;
...
with
while (<>) {
chomp( my $host = $_ );
my #header;
...
}
You would just open your file, read the contents into a list, then iterate over the list:
open my $fh, '<', $file or die "$!";
my #ips = <$fh>;
close $fh;
foreach my $ip ( #ips ) {
chomp $ip;
...
}
Why doesn't my program work? It refuses to connect to the host, I've tried two different servers and verified which port is used.
Note that I'm not very experienced when it comes to Perl.
use strict;
use Net::FTP;
use warnings;
my $num_args = $#ARGV+1;
my $filename;
my $port;
my $host;
my $ftp;
if($num_args < 2)
{
print "Usage: ftp.pl host [port] file\n";
exit();
}
elsif($num_args == 3)
{
$port = $ARGV[1];
$host = $ARGV[0];
$filename = $ARGV[2];
print "Connecting to $host on port $port.\n";
$ftp = Net::FTP->new($host, Port => $port, Timeout => 30, Debug => 1)
or die "Can't open $host on port $port.\n";
}
else
{
$host = $ARGV[0];
$filename = $ARGV[1];
print "Connecting to $host with the default port.\n";
$ftp = Net::FTP->new($host, Timeout => 30, Debug => 1)
or die "Can't open $host on port $port.\n";
}
print "Usename: ";
my $username = <>;
print "\nPassword: ";
my $password = <>;
$ftp->login($username, $password);
$ftp->put($filename) or die "Can't upload $filename.\n";
print "Done!\n";
$ftp->quit;
Thanks in advance.
Now that you already have your answer <> -> <STDIN>, I think I see the problem. When #ARGV contains anything, <> is the 'magic open'. Perl interprets the next item in #ARGV as a filename, opens it and reads it line by line. Therefore, I think you can probably do something like:
use strict;
use Net::FTP;
use warnings;
use Scalar::Util 'looks_like_number';
if(#ARGV < 2)
{
print "Usage: ftp.pl host [port] file [credentials file]\n";
exit();
}
my $host = shift; # or equiv shift #ARGV;
my $port = (looks_like_number $ARGV[0]) ? shift : 0;
my $filename = shift;
my #ftp_args = (
$host,
Timeout => 30,
Debug => 1
);
if ($port)
}
print "Connecting to $host on port $port.\n";
push #ftp_args, (Port => $port);
}
else
{
print "Connecting to $host with the default port.\n";
}
my $ftp = Net::FTP->new(#ftp_args)
or die "Can't open $host on port $port.\n";
#now if #ARGV is empty reads STDIN, if not opens file named in current $ARGV[0]
print "Usename: ";
chomp(my $username = <>); #reads line 1 of file
print "\nPassword: ";
chomp(my $password = <>); #reads line 2 of file
$ftp->login($username, $password);
$ftp->put($filename) or die "Can't upload $filename.\n";
print "Done!\n";
$ftp->quit;
Then if you had some connection creditials in a file (say named cred) like
myname
mypass
then
$ ftp.pl host 8020 file cred
would open host:8020 for file using credentials in cred.
I'm not sure you want to do that, its just that THAT is how <> works.
I have been using Perl::Net::SSH to automate running some scripts on my remote boxes. However, some of these scripts take a really long time to complete (hour or two) and sometimes, I stop getting data from them, without actually losing the connection.
Here's the code I'm using:
sub run_regression_tests {
for(my $i = 0; $i < #servers; $i++){
my $inner = $users[$i];
foreach(#$inner){
my $user = $_;
my $server = $servers[$i];
my $outFile;
open($outFile, ">" . $outputDir . $user . "#" . $server . ".log.txt");
print $outFile "Opening connection to $user at $server on " . localtime() . "\n\n";
close($outFile);
my $pid = $pm->start and next;
print "Connecting to $user#" . "$server...\n";
my $hasWentToDownloadYet = 0;
my $ssh = Net::SSH::Perl->new($server, %sshParams);
$ssh->login($user, $password);
$ssh->register_handler("stdout", sub {
my($channel, $buffer) = #_;
my $outFile;
open($outFile, ">>", $outputDir . $user . "#" . $server . ".log.txt");
print $outFile $buffer->bytes;
close($outFile);
my #lines = split("\n", $buffer->bytes);
foreach(#lines){
if($_ =~ m/REGRESSION TEST IS COMPLETE/){
$ssh->_disconnect();
if(!$hasWentToDownloadYet){
$hasWentToDownloadYet = 1;
print "Caught exit signal.\n";
print("Regression tests for ${user}\#${server} finised.\n");
download_regression_results($user, $server);
$pm->finish;
}
}
}
});
$ssh->register_handler("stderr", sub {
my($channel, $buffer) = #_;
my $outFile;
open($outFile, ">>", $outputDir . $user . "#" . $server . ".log.txt");
print $outFile $buffer->bytes;
close($outFile);
});
if($debug){
$ssh->cmd('tail -fn 40 /GDS/gds/gdstest/t-gds-master/bin/comp.reg');
}else{
my ($stdout, $stderr, $exit) = $ssh->cmd('. ./.profile && cleanall && my.comp.reg');
if(!$exit){
print "SSH connection failed for ${user}\#${server} finised.\n";
}
}
#$ssh->cmd('. ./.profile');
if(!$hasWentToDownloadYet){
$hasWentToDownloadYet = 1;
print("Regression tests for ${user}\#${server} finised.\n");
download_regression_results($user, $server);
}
$pm->finish;
}
}
sleep(1);
print "\n\n\nAll tests started. Tests typically take 1 hour to complete.\n";
print "If they take significantly less time, there could be an error.\n";
print "\n\nNo output will be printed until all commands have executed and finished.\n";
print "If you wish to watch the progress tail -f one of the logs this script produces.\n Example:\n\t" . 'tail -f ./gds1#tdgds10.log.txt' . "\n";
$pm->wait_all_children;
print "\n\nAll Tests are Finished. \n";
}
And here is my %sshParams:
my %sshParams = (
protocol => '2',
port => '22',
options => [
"TCPKeepAlive yes",
"ConenctTimeout 10",
"BatchMode yes"
]
);
Sometimes randomly one of the long running commands just halts printing/firing the stdout or stderr events and never exits. The ssh connection doesn't die (as far as I'm aware) because the $ssh->cmd is still blocking.
Any idea how to correct this behaviour?
In your %sshParams hash, you may need to add "TCPKeepAlive yes" to your options:
$sshParams{'options'} = ["BatchMode yes", "TCPKeepAlive yes"];
Those options might or might not be right for you, but the TCPKeepAlive is something I would recommend setting for any long running SSH connection. If you have any kind of stateful firewall in your path it could drop the state if it hasn't passed traffic over the connection for a long period of time.
It fails probably due to the way you look into the output for the REGRESSION TEST IS COMPLETE mark. It may be split over two different SSH packets and so your callback will never found it.
Better, use a remote command that ends when it is done as this one-liner:
perl -pe 'BEGIN {$p = open STDIN, "my.comp.reg |" or die $!}; kill TERM => -$p if /REGRESSION TEST IS COMPLETE/}'
Otherwise, you are closing the remote connection but not stopping the remote process that will stay alive.
Besides that, you should try using Net::OpenSSH or Net::OpenSSH::Parallel instead of Net::SSH::Perl:
use Net::OpenSSH::Parallel;
my $pssh = Net::OpenSSH::Parallel->new;
for my $i (0..$#server) {
my $server = $server[$i];
for my $user (#{$users[$ix]}) {
$pssh->add_host("$user\#$server", password => $password);
}
}
if ($debug) {
$pssh->all(cmd => { stdout_file => "$outputDir%USER%\#%HOST%.log.txt",
stderr_to_stdout => 1 },
'fail -fn 40 /GDS/gds/gdstest/t-gds-master/bin/comp.reg');
}
else {
$pssh->all(cmd => { stdout_file => "$outputDir%USER%\#%HOST%.log.txt",
stderr_to_stdout => 1 },
'. ./.profile && cleanall && my.comp.reg');
}
$pssh->all(scp_get => $remote_regression_results_path, "regression_results/%USER%\#%HOST%/");
$pssh->run;
Error:
Syntax error: end of file unexpected
Below is the Code
I changed.
"#!/usr/local/bin/perl"
The actual program is
#!/local/perl5/bin/perl5.003
use Socket;
$sockaddr = 'S n a4 x8';
$host = $ARGV[0];
$them = $host;
$port = 79;
print "Finger $host: \n";
$hostname = ``;
`nslookup $host |grep Name: >> $test`;
print $test;
#($name, $aliases, $proto) = getprotobyname('tcp');
($name, $aliases, $port) = getservbyname($port, 'tcp') unless $port =~ /^\d+$/;
($name, $aliases, $type, $len, $thisaddr) = gethostbyname($hostname);
$n1 = $name;
($name, $aliases, $type, $len, $thataddr) = gethostbyname($them);
$this = pack($sockaddr, &AF_INET, 0, $thisaddr);
$that = pack($sockaddr, &AF_INET, $port, $thataddr);
socket(S, &PF_INET, &SOCK_STREAM, $proto) || die "socket: $!";
bind(S, $this) || die "bind: $!";
connect(S, $that);
select(S); $| = 1; select(stdout);
print S "\n\n";
while (<S>) {print $_;};
Here:
`nslookup $host |grep Name: >> $test`;
$test is undefined at that point, so you're asking the shell to execute nslookup whatever.com |grep Name: >>. Where is the shell supposed to redirect the output to?
If you set $test to be something, like a filename.. or even $test = "$host.txt"; it will get you further.
Nothing to do with your Perl version, although being able to use strict;use warnings does help, as it would've caught the above error.