—————————#!/usr/bin/perl
package
LaBrea::NetIO;
use
strict;
#use diagnostics;
$VERSION
=
do
{
my
@r
= (
q$Revision: 0.01 $
=~ /\d+/g);
sprintf
"%d."
.
"%02d"
x
$#r
,
@r
};
use
Socket;
require
Exporter;
@ISA
=
qw(Exporter)
;
@EXPORT_OK
= (
@Socket::EXPORT
,
@Socket::EXPORT_OK
,
qw(
TARPIT_PORT
open_listen_sock
open_tcp
alarm_wrap
daemon_handler
read_daemon
fetch
reap_kids
set_so_linger
)
);
# autoload declarations
sub
open_listen_sock;
sub
open_tcp;
sub
alarm_wrap;
sub
daemon_handler;
sub
fetch;
sub
read_daemon;
sub
reap_kids;
sub
set_so_linger;
sub
TARPIT_PORT { 8686; };
#
sub
_fetch;
sub
_want_daemon;
sub
DESTROY {};
1;
__END__
=head1 NAME
LaBrea::Tarpit::NetIO
=head1 SYNOPSIS
use LaBrea::Tarpit::NetIO qw (
TARPIT_PORT
open_listen_sock
open_tcp
alarm_wrap
daemon_handler
read_daemon
fetch
reap_kids
set_so_linger
[plus any Socket.pm variable]
);
$error=open_listen_sock(HANDLE,address,port);
$error=open_tcp(*S,$host,$port);
*rv = alarm_wrap($timeout,$subref,@args);
$subref=daemon_handler(*HANDLE,$target);
read_daemon($subref,\@response);
$err=fetch($target,\@response,$command);
$alive = reap_kids(\%kids);
$rv = set_so_linger(*HANDLE,$seconds);
=head1 DESCRIPTION
B<NetIO> contains TCP client and server modules used by Tarpit modules.
B<NetIO> has available for EXPORT, any variable from the standard Socket.pm
module.
=over 4
=item $error=open_listen_sock(HANDLE,address,port);
Opens a server listening socket on HANDLE
input: HANDLE,
address, name or ip
defaults to all
interfaces if false
port defaults to 8686
returns: false on success
or error message
=cut
sub open_listen_sock {
my ($S,$host,$port) = @_;
# default connection is to ANY interface
my $iaddr = INADDR_ANY;
return 'interface address not found'
if $host && ! ($iaddr = inet_aton($host));
my $proto = getprotobyname('tcp');
$port = TARPIT_PORT unless $port && $port !~ /[\D]/;
return 'failed to create socket'
unless socket($S,PF_INET,SOCK_STREAM,$proto);
unless (setsockopt($S,SOL_SOCKET,SO_REUSEADDR,1)) {
close $S;
return 'failed to set socket options';
}
unless (bind($S,sockaddr_in($port,$iaddr))) {
close $S;
return 'failed to bind socket';
}
unless (listen($S,SOMAXCONN)) {
close $S;
return "failed to set listen queue";
}
$_ = select $S;
$| = 1;
select $_;
return undef;
}
=item $error=open_tcp(*S,$host,$port);
Open a tcp connection on port to host.
input: *S,hostname, port
returns: false on success
error message on failure
=cut
sub open_tcp {
my ($S,$host,$port) = @_;
my $iaddr;
return 'port is not numeric'
if !$port || $port =~ /\D/;
return 'hostname not found'
unless ($iaddr = inet_aton($host));
my $proto = getprotobyname('tcp');
return 'unable to open socket'
unless socket($S, PF_INET, SOCK_STREAM, $proto );
my $paddr = sockaddr_in($port, $iaddr);
unless (connect($S, $paddr)) {
close $S;
return 'could not connect to host';
}
$host = select $S; # temp save old selection
$| = 1;
select $host; # restore selection
return undef;
}
=item $rv = alarm_wrap($timeout,$subref,@args);
Provides an alarm wrapper for subroutines that may time out or B<die>.
input: timeout,
$subref,
arguments for $subref
returns: $subref return value(s)
on error
undef or () on error
$@ is set with error value
which will contain the string
'alarm_wrap timeout' if
the fault was timeout only
timeout is ignored if false
=cut
sub alarm_wrap {
my ($timeout,$subref,@args) = @_;
local $SIG{ALRM} = sub { die 'alarm_wrap timeout' };
my @rv;
alarm $timeout if $timeout;
eval { @rv = &$subref(@args) };
alarm 0;
@rv = () if $@;
return (wantarray) ? @rv : "@rv";
}
=item $subref=daemon_handler(*HANDLE,$target);
Opens a handle *HANDLE pointing to the Tarpit daemon, pipe or file
and returns a CODEREF to a subroutine that will read full
lines of data from the HANDLE. Do not try to read the handle directly.
input: *HANDLE
file name/path
or
hash ->{d_host} [optional]
->{d_port} [optional]
If B<target> is a HASH and d_host and/or d_port are not specified,
they default to localhost:8686
returns: subref or undef on open fail
usage: $present = daemon_handler(*H,$t);
while ( $data = &$present ) {
do something with $data;
}
close H;
=cut
sub daemon_handler {
my ($S,$target) = @_;
if ( &_want_daemon(\$target) ) {
my $d_port = $target->{d_port} || TARPIT_PORT;
my $d_host = $target->{d_host} || 'localhost';
return undef if open_tcp($S,$d_host,$d_port);
return sub { readline($S) };
} else {
return undef unless open($S,$target);
return sub { return scalar <$S> };
}
return undef;
}
# input: pointer to target
# returns: true if daemon
# false if file
# target is modified in place
# to point to file if HASH->{file}
#
sub _want_daemon {
my ($tgp) = @_;
return undef unless $tgp;
return undef unless ref $$tgp eq 'HASH';
if ( exists ${$tgp}->{file} ) {
$$tgp = ${$tgp}->{file}; # replace with file name
return undef;
}
1;
}
=item read_daemon($subref,\@response);
B<read_daemon> retrieves the response text from a
file or daemon and places the lines in array.
input: $subref to execute
pointer to @response
returns: number of lines
fills @response
Note: use 'alarm_wrap' with this routine
=cut
sub read_daemon {
my ($subref,$ary) = @_;
while ($_ = &$subref) {
push @$ary, $_; # recover report from daemon
}
$ary = @$ary; # return number of lines
}
=item $error=fetch($target,\@response,$command);
B<fetch> a response from B<target> using B<args>. Essentially a combination
of B<daemon_handler> and B<read_daemon> wrapped with B<alarm_wrap>.
Retrieves data from the host or file specified by B<target>. The B<args>
argument is ignored if B<target> is a file.
input: target, # hash->{host} [optional]
# hash->{port} [optional]
# hash->{d_timeout} [optional]
\@response, # result lines
command, # what to tell host
returns: error if fail
false on success
=cut
sub fetch {
my ($target,$ary,$command) = @_;
local *DAEMON;
my $subref = daemon_handler(*DAEMON,$target);
return "failed to open target" unless $subref;
my $timeout = (&_want_daemon(\$target) && $target->{d_timeout})
? $target->{d_timeout}
: 180;
alarm_wrap($timeout,\&_fetch,*DAEMON,$target,$subref,$ary,$command);
close DAEMON;
return $@;
}
sub _fetch {
my ($DAEMON,$target,$subref,$ary,$command) = @_;
print $DAEMON $command,"\n"
if $command && &_want_daemon(\$target);
read_daemon($subref,$ary);
}
=item $alive = reap_kids(\%kids);
Non-blocking reaper for PID's in (keys %kids). Deletes zombie children from
%kids and returns the number of kids remaining.
input: \%kids # hash of child PID's
returns: number of kids remaining
=cut
sub reap_kids {
my ($kp) = @_;
return 0 unless (@_ = keys %$kp);
require POSIX;
$_ = &POSIX::WNOHANG;
foreach my $pid (@_) {
delete $kp->{$pid} if waitpid($pid,$_);
}
return scalar keys %$kp;
}
=item $rv = set_so_linger(*HANDLE,$seconds);
Set SO_LINGER on top level socket
input: *HANDLE, seconds
returns: true = success, false = fail
=back
=cut
sub set_so_linger {
my ($FH,$sec) = @_;
setsockopt($FH,SOL_SOCKET,SO_LINGER,pack("ll",1,$sec));
}
=head1 EXPORT_OK
TARPIT_PORT
open_listen_sock
open_tcp
alarm_wrap
daemon_handler
read_daemon
fetch
reap_kids
set_so_linger
[plus any Socket.pm variable]
=head1 COPYRIGHT
Copyright 2002, Michael Robinton & BizSystems
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
=head1 AUTHOR
Michael Robinton, michael@bizsystems.com
=head1 SEE ALSO
perl(1), Socket(3), LaBrea::Tarpit(3), LaBrea::Tarpit::Get(3), LaBrea::Tarpit::Report(3),
LaBrea::Tarpit::Util(3), LaBrea::Tarpit::DShield(3), LaBrea::Tarpit::Codes(3)
=cut
1;