#!/usr/bin/perl
##########################################################################
# #
# Perl Script for send keep alive udp packets #
# Created on 110627 by oussama.hammami@switzernet.com #
# Switzernet(c)2011 #
# #
##########################################################################
# my modules
use strict;
use warnings;
use Sys::Syslog;
use POSIX;
use Config::IniFiles;
use Switch;
use DBI;
use Date::Manip;
use Time::Local;
use Net::RawIP;
use threads;
use threads::shared;
##########################################################################
# Setting
my $lock_file = '/var/run/ast-send-empty.pid';
my $runas_user = 'root';
my $config_dir = '/etc/astrad/config';
##########################################################################
# Globals
my ($dbh,$DB_NAME,$DB_HOST,$DB_USER,$DB_PASS);
my %peers;
my %peers_info;
##########################################################################
# Initialization #
##########################################################################
# Check if already running
if( -e $lock_file ) {
open(PID,$lock_file);
my $pid=<PID>;
close PID;
chomp $pid;
if( !-e "/proc/$pid" ) {
print STDERR "Lock file present, but no process with pid=$pid.\n";
die "Can't delete lock file $lock_file\n" if !unlink $lock_file;
print STDERR "Lock file has been removed.\n";
} else {
die "Lockfile present, another copy is punning pid=$pid\n";
}
}
my ($name, $passwd, $uid, $gid) = getpwnam($runas_user) or die "$runas_user not in passwd file";;
##########################################################################
# set C locale
POSIX::setlocale(LC_ALL, "C");
##########################################################################
# Become daemon
my $pid;
if( !defined($pid = fork()) ) {
die "cannot fork: $!";
} elsif ($pid) {
# Create lockfile, and finish parent process
open(PID, "> $lock_file") || die "ast-notify-send.pl: Unable to create lockfile $lock_file\n";
print PID "$pid";
close PID;
chown $uid, $gid, $lock_file;
exit;
} else {
# daemon
setpgrp();
select(STDERR); $| = 1;
select(STDOUT); $| = 1;
openlog('ast-notify-send', 'cons,pid', 'daemon');
syslog('notice', "Ast-Empty: Resend lost Accounting packets for Asterisk started");
}
##########################################################################
# Install signall handler
$SIG{INT} = \&safe_exit;
$SIG{QUIT} = \&safe_exit;
$SIG{TERM} = \&safe_exit;
$SIG{HUP} = \&load_config;
##########################################################################
# Drop privileges
setuid($uid);
$< = $uid;
$> = $uid;
##########################################################################
# Signal Handlers
sub safe_exit {
my($sig) = @_;
syslog('crit', "Ast-Empty: Caught a SIG - shutting down");
$dbh->disconnect if $dbh;
unlink $lock_file or syslog('crit', "Ast-Empty: Unable to create lockfile $lock_file\n");
closelog();
exit;
}
##########################################################################
# FUNCTION #
##########################################################################
# Load config from mysql.conf
sub load_config {
my $conf=Config::IniFiles->new(-file => "$config_dir/switzer.conf");
return 0 if !defined $conf ;
$DB_NAME = $conf->val('ASTER_DB','DB_NAME');
$DB_HOST = $conf->val('ASTER_DB','DB_HOST');
$DB_USER = $conf->val('ASTER_DB','DB_USER');
$DB_PASS = $conf->val('ASTER_DB','DB_PASS');
return 1;
}
##########################################################################
# Astrad Radius DB connect
sub DB_connect {
my $cop=0;
while( 1 ) {
$cop++;
$dbh = DBI->connect_cached("DBI:mysql:$DB_NAME;host=$DB_HOST",$DB_USER, $DB_PASS);
if( $dbh ) {
syslog('info', 'Ast-Empty: Connected to Astrad DB');
return 1;
}
else {
syslog('info', "Ast-Empty: Connection to Astrad DB failed -> SLEEP $cop");
return 0 if ($cop>=3);
}
sleep $cop;
}
}
##########################################################################
# Load register request from Astrad Reg. DB
sub load_peers {
DB_connect();
my $result;
my $request="SELECT username,contact FROM location3;";
syslog('info', "Ast-Empty: request = $request");
my $sth = $dbh->prepare($request);
return 0 if (!$sth->execute());
syslog('info', "Ast-Empty: sth->rows = ".$sth->rows);
$peers{$result->{username}}=$result->{contact} while ($result = $sth->fetchrow_hashref);
$sth->finish;
return 1;
}
##########################################################################
sub send_empty {
my ($phone_ip,$port,$acc)=@_;
my $ipid = int rand 5000 + 100;
my ($a,$p,$f);
$SIG{'KILL'} = sub { threads->exit(); };
$a = new Net::RawIP({udp=>{}});
$a->set({
ip => { saddr => '94.23.225.212',
daddr => $phone_ip,
id => $ipid,
frag_off => 0,
tos => 0,
protocol => 0x11
},
udp => { source => 5060,
dest => $port,
len => 9,
data=> chr(0)} # chr(0)
});
while (1) {
$a->send(0,1);
sleep 15;
}
}
##########################################################################
sub kill_thr {
my $acc=shift;
foreach my $thr (threads->list) {
if ($thr->tid && !threads::equal($thr, threads->self) && $thr->tid==$peers_info{$acc}{'tid'}) {
$thr->kill('KILL')->detach();
}
}
}
##########################################################################
sub set_tid {
foreach my $pr (keys %peers) {
if ($peers{$pr} =~ /^sip:\s*([^@]*)@([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}):([0-9]+)$/) {
my $thr = threads->new(\&send_empty,$2,$3,$1);
($peers_info{$1}{'ip'},$peers_info{$1}{'port'},$peers_info{$1}{'tid'})=($2,$3,$thr->tid());
}
}
return 1;
}
##########################################################################
sub check_thr {
foreach my $pr (keys %peers_info) {
if ($peers{$pr}) {
delete($peers{$pr});
}
else {
kill_thr($pr);
delete($peers_info{$pr});
}
}
return 1;
}
##########################################################################
# main() #
##########################################################################
load_config();
while (load_peers() && check_thr() && set_tid() ) {
sleep 15;
undef(%peers);
}
exit;
safe_exit();
##########################################################################
# END #
##########################################################################