Currently I have a perl script to trigger a cron job on multiple websites however the script is only working for http and not for https. In other words when the script tries to trigger a link on a domain with an SSL certificate, the cron never gets triggered. What is required to trigger the https cron URL?
#!/usr/bin/perl
#
# Enter domains, one per line.
#
# They do not have to be indented.
#
# Do not use an '#' on the beginning of your domains.
#
# Enter domains below next line: (without #)
my #domains = qw/#############################
#www.example.com
#www.example.com
#www.example.com
###############_END_OF_DOMAINS_###############/;
my $num_domains = 200; # number of domains
my $cycle_time = 900; # seconds per cycle
# So if $num_domain=200 and $cycle_time=900,
# there will be 4.5 seconds between sites.
# Nothing below here to service.....
#############################################################################
#############################################################################
#############################################################################
#############################################################################
use strict;
use warnings;
use IO::Socket;
use constant DEBUG => 0;
my $time_wait = $cycle_time / $num_domains;
my #stdin = -t STDIN
? ()
: grep !/^$/, map { s/^\s+//; s/\s+$//; s/\#.*?$//; $_ } <STDIN>
;
for my $site ( grep !/^\s*\#/, #domains, #stdin ) {
chomp $site;
print "contacting: $site\n" if DEBUG;
my $sock = IO::Socket::INET->new(
PeerHost => $site,
PeerPort => 'http(80)',
Proto => 'tcp',
Type => SOCK_STREAM,
Timeout => 10,
);
unless ( defined $sock ) {
warn "Couldn't connect to $site: $!\n" if DEBUG;
next;
}
#Example target:
# http://www.example.com/index.php?option=com_acymailing&ctrl=cron
print $sock
"GET /index.php?option=com_acymailing&ctrl=cron HTTP/1.0\n" .
"Host: $site\n\n"
;
# wait for response...
while (defined( $_ = scalar <$sock> )) {
# we dont care what it said...
print 'recv: ', $_ if DEBUG;
}
print "\ndone\n" if DEBUG;
$sock->close;
}
continue {
print "sleeping $time_wait seconds\n" if DEBUG;
select( undef, undef, undef, $time_wait );
}
__END__
It seems likely that the problem is because you're always configuring your socket object to use the HTTP port (PeerPort => 'http(80)'). The HTTPS port is 443, not 80.
But I have no idea why you would write code like this using low-level socket programming. You would be far better advised to look at something like LWP::Simple (you will also want to install LWP::Protocol::https for HTTPS support).
Related
I am using an docker overlay network
I am starting a server, myserver.pl, in a container
use strict;
use warnings;
use POSIX qw(:sys_wait_h :errno_h :fcntl_h strftime);
use Getopt::Long;
use File::Basename;
use IO::Socket;
use IO::Select;
STDOUT->autoflush(1);
use Fcntl;
use Socket;
use Carp;
use FileHandle;
use Cwd qw{abs_path cwd};
use Sys::Hostname;
our $HOST = hostname();
our $PUBQDIR = '/x/eng/site/build/altload';
our $PWD = cwd();
our $EMPTY = q{};
our $NL = "\n";
our $SPACE = q{ };
our $ANY_RE = qr{.*};
our $REDIS_OS_RE = qr{^br-redis$};
our $HOST_SPLIT_RE = qr{[.][^:]*};
our $bash_and_list_op = '&&';
my $Request_File = $EMPTY;
# options from command line
my $Opt_Class = 'L';
my $Redis = $EMPTY;
my %Published_Requests = (); # Track published queue requests for cleanup
chomp $HOST;
$HOST =~ s/[.].*//g;
chomp $PWD;
my $Pool = $EMPTY;
sub sock_initialize {
my $sock = q{};
my $port = q{};
# Get a port for our server.
$sock = IO::Socket::INET->new(
Listen => SOMAXCONN, # listen queue depth
LocalPort => 0,
Reuse => 1
);
die "Unable to bind a port: $!" if !$sock;
$port = $sock->sockport();
# Determine and log the dispatcher queue id
#my $ip = inet_ntoa( scalar gethostbyname( $HOST ));
my $ip = "";
my $uid = (getpwuid( $> ))[2];
my $queue = join(":", $ip, $port, $$, $uid);
# Include in the published queue name:
# - job class, which must be the same for all jobs we submit
# - Our Hostname and Port
# Can't lower hostname to IP yet, it might give 127.0.0.1
print sprintf("Connect me at $HOST on port $port ($$), SOMAXCONN=%d\n", SOMAXCONN);
return $sock;
} ## end sub sock_initialize
my $listen_sock = sock_initialize();
while (1) {
#my $xsock = Accept();
my $xsock;
while (1) {
$! = 0;
# Accept can block. Need to use nonblocking poll (Stevens)
$xsock = $listen_sock->accept; # ACCEPT
last if defined $xsock;
next if $! == EINTR;
die "accept error: $!";
if ( defined $xsock ) {
$xsock->blocking(0); # mark executor socket nonblocking
$xsock->sockopt( SO_KEEPALIVE() => 1 ) or die "sockopt: $!";
}
}
my $buff = "";
while (1) {
my $nbytes = sysread $xsock, $buff, 32768, length($buff); # SYSCALL
if ( !defined $nbytes ) { # read error
next if $! == EINTR;
last if $! == EWOULDBLOCK; # normal
return;
}
last if $nbytes == 0; # EOF
}
print "received $buff\n";
last;
by docker run -v /home/:/home/ --name myhost01 --network test-net perl perl /home/myserver.pl
it outputs:
Connect me at ebcf3c65c3e1 on port 39580 (1), SOMAXCONN=128
I can connect to this server from another container on different host (docker daemon) attached to the same overlay network via : i.e. myhost01:39580
my $host = "myhost01";
my $port = 39850;
my $s = IO::Socket::INET->new (PeerAddr => $host,
PeerPort => $port,
Type => SOCK_STREAM,
Proto => 'tcp',
Timeout => 1);
So, thats fine but i was aware of the container name beforehand so i knew to connect to it, I want the myserver.pl to advertise its hostname as myhost01, not ebcf3c65c3e1
Connect me at myhost01 on port 39580 (1), SOMAXCONN=128
Is there a way to set the hostname inside the container that it thinks the hostname is the same as container name and that perl's Sys::Hostname:hostname() or python's socket.gethostname() or whatever ways to get hostname would think that and not using container id such as ebcf3c65c3e1 as hostname?
[EDIT]
i see the option --hostname for docker run but do i really need to supply both --name and --hostname? Is there other ways to automatically inherit container name as its hostname?
Personal opinion: you should leave the hostname alone. If you need to spawn multiple, similar containers with scale: <n>, any attempt to set the hostname would not work.
But you can obtain the container name from inside the container via docker:socket, and use that for many of your other purposes, such as for the code to identify itself or other containers.
See my example here
I am having issues printing Zebra labels from a Perl CGI, where it works on one server but not another. Also, if I run the program from the command line it works on either server. The servers are IIS 7 (don't laugh it's what I'm stuck using).
Here is the code:
use strict;
use Socket;
use CGI qw(:cgi-lib);
use CGI::Carp qw ( fatalsToBrowser );
my %formdata = Vars;
print "Content-type: text/html\r\n\r\n";
# to test running from the command line, hardcode the paramters normally passed from the web interface
# comment these out when running CGI
$formdata{printer} = "zebraprinter.mycompany.com";
$formdata{serials} = "TR16170003|Gerry's Product TR|This is a generic product where all serial numbers start with the letters TR|T~";
# initialize server and port
my $port = 9100;
# create the socket, connect to the port
socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2]) or myExit("Can't create a socket $!\n");
connect( SOCKET, pack_sockaddr_in($port, inet_aton($formdata{printer}))) or myExit("Can't connect to port $port! \n");
foreach my $serial(split("~", $formdata{serials}))
{
my #ar = split(/\|/, $serial);
my $line;
if ($formdata{printer} =~ /label2/) # small labels
{
$line = "^XA^PRA,A,A^LH5,5^FO10,10^BCN,50,N,N,N,D^FD$ar[0]^FS";
$line .= "^FO300,10^AD,15,12^FDSerial Number:^FS";
$line .= "^FO300,30^AD,15,12^FD$ar[0]^FS^XZ";
}
else # large labels
{
$line = "^XA^PRA,A,A^LH20,20";
$line .= "^FO20,40^FWN^AT,60,10^FD Serial Number: $ar[0]^FS";
$line .= "^FO20,120^FWN^AT,60,10^FD $ar[1]^FS";
# need to hard break and limit long lines
if (length($ar[2]) > 60)
{
my $part = substr($ar[2],0,60);
$line .= "^FO20,200^FWN^AT,60,10^FD Description: $part^FS";
$part = substr($ar[2],61,74);
$line .= "^FO20,260^FWN^AT,60,10^FD$part^FS";
$line .= "^FO50,340^B3N,N,100,Y,N^FD$ar[0]^FS";
}
else
{
$line .= "^FO20,200^FWN^AT,60,10^FD Description: $ar[2]^FS";
$line .= "^FO50,280^B3N,N,100,Y,N^FD$ar[0]^FS";
}
$line .= "^XZ";
# example formatted label
#$line = qq~^XA^PRA,A,A^LH20,20^FO20,40^FWN^AT,60,10^FD Serial Number: $ar[0]^FS^FO20,120^FWN^AT,60,10^FD Product: $ar[1]^FS^FO20,200^FWN^AT,60,10^FD Description: $ar[2]^FS^FO50,280^B3N,N,100,Y,N^FD$ar[0]^FS^XZ~;
}
print SOCKET $line;
}
close SOCKET;
myExit("Labels Printed.");
sub myExit
{
my $msg = shift;
print "<script>alert('$msg')</script>";
exit;
}
I'm guessing it has something to do with opening sockets in a CGI but I don't have a whole lot of experience with that.
Thanks in advance
It turns out that our Zebra printer is sending a response after printing labels and waiting to verify it was delivered, which locked it up. The solution that is working so far is to get the response but also set a short timeout on the socket just in case. Also went up the food chain and used IO::Socket instead of the old Socket library:
use strict;
use IO::Socket;
use CGI qw(:cgi-lib);
use CGI::Carp qw ( fatalsToBrowser );
my %formdata = Vars;
print "Content-type: text/html\r\n\r\n";
# to test running from the command line, hardcode the paramters normally passed from the web interface
# comment these out when running CGI
$formdata{printer} = "zebralabel1.mycompany.com";
$formdata{serials} = "TR16170003|Gerry's Product TR|This is a generic product where all serial numbers start with the letters TR|T~";
# create the socket, connect to the port
my $remote = IO::Socket::INET->new(
Proto => 'tcp',
PeerAddr=> "$formdata{printer}",
PeerPort=> "9100",
ReuseAddr=> 0,
Timeout => 2,
) or myExit("Cannot connect to printer: $!");
$remote->autoflush(1); # Send immediately
my ($serial, $product, $desc) = split(/\|/, $formdata{serials});
# example formatted label
my $line = qq~^XA^PRA,A,A^LH20,20^FO20,40^FWN^AT,60,10^FD Serial Number: $serial^FS^FO20,120^FWN^AT,60,10^FD Product: $product^FS^FO20,200^FWN^AT,60,10^FD Description: $desc^FS^FO50,280^B3N,N,100,Y,N^FD$serial^FS^XZ~;
print $remote $line;
my $dontCare = <remote>;
close $remote;
myExit("Labels Printed.");
sub myExit
{
my $msg = shift;
print "<script>alert('$msg')</script>";
exit;
}
First of all I would thank you guys not offering a work around as a solution (although it would be cool to know other ways to do it). I was setting up tg-master project (telegram for cli) to be used by check_mk alert plugin. I found out that telegram runs on a stdin/stdout proccess so I tought it would be cool to "glue" it, so i wrote with a lot of building blocks from blogs and cpan the next 2 pieces of code. They already work (i need to handle broken pipes sometimes) but I was wondering if sharing this could come from some experts new ideas.
As you could see my code relies on a eval with a die reading from spawned process, and I know is not the best way to do it. Any suggestions? :D
Thank you guys
Server
use strict;
use IO::Socket::INET;
use IPC::Open2;
use POSIX;
our $pid;
use sigtrap qw/handler signal_handler normal-signals/;
sub signal_handler {
print "what a signal $!\nlets kill $pid\n";
kill 'SIGKILL', $pid;
#die "Caught a signal $!";
}
# 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";
my ( $read_proc, $write_proc );
my ( $uid, $gid ) = ( getpwnam "nagios" )[ 2, 3 ];
POSIX::setgid($gid); # GID must be set before UID!
POSIX::setuid($uid);
$pid = open2( $read_proc, $write_proc, '/usr/bin/telegram' );
#flush first messages;
eval {
local $SIG{ALRM} = sub { die "Timeout" }; # alarm handler
alarm(1);
while (<$read_proc>) { }
};
while (1) {
my $client_socket = $socket->accept();
my $client_address = $client_socket->peerhost();
my $client_port = $client_socket->peerport();
print "connection from $client_address:$client_port\n";
# read until \n
my $data = "";
$data = $client_socket->getline();
# write to spawned process stdin the line we got on $data
print $write_proc $data;
$data = "";
eval {
local $SIG{ALRM} = sub { die "Timeout" }; # alarm handler
alarm(1);
while (<$read_proc>) {
$client_socket->send($_);
}
};
# notify client that response has been sent
shutdown( $client_socket, 1 );
}
$socket->close();
Client
echo "contact_list" | nc localhost 7777
or
echo "msg user#12345 NAGIOS ALERT ... etc" | nc localhost 7777
or
some other perl script =)
If you are going to implement a script that performs both reads and writes from/to different handles, consider using select (the one defined as select RBITS,WBITS,EBITS,TIMEOUT in the documentation). In this case you will totally avoid using alarm with a signal handler in eval to handle a timeout, and will only have one loop with all of the work happening inside it.
Here is an example of a program that reads from both a process opened with open2 and a network socket, not using alarm at all:
use strict;
use warnings;
use IO::Socket;
use IPC::Open2;
use constant MAXLENGTH => 1024;
my $socket = IO::Socket::INET->new(
Listen => SOMAXCONN,
LocalHost => '0.0.0.0',
LocalPort => 7777,
Reuse => 1,
);
# accepting just one connection
print "waiting for connection...\n";
my $remote = $socket->accept();
print "remote client connected\n";
# simple example of the program writing something
my $pid = open2(my $localread, my $localwrite, "sh -c 'while : ; do echo boom; sleep 1 ; done'");
for ( ; ; ) {
# cleanup vectors for select
my $rin = '';
my $win = '';
my $ein = '';
# will wait for a possibility to read from these two descriptors
vec($rin, fileno($localread), 1) = 1;
vec($rin, fileno($remote), 1) = 1;
# now wait
select($rin, $win, $ein, undef);
# check which one is ready. read with sysread, not <>, as select doc warns
if (vec($rin, fileno($localread), 1)) {
print "read from local process: ";
sysread($localread, my $data, MAXLENGTH);
print $data;
}
if (vec($rin, fileno($remote), 1)) {
print "read from remote client: ";
sysread($remote, my $data, MAXLENGTH);
print $data;
}
}
In the real production code you will need to carefully check for errors returned by various function (socket creation, open2, accept, and select).
I am trying to read from an instrument connected over network using TCP protocol from Perl.
The code I have used is below:
$socket = new IO::Socket::INET (
PeerHost => '210.232.14.204',
PeerPort => '23',
Proto => 'tcp',
) or die "ERROR in Socket Creation";
while(!($data=~m/"ABC"/))
{
$temp = <$socket>;
$data = $data + $temp;
print $temp;
}
What happens is the first character of every line that is read over the TCP is not printed. Instead it is replace with a space. Why does this happen?
Example:
Expected output
Copyright (c) ACME Corporation
2009 - 2010
Actual Output
opyright (c) ACME Corporation
009 - 2010
Thanks...
The telnet protocol has a little bit of negotiation at its startup, something I sometimes jokingly refer to as a “secret handshake”. You should use a more straight-through service/port to get up to speed with sockets.
Also, you really need two different threads of control for this sort of thing; otherwise it’s too hard. Here’s a simple telnetish program from 1998:
use strict;
use IO::Socket;
my ($host, $port, $kidpid, $handle, $line);
unless (#ARGV == 2) { die "usage: $0 host port" }
($host, $port) = #ARGV;
# create a tcp connection to the specified host and port
$handle = IO::Socket::INET->new(Proto => "tcp",
PeerAddr => $host,
PeerPort => $port)
or die "can't connect to port $port on $host: $!";
$handle->autoflush(1); # so output gets there right away
print STDERR "[Connected to $host:$port]\n";
# split the program into two processes, identical twins
die "can't fork: $!" unless defined($kidpid = fork());
if ($kidpid) {
# parent copies the socket to standard output
while (defined ($line = <$handle>)) {
print STDOUT $line;
}
kill("TERM" => $kidpid); # send SIGTERM to child
}
else {
# child copies standard input to the socket
while (defined ($line = <STDIN>)) {
print $handle $line;
}
}
exit;
And here’s a more complete implementation, a program that sits on your firewall and waits for internal connections to some outside port:
#!/usr/bin/perl -w
# fwdport -- act as proxy forwarder for dedicated services
use strict; # require declarations
use Getopt::Long; # for option processing
use Net::hostent; # by-name interface for host info
use IO::Socket; # for creating server and client sockets
use POSIX ":sys_wait_h"; # for reaping our dead children
my (
%Children, # hash of outstanding child processes
$REMOTE, # whom we connect to on the outside
$LOCAL, # where we listen to on the inside
$SERVICE, # our service name or port number
$proxy_server, # the socket we accept() from
$ME, # basename of this program
);
($ME = $0) =~ s,.*/,,; # retain just basename of script name
check_args(); # processing switches
start_proxy(); # launch our own server
service_clients(); # wait for incoming
die "NOT REACHED"; # you can't get here from there
# process command line switches using the extended
# version of the getopts library.
sub check_args {
GetOptions(
"remote=s" => \$REMOTE,
"local=s" => \$LOCAL,
"service=s" => \$SERVICE,
) or die <<EOUSAGE;
usage: $0 [ --remote host ] [ --local interface ] [ --service service ]
EOUSAGE
die "Need remote" unless $REMOTE;
die "Need local or service" unless $LOCAL || $SERVICE;
}
# begin our server
sub start_proxy {
my #proxy_server_config = (
Proto => 'tcp',
Reuse => 1,
Listen => SOMAXCONN,
);
push #proxy_server_config, LocalPort => $SERVICE if $SERVICE;
push #proxy_server_config, LocalAddr => $LOCAL if $LOCAL;
$proxy_server = IO::Socket::INET->new(#proxy_server_config)
or die "can't create proxy server: $#";
print "[Proxy server on ", ($LOCAL || $SERVICE), " initialized.]\n";
}
sub service_clients {
my (
$local_client, # someone internal wanting out
$lc_info, # local client's name/port information
$remote_server, # the socket for escaping out
#rs_config, # temp array for remote socket options
$rs_info, # remote server's name/port information
$kidpid, # spawned child for each connection
);
$SIG{CHLD} = \&REAPER; # harvest the moribund
accepting();
# an accepted connection here means someone inside wants out
while ($local_client = $proxy_server->accept()) {
$lc_info = peerinfo($local_client);
set_state("servicing local $lc_info");
printf "[Connect from $lc_info]\n";
#rs_config = (
Proto => 'tcp',
PeerAddr => $REMOTE,
);
push(#rs_config, PeerPort => $SERVICE) if $SERVICE;
print "[Connecting to $REMOTE...";
set_state("connecting to $REMOTE"); # see below
$remote_server = IO::Socket::INET->new(#rs_config)
or die "remote server: $#";
print "done]\n";
$rs_info = peerinfo($remote_server);
set_state("connected to $rs_info");
$kidpid = fork();
die "Cannot fork" unless defined $kidpid;
if ($kidpid) {
$Children{$kidpid} = time(); # remember his start time
close $remote_server; # no use to master
close $local_client; # likewise
next; # go get another client
}
# at this point, we are the forked child process dedicated
# to the incoming client. but we want a twin to make i/o
# easier.
close $proxy_server; # no use to slave
$kidpid = fork();
die "Cannot fork" unless defined $kidpid;
# now each twin sits around and ferries lines of data.
# see how simple the algorithm is when you can have
# multiple threads of control?
# this is the fork's parent, the master's child
if ($kidpid) {
set_state("$rs_info --> $lc_info");
select($local_client); $| = 1;
print while <$remote_server>;
kill('TERM', $kidpid); # kill my twin cause we're done
}
# this is the fork's child, the master's grandchild
else {
set_state("$rs_info <-- $lc_info");
select($remote_server); $| = 1;
print while <$local_client>;
kill('TERM', getppid()); # kill my twin cause we're done
}
exit; # whoever's still alive bites it
} continue {
accepting();
}
}
# helper function to produce a nice string in the form HOST:PORT
sub peerinfo {
my $sock = shift;
my $hostinfo = gethostbyaddr($sock->peeraddr);
return sprintf("%s:%s",
$hostinfo->name || $sock->peerhost,
$sock->peerport);
}
# reset our $0, which on some systems make "ps" report
# something interesting: the string we set $0 to!
sub set_state { $0 = "$ME [#_]" }
# helper function to call set_state
sub accepting {
set_state("accepting proxy for " . ($REMOTE || $SERVICE));
}
# somebody just died. keep harvesting the dead until
# we run out of them. check how long they ran.
sub REAPER {
my $child;
my $start;
while (($child = waitpid(-1,WNOHANG)) > 0) {
if ($start = $Children{$child}) {
my $runtime = time() - $start;
printf "Child $child ran %dm%ss\n",
$runtime / 60, $runtime % 60;
delete $Children{$child};
} else {
print "Bizarre kid $child exited $?\n";
}
}
# If I had to choose between System V and 4.2, I'd resign. --Peter Honeyman
$SIG{CHLD} = \&REAPER;
};
As I said, that’s from 1998. These days I’d use warnings and possibly use autodie, but you still should be able to learn a good bit from it.
here is the request URL http://localhost:9009/?comd&user=kkc&mail=kkc#kkc.com
what are the modification need to do in the server perl script.
server-Perl-script
use IO::Socket;
use Net::hostent; # for OO version of gethostbyaddr
$PORT = 9009; # pick something not in use
$server = IO::Socket::INET->new( Proto => 'tcp',
LocalPort => $PORT,
Listen => SOMAXCONN,
Reuse => 1);
die "can't setup server" unless $server;
print "[Server $0 accepting clients]\n";
while ($client = $server->accept())
{
$client->autoflush(1);
print $client "Welcome to $0; type help for command list.\n";
$hostinfo = gethostbyaddr($client->peeraddr);
printf "[Connect from %s]\n", $hostinfo ? $hostinfo->name : $client->peerhost;
print $client "Command? ";
while ( <$client>) {
next unless /\S/; # blank line
if (/comd/i ) { print $client `dir`; }
} continue {
print $client "Command? ";
}
close $client;
print "client closed";
}
I assume that your script is not for production, but for homework or testing sometime. There are multiple very efficient web server solutions in/with Perl like Apache with CGIs or mod_perl, HTTP::Server::Simple and PSGI/Plack.
You'll also typically use a framework like Dancer, Mojo or Catalyst which does most of the boring standard stuff for you:
use Dancer;
get '/' => sub {
return 'Hi there, you just visited host '.request->host.
' at port '.request->port.' asking for '.request->uri;
};
Back to your question: Your script is a interactive server while HTTP has a strict request and response structure:
Client connects to server
Client sends a request
Server sends a response
You need to remove the interactive part and just wait for the client to start the conversation:
use IO::Socket;
use Net::hostent; # for OO version of gethostbyaddr
$PORT = 9009; # pick something not in use
$server = IO::Socket::INET->new( Proto => 'tcp',
LocalPort => $PORT,
Listen => SOMAXCONN,
Reuse => 1);
die "can't setup server" unless $server;
print "[Server $0 accepting clients]\n";
while ($client = $server->accept())
{
$hostinfo = gethostbyaddr($client->peeraddr);
# Read request up to a empty line
my $request;
while ( <$client>) {
last unless /\S/;
$request .= $_;
}
# Do something with the request
# Send response
print $client "Status: 200 OK\r\nContent-type: text/plain\r\n\r\n".$request;
close $client;
print "client closed";
}
The server reads the full request from the client and returns a minimized HTTP header plus the original request.