telecaster.pl
#!/usr/bin/perl -w
sub usage {
local $OUTPUT_RECORD_SEPARATOR = '';
pod2usage (-exitval => 1, -verbose => 0);
}
our $VERSION = 'ambulance';
$OUTPUT_RECORD_SEPARATOR = "\n";
use strict;
use warnings;
use English;
use Socket;
use Getopt::Long 2.33;
use Pod::Usage;
my (@DESTS, @IFACES, $PORT, $DRY);
GetOptions (
'dest=s' => \@DESTS,
'iface=s' => \@IFACES,
'port=i' => \$PORT,
'test' => \$DRY,
) or usage ();
(defined ($PORT) and $PORT > 0 and $PORT < 65536) or usage ();
defined ($DRY) or $DRY = 0;
if ($#DESTS < 0 and $#IFACES < 0) { @DESTS = inet_ntoa (INADDR_BROADCAST) }
foreach (@IFACES) {
open (IFCONFIG, "ifconfig '$_' 2>&1 |"
or die ("open fail: $!"
;
while () {
/^\s*inet\s+.*broadcast\s+(.*)\s*/ and push (@DESTS, $1);
}
close (IFCONFIG) or warn ("ifconfig fail: $! $?"
;
}
my $DEST;
for (my $ID = 0; $ID <= $#DESTS; $ID++) {
if ($DEST = inet_aton ($DESTS [$ID])) {
$DESTS [$ID] = $DEST;
} else {
warn ("bad destination: ${DESTS [$ID]}"
;
splice (@DESTS, $ID, 1);
$ID -= 1;
}
}
@DESTS = keys %{{ map { $_ => 1 } @DESTS }}; #this voodoo uniq's DESTS
my $MSG = '';
if ($#ARGV < 0) {
while () { $MSG .= $_; }
@ARGV = $MSG;
}
socket (UDP, PF_INET, SOCK_DGRAM, getprotobyname ('udp')) or die ("socket fail: $!"
;
setsockopt (UDP, SOL_SOCKET, SO_BROADCAST, 1) or die ("setsockopt fail: $!"
;
foreach $MSG (@ARGV) {
if ($MSG) {
foreach $DEST (@DESTS) {
if ($DRY) {
print ("send ".length ($MSG)." bytes to ".inet_ntoa ($DEST).":$PORT"
;
} else {
send (UDP, $MSG, 0, sockaddr_in ($PORT, $DEST)) or warn ("send fail: $!"
;
}
}
} else { warn ("can't send an empty message"
}
}
__END__
=head1 NAME
B -- send or broadcast your messages over UDP
=head1 SYNOPSIS
B [B<-t>] [B<-d> I] [B<-i> I] B<-p> I [B<] [I ..]
Use B< to explicitely separate the messages from the options.
If no messages are given as arguments, reads for one on STDIN.
=head1 OPTIONS
=over 4
=item B<--dest> or B<-d>
Specifies a destination IP address.
May be supplied more than once.
=item B<--iface> or B<-i>
Specifies an interface name to use its broadcast address.
Actually this option is passed to L, so you can specify anything
L expects, for example an interface group.
May be supplied more than once.
=item B<--port> or B<-p>
Specifies a UDP port.
Since there is no sensible default value, this option is mandatory.
=item B<--test> or B<-t>
Do not actually send anything, just show what will be done.
=item B<--version> or B<-v>
Show the program version and exit.
=item B<--help> or B<-h>
Show a short syntax summary and exit.
=back
=head1 DESCRIPTION
This program allows you to broadcast data over UDP or send it to a host.
You have to specify the UDP port number and you may provide the destination
address (possibly a broadcast one). The text of message may be given as
an argument (there may be more than one argument, in this case every argument
will be treated as a separate message and will be sent as such) or you may
pipe the text to the program. Note that STDIN will only be read if no arguments
are given.
Passing more than one destination and interface is supported. The sсript tries
to determine every possible address, removing duplicates, then the message(s)
will be sent to every address supplied. If there is not a single address to
send to, nothing will be sent (obviously). Note that the default value of
B<255.255.255.255> (a local network broadcast) will only be used if not a single
destination or interface is given.
Be warned that this program is not designed to send an arbitrary stream of data.
No packet management is available, it just gets a string and sends it, that's all.
Also, seems that the maximum size of the message cannot exceed ( - 28) bytes.
Exit status is B<0> on success or some non-zero value if any error occurs.
=head1 EXAMPLES
B
This command will send two messages: "hello" and "bye bye" to host 10.10.10.10 on port 12345.
B
This command will broadcast two messages: "hello" and "bye bye" to every host in the network 10.0.0.0/8 on port 12345.
The same messages will also be sent to the broadcast addresses of all the interfaces in the group "carp".
B
This command will broadcast a message "Wait, what?" to every host in the segment on port 12345.
=head1 SEE ALSO
L
=head1 BUGS
B<--iface> option parses L's output, which differs between systems. Only tested on OpenBSD currently. Should not
work in Linux.
A broadcast to 255.255.255.255 does not seem to actually work. One has to set the route explicitely. (SO_DONTROUTE, bind to i
face?)
=head1 LICENSE
This software is public domain.
=cut
tbrd.pl
#!/usr/bin/perl -w
sub usage {
local $OUTPUT_RECORD_SEPARATOR = '';
pod2usage (-exitval => 1, -verbose => 0);
}
sub cleanup {
defined (fileno (CHILD)) and close (CHILD);
defined ($PIDFILE) and -e ($PIDFILE) and unlink ($PIDFILE);
}
our $VERSION = 'absolute';
$OUTPUT_RECORD_SEPARATOR = "\n";
use strict;
use warnings;
use English;
use Socket;
use Getopt::Long 2.33 qw(:config no_ignore_case);
use Pod::Usage;
use POSIX;
use MIME::Base64;
my ($NOFORK, $CHILD, $MYPORT, $MYADDR, $PIDFILE);
GetOptions (
'no-detach' => \$NOFORK,
'call=s' => \$CHILD,
'port=i' => \$MYPORT,
'addr=s' => \$MYADDR,
'Pidfile=s' => \$PIDFILE,
) or usage ();
defined ($CHILD) or usage ();
(defined ($MYPORT) and $MYPORT > 0 and $MYPORT < 65536 and $CHILD) or usage ();
defined ($NOFORK) or $NOFORK = 0;
defined ($PIDFILE) or $PIDFILE = '/var/run/tbrd.pl.pid';
defined ($MYADDR) or $MYADDR = inet_ntoa (INADDR_ANY);
my $MYIP = inet_aton ($MYADDR) or die ("bad IP address: $MYADDR"
;
chdir ('/') or die ("chdir fail: $!"
;
umask (0) or die ("umask fail: $!"
;
open (STDIN, '/dev/null') or die ("reopening STDIN fail: $!"
;
my $FORKED_PID;
if (! $NOFORK) {
setsid () or die ("setsid fail: $!"
;
defined ($FORKED_PID = fork ()) or die ("fork fail: $!"
;
$FORKED_PID and exit ();
-e $PIDFILE and die ("pidfile $PIDFILE exists"
;
open (PID, ">$PIDFILE"
or die ("open pidfile fail: $!"
;
syswrite (PID, $PROCESS_ID) or die ("write to pidfile fail: $!"
;
close (PID) or die ("close pidfile fail: $!"
;
open (STDOUT, '>/dev/null') or die ("reopening STDOUT fail: $!"
;
open (STDERR, '>/dev/null') or die ("reopening STDERR fail: $!"
;
}
socket (UDP, PF_INET, SOCK_DGRAM, getprotobyname ('udp')) or die ("socket fail: $!"
;
bind (UDP, sockaddr_in ($MYPORT, $MYIP)) or die ("bind fail: $!"
;
print ("Bound to $MYADDR:$MYPORT okay."
;
$SIG{CHLD} = 'IGNORE';
open (CHILD, "| $CHILD"
or die ("opening child process ($CHILD) fail: $!"
;
print ("Opened child ($CHILD) okay."
;
my $MAXSIZE = 10240;
my ($MSG, $PEERADDR, $PEERPORT, $LINE);
while (1) {
($PEERPORT, $PEERADDR) = sockaddr_in (recv (UDP, $MSG, $MAXSIZE, 0));
$LINE = inet_ntoa ($PEERADDR)." $PEERPORT ".encode_base64 ($MSG, '');
syswrite (CHILD, $LINE."\n"
;
print ($LINE);
}
END {
cleanup ();
}
__END__
=head1 NAME
B -- a trivial UDP broadcast receiver daemon
=head1 SYNOPSIS
B [B<-n>] [B<-a> I] [B<-P> I] B<-p> I B<-c> F
=head1 OPTIONS
=over 4
=item B<--addr> or B<-a>
Specifies an IP address to bind to.
B<0.0.0.0> (all interfaces) is used if not given.
=item B<--port> or B<-p>
Specifies a UDP port.
Since there is no sensible default value, this option is mandatory.
=item B<--call> or B<-c>
Specifies a program to run. This options is mandatory.
=item B<--Pidfile> or B<-P>
Specifies a filename to write the process ID of the forked precess.
If B<--no-detach> option is given, PID will not be written at all.
=item B<--no-detach> or B<-n>
Tells the daemon to not detach from the terminal and to echo some information.
Useful for debugging.
=item B<--version> or B<-v>
Show the program version and exit.
=item B<--help> or B<-h>
Show a short syntax summary and exit.
=back
=head1 DESCRIPTION
This program binds to a UDP port and listens for UDP packets on it.
Every packet received is then supplied to some external program or
sсript (defined with B<--call> option). It does nothing besides this.
The child is called as the same user as the daemon itself.
The packet contents is piped on child's STDIN along with sender's
IP address and UDP port.
So the logic is like this:
1. Daemon binds to a socket and forks out (unless B<--no-detach> option was given);
2. Daemon starts a program specified and opens its STDIN;
3. Every packet received is fed to a program's STDIN like this: sender's address,
a space, sender's port, a space, base64-encoded message.
=head2 Security considerations
If you need to bind to a priviledged port you have to run this daemon as root.
If you need to run the child as root (for example you have to modify the routing table),
then you should run this daemon as root also.
=head1 FILES
=over 4
=item F
Contains the process ID of the running daemon. The name of this file can be manually set with
B<--Pidfile> option.
=back
=head1 SEE ALSO
L
=head1 BUGS
This program is intentionally developed in the most primitive way to keep things as simple
as possible. If you need anything this program does not do (like binding to multiple addresses)
it is advised to search for a more appropriate tool.
The program should complain about the problems to syslog rather than just STDERR.
=head1 LICENSE
This software is public domain.
=cut