Reuse a Perl IO::socket::ssl client - perl

I'm trying to lighten a part of my code where I was user an LWP::UA
To acces a Rest API using a SSL Layer.
I'm trying to achieve the same result with the bare IO::Socket::SSL module.
It work great on the first call with something like this :
my $SSLSocket = IO::Socket::SSL->new(
PeerHost => $server,
PeerPort => "https");
print $SSLSocket "GET /restapi/json/v1/resources?AUTHTOKEN=$token HTTP/1.0\r\n\r\n";
while(<$SSLSocket>){
$response = $_;
}
$resources = $json->decode($response);
But when I'm trying to reuse this socket I'm getting no awnser
my $SSLSocket = IO::Socket::SSL->new(
PeerHost => $server,
PeerPort => "https");
print $SSLSocket "GET /restapi/json/v1/resources?AUTHTOKEN=$token HTTP/1.0\r\n\r\n";
while(<$SSLSocket>){
$response = $_;
}
$resources = $json->decode($response);
print $SSLSocket ,"GET /restapi/json/v1/resources/3138/accounts?AUTHTOKEN=$token HTTP/1.0\r\n\r\n";
while(<$SSLSocket >){
$response = $_;
}
close($SSLSocket );
my $accounts = $json->decode($response);
The second read is empty.
Since I cannot use the recv or send method I'm a bit lost.
Is there any way to not open a new SSL Socket on each request ?
Thank you for you answers.

Related

connect to localhost failed (Connection refused) no (more) retries

I want to send an email using perl ,but when i execute the command as follows:
#./sendmail.sh "par1" "par2" "par3"
i got the error msg "connect to localhost failed (Connection refused) no (more) retries"
sendmail.sh:
/usr/bin/perl /code/sendmail.pl "$1" "$2" "$3";
sendmail.pl:
#!/usr/bin/perl -w
use Mail::Sendmail;
my $event1 = shift(#ARGV);
my $event2 = shift(#ARGV);
my $time = shift(#ARGV);
#my $info = shift(#ARGV);
my $datetime = `/bin/date "+20%y-%m-%d %H:%M:%S"`;
chomp $datetime;
$msg = "This is Monitor System speak:\n
The system discovers the events at $datetime.
Something may be abnormal, please check it. The detail is below:\n";
$msg = $msg."$event1 and $event2 at $time\n";
$msg = $msg."\n";
$msg = $msg."Any problem, check it from http://map_test.php\n\n\n";
$mail_subject = "Abnormal";
sendmail(
From => 'localhost',
To => 'test#mail.com',
Subject => $mail_subject,
Message => $msg,
);
Any help appreciated.
smtp stands for simple mail transfer protocol.
When you need to send an email your mail client needs to talk to an smtp server which will accept the message. Normally your internet service provider will provide an smtp host. If you look at your mail client it will need to have an smtp server configured to be able to send mail.
Ok so when you install the Mail::Sendmail module, it doesn't know what your smtp server will be. It is up to you to tell it. It provides a default of localhost which would often be true if your server is running a sendmail daemon.
The configuration of Mail::Sendmail is stored in a variable called
%Mail::Sendmail::mailcfg
You can change the value of the sendmail server using this snippet of code:
unshift #{$Mail::Sendmail::mailcfg{'smtp'}} , 'my.smtp.server';
You need to add this line of code to your script to set the smtp server.
It adds this server to an array which also includes localhost.
So if neither of the hosts work it will still print an error message about localhost which is slightly confusing.
If you use Data::Dumper to print the contents of the mailcfg variable it will look something like this:
#!/usr/bin/perl
use Mail::Sendmail;
use Data::Dumper;
unshift #{$Mail::Sendmail::mailcfg{'smtp'}} , 'my.smtp.server';
print Dumper(\%Mail::Sendmail::mailcfg);
Should return:
$VAR1 = {
'retries' => 1,
'smtp' => [
'my.smtp.server',
'localhost'
],
'delay' => 1,
'port' => 25,
'from' => '',
'debug' => 0,
'tz' => '',
'mime' => 1
};

How to fail over

If I use wget to retrieve something from the geonames.org server, it reports two IP addresses, and the first one fails but it gets it from the second:
Resolving ws.geonames.org (ws.geonames.org)... 5.9.41.208, 176.9.107.169
Connecting to ws.geonames.org (ws.geonames.org)|5.9.41.208|:80... failed: Connection refused.
Connecting to ws.geonames.org (ws.geonames.org)|176.9.107.169|:80... connected.
HTTP request sent, awaiting response... 200 OK
But unfortunately I have to access it through perl using LWP::UserAgent and HTTP::Request. How can I make them try the second IP if the first fails?
my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new(
GET =>
"http://ws.geonames.org/countrySubdivision?lat=$lat&lng=$long&radius=$radius&username=xyzzy");
my $res = $ua->request($req);
You can do it yourself: get all the IP addresses with the help of Net::DNS::Resolver, and then try all IP addresses until you get a successful response. Note that you have to supply the "Host" header yourself if working with an IP address, in case the server is doing name-based virtual hosts.
Something like the following lines could work. Maybe there's even a CPAN module for this, I did not check:
use Net::DNS;
use LWP::UserAgent;
my #addrs;
{
my $res = Net::DNS::Resolver->new;
my $query = $res->search("ws.geonames.org");
if ($query) {
for my $rr ($query->answer) {
if ($rr->type eq "A") {
push #addrs, $rr->address;
}
}
} else {
die "DNS query failed: ", $res->errorstring, "\n";
}
}
my $ua = LWP::UserAgent->new;
my $res;
for my $addr (#addrs) {
$res = $ua->get("http://$addr/countrySubdivision?lat=$lat&lng=$long&radius=$radius&username=xyzzy", Host => 'ws.geonames.org');
last if $res->is_success;
}
The solution from Slaven is OK except when the IP addresses are not directly accessible.
In that case, the following works for me:
local #LWP::Protocol::http::EXTRA_SOCK_OPTS = (
PeerAddr => 'my_hostname',
MultiHomed => 1,
);
my $response = $ua->post('https://my_hostname/...', ...);

Perl: How can i test for a URL ( https ) accepting GET requests using "login" parameter

I have a CGI server side script that accepts GET and POST, with login parameters.
I want to test it to make sure it is not vulnerable. So the plan is to use Perl LWP, and send login parameters in GET and POST, and compare the results. the interface has been changed, so that only in POST we can send user-name and password in session cookies ( not sure if that is a great idea ) , so how do i test it ? Here is what i have so far:
#!/usr/bin/perl
use LWP;
print "This is libwww-perl-$LWP::VERSION\n";
# Create a user agent object
use LWP::UserAgent;
my $ua = LWP::UserAgent->new;
$ua->agent("MyApp/0.1 ");
# Create a request
#my $req = HTTP::Request->new(POST => 'http://search.cpan.org/search');
#my $req = HTTP::Request->new(GET => 'https://qa.co.net:443/cgi-bin/n-cu.cgi');
my $req = HTTP::Request->new(GET => 'https://qa.co.net:443/cgi-bin/n-cu.cgi?mode=frameset&JScript=1&remote_user&login=foo&password=foo HTTP/1.1');
$req->content_type('application/x-www-form-urlencoded');
$req->content('query=libwww-perl&mode=dist');
# Pass request to the user agent and get a response back
my $res = $ua->request($req);
# Check the outcome of the response
if ($res->is_success) {
print $res->content;
#print $res->code;
#print $res->message;
}
else {
print $res->status_line, "\n";
}
This is not going to do it, since it does not have the session cookie stuff. But might be a good start though. Is this the right way to test the GET and POST ?
Here is what was implemented in the cgi:
#cr_login for POST && login for GET -- leave GET param as it used to be.
if ($m eq 'GET' && defined($req->param('login'))) {
$msg = 'parameter "login" is invalid for this request type.';
+ my $seclog = $event_logging_directory . '/invalid_request.log';
+ open(S, ">>$seclog") or die $!;
+ my $logmsg = sprintf("%4d-%02d-%02d %02d:%02d:%02d",Today_and_Now())
+ . "|mode:" . $req->param('mode')
+ . "|login:" . $req->param('login')
+ . "|remote_addr:" . $ENV{REMOTE_ADDR}
+ . "|$msg\n";
+ print S $logmsg;
and :
POST request to n-cu.cgi should use parameter "cr_login". If the parameter "login" is passed in a post request, it should throw error and return to login screen.
GET request to n-cu.cgi should use the parameter "login". If the parameter "cr_login" is passed in a post request, it should throw error and return to login screen.
so here is how we do it:
Keep the session cookie and context alive :
my $browser = LWP::UserAgent->new(keep_alive => 10);
$browser->cookie_jar( {} );
$browser->agent('Mozilla/8.0');
#$browser->ssl_opts({ verify_hostname => 0 });
$browser->show_progress(1);
and later: print the response
print "Cookies:\n", Dumper($browser->cookie_jar()), "\n\n";
my $content = $response->as_string;
print "$content\n";
Sending password in a cookie? Nope.
Disallow GET for /login.
POST username and password to /login, over SSL.
In CGI, the GET/POST is indicated via the REQUEST_METHOD environment variable.
You cannot stop determined people from issuing a GET request to your server, but you can refuse to process it like so (untested code - you have to fill in details):
if ($ENV{REQUEST_METHOD} ne 'POST') {
# issue a redirect to a suitable error page, then return.
}
my $q = CGI->new();
my $user = $q->params('username');
my $password = $q->params('password');
my $encrypted_password = my_password_encryptor($password);
unless ( can_log_in($user, $encrypted_password) ) {
# issue an error message - redirect&return or fall-through...
}
else {
$session->set_user_logged_in();
}
Most people do not roll their own authentication or session handling. They mostly use one from CPAN, or one included with the larger app framework. If you're doing CGI, you can use CGI::Session.
You might give CGI::Application and/or its offspring a look. Those authors have already solved a bunch of the problems that you're encountering.

Connecting keeps closing?

so i'm having a problem trying to automatically login to a internal website. I'm able to send a post request but in the response I always get the Header Connection: close. I've tried to pass is through the post request but it still seems to respond with Connection: close. I want to be able to navigate through the website so I need the Connection: keep-alive so that i can send more request. Could anyone tell me what I'm doing wrong? here's the code:
#usr/bin/perl
#NetTelnet.pl
use strict; use warnings;
#Sign into cfxint Unix something...
use Net::Telnet;
# Create a new instance of Net::Telnet,
my $telnetCon = new Net::Telnet (Timeout => 10,
Prompt => '/bash\$ $/') or die "Could not make connection.";
my $hostname = 'cfxint';
# Connect to the host of the users choice
$telnetCon->open(Host => $hostname,
Port => 23) or die "Could not connect to $hostname.";
use WWW::Mechanize;
my $mech = WWW::Mechanize->new(cookie_jar => {});
&login_alfresco;
sub login_cxfint {
#get username and password from user
my $CXusername = '';
my $CXpassword = '';
# Recreate the login
# Wait for the login: message and then enter the username
$telnetCon->waitfor(match => '/login:/i');
# this method adds a \n to the end of the username, it mimics hitting the enter key after entering your username
$telnetCon->print($CXusername);
# does the same as the previous command but for the password
$telnetCon->print($CXpassword);
#Wait for the login successful message
$telnetCon->waitfor();
}
sub login_alfresco{
my $ALusername = '';
my $ALpassword = '';
$mech->get('http://documents.ifds.group:8080/alfresco/faces/jsp/login.jsp');
my $res = $mech->res;
my $idfaces = '';
if($res->is_success){
my $ff = $res->content;
if($ff =~ /id="javax.faces.ViewState" value="(.*?)"/){
$idfaces = $1;
}
else {
print "javax.faces /Regex error?\n";
die;
}
}
print $idfaces, "\n";
#Send the get request for Alfresco
$mech->post('http://documents.ifds.group:8080/alfresco/faces/jsp/login.jsp',[
'loginForm:rediretURL' =>,
'loginForm:user-name' => $ALusername,
'loginForm:user-password' => $ALpassword,
'loginForm:submit' => 'Login',
'loginForm_SUBMIT' => '1',
'loginForm:_idcl' => ,
'loginForm:_link_hidden_' => ,
'javax.faces.ViewState' => $idfaces], **'Connection' =>'keep-alive'**);
$res = $mech->res;
open ALF, ">Alfresco.html";
print ALF $mech->response->as_string;
if($res->is_success){
my $ff = $res->content;
if($ff =~ /id="javax.faces.ViewState" value="(.*?)"/){
$idfaces = $1;
}
else {
print "javax.faces /Regex error?\n";
die;
}
}
print $idfaces, "\n";
#Logout
$mech->post('http://documents.ifds.group:8080/alfresco/faces/jsp/extension/browse/browse.jsp', [
'browse:serach:_option' => '0',
'browse:search' => ,
'browse:spaces-pages' => '20',
'browse:content-pages' => '50',
'browse_SUBMIT' => '1',
'id' => ,
'browse:modelist' => '',
'ref'=>'',
'browse:spacesList:sort' => ,
'browse:_idJsp7' => ,
'browse:sidebar-body:navigator' => ,
'browse:contentRichList:sort' => ,
'browse:act' => 'browse:logout',
'outcome' => 'logout',
'browse:panel' => ,
'javax.faces.ViewState' => $idfaces,])
}
You can enable keep-alive by using a connection cache:
use LWP::ConnCache;
...
$mech->conn_cache(LWP::ConnCache->new);
All that header means is that the connection will be closed upon completion of the request, instead of being kept open for possible further requests. This is perfectly normal and should not interfere with sending the request.
EDIT: If you're sending a Connection:Keep-Alive and the server is still responding with Connection:Close, then the server configuration needs to be changed. The default for HTTP/1.1 is persistent connections, so the server must explicitly be configured to send Connection:Close. See Section 8 of RFC2616.

Perl SSH connection to execute telnet

I tried the following to access a router via a central admin server as "ssh hop" server
#!/usr/bin/perl -X
use strict;
use Net::OpenSSH;
use Net::Telnet;
my $lhost = "linuxserver";
my $luser = "linuxuser";
my $lpass = "linuxpassword";
my $chost = "routername";
my $cpass = "Routerpassword";
my $prompt = '/(?:Password: |[>])/m';
my #commands = ("show users\r");
my $ssh = Net::OpenSSH->new($lhost,
'user' => $luser,
'password' => $lpass,
'master_opts' => [ '-t' ],
#'async' => 1 # if enabled then password cannot be set here
);
my ($pty, $err, $pid) = $ssh->open2pty("telnet $chost");
my $t = new Net::Telnet(
-telnetmode => 0,
-fhopen => $pty,
-prompt => $prompt,
-cmd_remove_mode => 1,
-output_record_separator => "\r",
#-dump_log => "debug.log",
);
my $end = 0;
while (!$end) {
my ($pre, $post) = $t->waitfor($prompt);
if ($post =~ /Password: /m) {
# send password
$t->print("$cpass");
}
elsif ($post =~ /[>#]/ && #commands) {
my $cmd = shift(#commands);
if ($cmd !~ /[\r\n]/) {
$t->print($cmd);
}
else {
print $t->cmd($cmd);
}
}
else {
$end = 1;
$t->cmd("exit");
}
}
#close $pty;
$t->close();
Unfortunately I always get the following error:
read error: Input/output error at test.pl line 71
Can somebody help me please or is there a better solution only to test if a telnet connection via the "hop" server is possible or not?
The connection looks like:
workstation --ssh-> server --telnet-> router
Thanks in advance.
I think best option is to make an SSH-tunnel to your admin server and use it for telnetting to the router.
Getting Net::Telnet to work over Net::OpenSSH sometimes is not as easy as it should be and it requires some experimentation to get to the right combination of flags and calls that make it work.
For instance, instead of telneting to the target host, use netcat to open a raw connection (or Net::OpenSSH support for TCP forwarding if tunnels are allowed on the proxy).
Expect + Net::OpenSSH may be a better option.