smeserver-dhcpmanager/root/usr/share/smanager/lib/SrvMngr/Controller/Dhcpd.pm

705 lines
22 KiB
Perl
Raw Permalink Blame History

package SrvMngr::Controller::Dhcpd;
#----------------------------------------------------------------------
# heading : Network
# description : DHCP manager
# navigation : 6000 550
#
# name : dhcpd, method : get, url : /dhcpd, ctlact : Dhcpd#main
# name : dhcpd1, method : get, url : /dhcpd1, ctlact : Dhcpd#do_leases
# name : dhcpd2, method : get, url : /dhcpd2, ctlact : Dhcpd#do_winpopup
# name : dhcpd3, method : get, url : /dhcpd3, ctlact : Dhcpd#do_scan
# name : dhcpd4, method : get, url : /dhcpd4, ctlact : Dhcpd#do_delete_all_leases
# name : dhcpd5, method : post, url : /dhcpd5, ctlact : Dhcpd#do_update_config
# name : dhcpd6, method : get, url : /dhcpd6, ctlact : Dhcpd#do_delete_one_lease
# name : dhcpd7, method : get, url : /dhcpd7, ctlact : Dhcpd#do_refresh_leases
# name : dhcpd8, method : get, url : /dhcpd8, ctlact : Dhcpd#do_winpopup
# name : dhcpd9, method : get, url : /dhcpd9, ctlact : Dhcpd#do_wol
# name : dhcpd10, method : post, url : /dhcpd10, ctlact : Dhcpd#do_update_check
#
# routes : end
#
#
# Documentation: https://wiki.koozali.org/Dhcpmanager
#
use strict;
use warnings;
use Mojo::Base 'Mojolicious::Controller';
use constant FALSE => 0;
use constant TRUE => 1;
use Locale::gettext;
use SrvMngr::I18N;
use SrvMngr qw(theme_list init_session);
use Data::Dumper;
use esmith::util;
use esmith::HostsDB;
use esmith::AccountsDB;
use Net::Ping;
use esmith::util::network qw(:all);
use Socket qw( inet_aton );
#our $adb = esmith::AccountsDB->open() or die("Unable to open accounts DB");
my %dhcp_data = ();
sub main {
#
# Initial page - full summary of parameters etc
# Initial para from the Wiki.
#
my $c = shift;
%dhcp_data = ();
do_display($c);
}
sub do_display {
#
# Front parameters page
#
my $c = shift;
$c->app->log->info( $c->log_req );
my $title = $c->l("dhcpd_DHCP manager");
my $modul = '';
my $trt = "SETTINGS";
our $db = esmith::ConfigDB->open() or die("Unable to open Configuration DB");
our %sme_conf = $db->get('dhcpd')->props;
our %smb_conf = $db->get('smb')->props;
$dhcp_data{trt} = $trt;
$dhcp_data{"status"} = [[$c->l('dhcpd_ENABLED'),'enabled'],
[$c->l('dhcpd_DISABLED'),'disabled']
];
$dhcp_data{"check"} = [[$c->l('dhcpd_ENABLED'),'enabled'],
[$c->l('dhcpd_DISABLED'),'disabled']
];
if (! $sme_conf{'winscustom'} ) {
$sme_conf{'winscustom'} = 'disabled' ;
}
if (! $sme_conf{'dnscustom'} ) {
$sme_conf{'dnscustom'} = 'disabled' ;
}
if ( ! $sme_conf{'leasetime'} )
{ $sme_conf{'leasetime'} = "86400" ;
}
if (! $sme_conf{'gatewaycustom'} ) {
$sme_conf{'gatewaycustom'} = 'disabled' ;
}
# Accumulate parameters for Configuration DB
$dhcp_data{'params'} = \%sme_conf;
$dhcp_data{'smbparams'} = \%smb_conf;
$c->stash( title => $title,
modul => $modul,
dhcp_data => \%dhcp_data
);
#die("here");
$c->render( template => 'dhcpd' );
}
sub do_leases {
#
# Show a table of the leases
#
my $c = shift;
my $title = $c->l("dhcpd_MANAGING_DHCP_CLIENT");
my $modul = '';
my $trt = "LEASES";
$dhcp_data{"check"} = [[$c->l('dhcpd_ENABLED'),'enabled'],
[$c->l('dhcpd_DISABLED'),'disabled']
];
$dhcp_data{trt} = $trt;
$dhcp_data{"first"} = '';
my @leases = get_leases_in_array($c);
$c->stash( title => $title, modul => $modul, dhcp_data => \%dhcp_data, leases=> \@leases );
$c->render( template => 'dhcpd' );
}
sub do_winpopup {
#
# call to win pop up
#
my $c = shift;
my $title = $c->l("dhcpd_GLOBAL_WINPOPUP");
my $modul = '';
my $trt = "WINPOPUP";
$dhcp_data{trt} = $trt;
$dhcp_data{"first"} = '';
#..... get winpopup details
$c->stash( title => $title, modul => $modul, dhcp_data => \%dhcp_data );
$c->render( template => 'dhcpd' );
}
sub do_scan {
#
# call to show scan results
#
my $c = shift;
my $title = $c->l("dhcpd_SCANNING_NETWORK_TITLE");
my $modul = '';
my $trt = "SCAN";
$dhcp_data{trt} = $trt;
$dhcp_data{"first"} = '';
# ..... get scan results into stash for passing to html template
my $dhcp_scanresults = get_scan_results($c);
$c->stash( title => $title, modul => $modul, "dhcp_data" => \%dhcp_data, "scanresults" => $dhcp_scanresults);
$c->render( template => 'dhcpd' );
}
sub do_update_config {
#
# Update config dhcp parameters
# called through form submit.
#
my $c = shift;
# Input results are in $c->param(<fieldname>).
# If parameters do not validate, then return error message.
# else write into config DB, and...
# signal-event and ...return ok
$dhcp_data{"success"} ="";
my $ret = Main_Save($c);
if ($ret eq 'ok') {
$dhcp_data{"success"}="dhcpd_SUCCESSFULLY_SAVED_SETTINGS";
} else {
$dhcp_data{"error"}=$ret;
}
do_display($c);
return;
}
sub do_update_check {
#Just update the check parameter
my $c = shift;
my $dhcpd_check = $c->param ('dhcp_check');
###Update SME configuration dbase
my $dbh_sme = esmith::ConfigDB->open('/home/e-smith/db/configuration');
##Initiate get method --> create record object
my $sme_record = $dbh_sme->get('dhcpd');
$sme_record->set_prop('check' , $dhcpd_check);
$dhcp_data{"success"}="dhcpd_SUCCESSFULLY_SAVED_SETTINGS";
do_display($c);
return;
}
sub Main_Save ($){
##Pull CGI object from parameters array
my $q = shift;
###Build Hash of config parameters to update from cgi submit
my $dhcpd_status = $q->param ('dhcp_enable');
my $dhcpd_winscustom = $q->param ('dhcp_winscustom');
my $dhcpd_check = $q->param ('dhcp_check');
my $dhcpd_start = $q->param ('dhcp_start');
my $dhcpd_end = $q->param ('dhcp_end');
my $dhcpd_winsserver = $q->param ('dhcp_winsserver');
my $dhcpd_leasetime = $q->param ('dhcp_leasetime');
my $dhcpd_dnscustom = $q->param ('dhcp_dnscustom');
my $dhcpd_dns1server = $q->param ('dhcp_dns1server');
my $dhcpd_dns2server = $q->param ('dhcp_dns2server');
my $dhcpd_dns3server = $q->param ('dhcp_dns3server');
my $dhcpd_gatewaycustom = $q->param ('dhcp_gatewaycustom');
my $dhcpd_gateway = $q->param ('dhcp_gateway');
###Update SME configuration dbase
my $dbh_sme = esmith::ConfigDB->open('/home/e-smith/db/configuration');
##Initiate get method --> create record object
my $sme_record = $dbh_sme->get('dhcpd');
#get localip of server
my $local_ip = $dbh_sme->get_value('LocalIP');
##Set status of service
$sme_record->set_prop('status', $dhcpd_status);
$sme_record->set_prop('check' , $dhcpd_check);
$sme_record->set_prop('winscustom', $dhcpd_winscustom);
$sme_record->set_prop('leasetime' , $dhcpd_leasetime);
$sme_record->set_prop('dnscustom' , $dhcpd_dnscustom);
$sme_record->set_prop('gatewaycustom' , $dhcpd_gatewaycustom);
#checkip to the dhcpserver, perform the save in DB configuration or display an error if value != of a valid ip or if dhcp_start is greater than dhcp_end
if ($dhcpd_status eq "enabled")
{
if ( isValidIP ($dhcpd_start) && isValidIP ($dhcpd_end))
{
#check if $dhcpd_start is greater than $dhcpd_end and if yes, display an error message.
if (inet_aton($dhcpd_start) ge inet_aton($dhcpd_end))
{
return $q->l('dhcpd_DHCP_START_GREATER_DHCP_END_ERRORS') . ' (' . $dhcpd_start . '/' . $dhcpd_end .')';
}
elsif ( ( (inet_aton($dhcpd_start) le inet_aton($local_ip) ) && ( inet_aton($dhcpd_end)) ge inet_aton($local_ip) ) )
{
#display an error if the range of dhcp server include the ip of the server address
return $q->l('dhcpd_DHCP_RANGE_MUST_NOT_INCLUDE_SERVER_IP') . ' (' . $local_ip . ')';
}
else
{
#set value
my $dhcpd_start = cleanIP($dhcpd_start);
my $dhcpd_end = cleanIP($dhcpd_end);
$sme_record->set_prop('end', $dhcpd_end);
$sme_record->set_prop('start', $dhcpd_start);
}
}
#if $dhcpd_start or $dhcpd_end are not valid ip then display an error
else
{
return $q->l('dhcpd_DHCP_RANGE_WITH_BAD_IP') . ' (' . $dhcpd_start . '/' . $dhcpd_end .')';
}
}
#checkip to the winserver perform the save in DB configuration or display an error if value != of a valid ip
if ($dhcpd_winscustom eq "enabled")
{
if ( isValidIP ($dhcpd_winsserver) )
{
#set value
my $dhcpd_winsserver = cleanIP($dhcpd_winsserver);
$dbh_sme->set_prop('smb','WINSServer', $dhcpd_winsserver);
}
else
{
#if $dhcpd_winsserver is not valid ip then display an error
return $q->l('dhcpd_WINSSERVER_BAD_IP') . ' (' . $dhcpd_winsserver .')';
}
}
elsif ($dhcpd_winscustom eq "disabled")
{
my $delws = $dbh_sme->get('smb');
$delws->delete_prop('WINSServer');
}
#checkip to the dnsserver custom, perform the save in DB configuration or display an error if value != of a valid ip
if ($dhcpd_dnscustom eq "enabled")
{
#check if $dhcpd_dns1server and ( $dhcpd_dns2server are valid ip or $dhcpd_dns2server = null )
if ( isValidIP ($dhcpd_dns1server) && (isValidIP($dhcpd_dns2server) || ( $dhcpd_dns2server eq "") ) && (isValidIP($dhcpd_dns3server) || ( $dhcpd_dns3server eq "") ) )
{
#set value
my $dhcpd_dns1server = cleanIP($dhcpd_dns1server);
$sme_record->set_prop('dns1server' , $dhcpd_dns1server);
my $dhcpd_dns2server = cleanIP($dhcpd_dns2server);
$sme_record->set_prop('dns2server' , $dhcpd_dns2server);
my $dhcpd_dns3server = cleanIP($dhcpd_dns3server);
$sme_record->set_prop('dns3server' , $dhcpd_dns3server);
}
else
{
##if $dhcpd_dns1server or $dhcpd_dns2server or $dhcpd_dns3server are not valid ip then display an error
return $q->l('dhcpd_DNS_SERVER_WITH_BAD_IP') . ' (' . $dhcpd_dns1server . '/' . $dhcpd_dns2server . '/' . $dhcpd_dns3server .')';
}
}
#checkip to the gateway_custom perform the save in DB configuration or display an error if value != of a valid ip
if ($dhcpd_gatewaycustom eq "enabled")
{
if ( isValidIP ($dhcpd_gateway) )
{
#set value
my $dhcpd_gateway = cleanIP($dhcpd_gateway);
$sme_record->set_prop('gateway' , $dhcpd_gateway);
}
else
{
#if $dhcpd_gateway is not valid ip then display an error
return $q->l('dhcpd_GATEWAY_BAD_IP') . ' (' . $dhcpd_gateway .')';
}
}
# - 4 expand templates
# changed to new sme standard signal-event
system ("/sbin/e-smith/signal-event","workgroup-update") == 0
or die "Error while saving settings: $!";
return 'ok';
exit;
}
sub do_delete_all_leases {
#
# Delete all the specified lease
# Called from button at top of leases list panel
#
my $c = shift;
my $ret = delete_all_leases($c);
if ($ret == 'ok') {
$dhcp_data{"success"}="dhcpd_SUCCESSFULLY_SAVED_SETTINGS";
do_leases($c);
}
else {$dhcp_data{"error"}=$ret;}
return ;
}
sub do_delete_one_lease {
#
# Delete the specified lease
# Called from link in table of leases
#
my $c = shift;
# Lease in $c->param("lease")
# Validate - if not return error message
# delete it
# If deletion not ok return message
# else return "ok"
my $ret = delete_one_lease($c);
if ($ret == 'ok') {
$dhcp_data{"success"}="dhcpd_SUCCESSFULLY_SAVED_SETTINGS";
do_leases($c);
}
else {$dhcp_data{"error"}=$ret;}
return ;
}
sub get_scan_results {
my $c = shift;
#...do it
return Scan_Local_Network($c);
}
sub update_config {
my $c = shift;
#...do it
return "ok";
}
sub delete_one_lease {
my $c = shift;
Perform_Del_Lease($c);
return "ok";
}
sub delete_all_leases {
my $c = shift;
Perform_del_all_Lease($c);
return "ok";
}
sub get_leases_in_array {
my $c = shift;
my @leases = Load_leases($c);
# ...do it
return @leases;
}
sub Perform_del_all_Lease ($){
system ('/bin/echo "" > /var/lib/dhcpd/dhcpd.leases') ==0
or die "Error while removing all leases: $!";
system ("/sbin/e-smith/signal-event","workgroup-update") == 0
or die "Error while saving settings: $!";
return 'ok';
exit;
}
#===============================================================================
#SUBROUTINE: Perform delete lease
#===============================================================================
sub Perform_Del_Lease($){
###Pull CGI object from parameters array
my $c = shift;
#my $fred = 1/0;
###Pull entry to delete
my $ip = $c->param('ip');
my $name = $c->param('name');
my $name_in_file = '/var/lib/dhcpd/dhcpd.leases' ;
my $name_tmp_file = '/var/lib/dhcpd/dhcpd.leases.tmp' ;
my $name_out_file = '/var/lib/dhcpd/dhcpd.leases~' ;
my $del_current = "0" ;
open(INFILE,"<$name_in_file") || die "Read Error $name_in_file, Read: $!";
open(OUTFILE,">$name_tmp_file") || die "Write error $name_in_file, Write: $!";
while (<INFILE>) {
if ( "$_" =~ /lease $ip/ ) {
$del_current = "1" ;
}
if ( $del_current == "0" ) { print OUTFILE "$_" ; }
if ( "$_" =~ /}/ ) {
$del_current = "0" ;
}
}
rename ($name_tmp_file,$name_in_file) ;
system ("/bin/cp","-f","$name_in_file","$name_out_file") ;
close(INFILE);
close(OUTFILE);
# changed to new sme standard signal-event
system ("/sbin/e-smith/signal-event","workgroup-update") == 0
or die "Error while saving settings: $!";
###Return action message
return $c->l('SUCCESSFULLY_DELETED_THE_CLIENT') . ' (' . $name . '/' . $ip .')';
}
#===============================================================================
#SUBROUTINE: Scan The Local Network
#===============================================================================
sub dec2bin {
my $str = unpack("B32", pack("N", shift));
return $str;
}
sub netmask2cidr {
my ($mask, $network) = @_;
my @octet = split (/\./, $mask);
my @bits;
my $binmask;
my $binoct;
my $bitcount=0;
foreach (@octet) {
$binoct = dec2bin($_);
$binmask = $binmask . substr $binoct, -8;
}
# let's count the 1s
@bits = split (//,$binmask);
foreach (@bits) {
if ($_ eq "1") {
$bitcount++;
}
}
my $cidr = $network . "/" . $bitcount;
return $cidr;
}
sub get_mac_address {
# From the leases file
my ($ip, $file) = @_;
open(my $fh, '<', $file) or die "Could not open file '$file' $!";
my $mac_address = undef;
my $lease_block;
while(my $line = <$fh>) {
if($line =~ /lease $ip {/ .. $line =~ /}/) {
$lease_block .= $line;
if($line =~ /}/) {
if($lease_block =~ /hardware ethernet (\S+);/) {
$mac_address = $1;
last;
} else {
$lease_block = '';
}
}
}
}
close $fh;
return $mac_address;
}
sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };
sub get_mac_address_by_ping {
my ($ip) = trim(@_);
# Ping the IP to ensure it's in the ARP table
my $ping_result = `ping -c 1 -W 1 $ip`;
#return $ping_result;
if($ping_result =~ m/1 packets transmitted, 1 received/) {
# Fetch the ARP table and find the line with the IP address
my $mac_result = `nmap -PR -sn $ip`;
#return "*$arp_result *$ip*";
# If found, return the MAC address
#if ($arp_result =~ m/.*at ((?:[0-9a-f]{2}\:){5}[0-9a-f]{2})/) {
if ($mac_result =~ /MAC Address: (..:..:..:..:..:..) /) {
return $1;
} else { return "mac not in nmap result";}
} else { return "no ping";}
return undef;
}
sub Scan_Local_Network ($) {
my $nmap_sme = esmith::ConfigDB->open('/home/e-smith/db/configuration');
my $mask = $nmap_sme->get_value('LocalNetmask');
my $network = $nmap_sme->get_prop('InternalInterface','Network');
#we start to calculate the method to find the cidr (we want to use network/cidr with nmap)
my $cidr = netmask2cidr($mask, $network);
#ok go to use nmap with nmap -T4 -sP network/cidr
my @nmap_output= `/usr/bin/nmap --script smb-os-discovery.nse -p U:137,T:139 $cidr|
/bin/grep -Ev "MAC|NetBIOS|OS CPE"| /bin/grep -E "scan|done|Computer|OS"|
/bin/sed -e 's/Nmap scan/-- Scan/g'|/bin/sed -e 's/done/Done/g'|
/bin/sed -e 's/Nmap//g'|/bin/sed -e 's/|/ /g'`;
my @extracted_output;
my $leasefile = "/var/lib/dhcpd/dhcpd.leases";
foreach my $line (@nmap_output) {
if ($line =~ m/Scan report for ([\w\.-]+) \(([\d\.\:]+)\)/) {
my $ip = $2;
my $mac = get_mac_address($ip,$leasefile);
if (!$mac){
$mac = get_mac_address_by_ping($ip);
}
# if (!$mac) {$mac = "unknown";}
my %pair = ('Network' => $1, 'ip' => $ip, 'mac' => $mac); # $1 => PC Name, $2 => IP Address
push @extracted_output, \%pair;
}
}
return \@extracted_output;
}
#=========================================================================
# Procedure qui charge le dhcpd.conf
# retourne un tableau contenant les informations
#=========================================================================
sub Load_leases ($){
my $q = shift;
#Definition de variable
use vars qw/
@detail $work_line $header /;
my $name_file = '/var/lib/dhcpd/dhcpd.leases' ;
our @liste_computer = '' ;
our $db = esmith::ConfigDB->open() or die("Unable to open Configuration DB");
our %sme_conf = $db->get('dhcpd')->props;
#Ouverture du fichier en lecture seule
open(FILE,"<$name_file") || die "Read error $name_file, Error: $!" ;
#Parcours du fichier dhcpd.leases
while (<FILE>) {
# suppression des commentaires
if ($_ =~ /^#/) { next; }
#spurresion des lignes vides
if ($_ =~ /^$/) { next; }
#supression de la ligne uid de windows
if ($_ =~ /uid/) { next; }
#supression de la ligne tstp de XP
if ($_ =~ /tstp/) { next; }
#supression de la ligne "binding state active"
if ($_ =~ /binding/) { next; }
#supression de la ligne "next binding state"
if ($_ =~ /next/) { next; }
#suprresion des commentaire et des ;
$_ =~ s/\s#.*$//;
$_ =~ s/(;|{) *$//;
#supression des espace en debut de lignes
$_ =~ s/^ *\s//;
#Suppression des retours chariot en fin de lignes.
chomp $_ ;
#Chargement d'un tableau avec le contenu de la ligne.
(@detail) = split(/ /, $_) ;
#initialisation d'un nouveau poste connect<63>
if ($_ =~ /lease/) {
$work_line = "" ;
}
$header = shift(@detail);
$work_line = $work_line . ";" . join(" ",@detail) ;
if ($header =~ /}/) {
$work_line =~ s/ /;/g;
$work_line =~ s/^;//;
$work_line =~ s/"//g;
$work_line =~ s/;$//;
#we want the last entry is the first element of the array
unshift(@liste_computer,$work_line) ;
}
}
close(FILE);
# Now check if up and extract details
my @mac = ();
my @count = ();
my @results = ();
my @liste_connected = ();
foreach (@liste_computer){
my @computer = split(/;/, $_) ;
#we want to sort elements the array by mac address
$computer[11] = uc($computer[11]) ;
push @mac, ($computer[11]);
my @count;
push @count , (grep /$computer[11]/ , @mac);
next if (scalar @count > 1);
#we start the table if it is a valid IP
if ( $computer[0] ) {
$computer[12] = uc($computer[12]) ;
my $datedeb = $computer[2] . "-" . $computer[3] ;
my $datefin = $computer[5] . "-" . $computer[6] ;
my $status = 'dhcpd_NOT_CHECKED';
#my $status = '<a href=/smanager/dhcpd1'."?state=wake_up&MAC=" . $computer[11] . "&name=" . $computer[12].">".$q->l('dhcpd_NOT_CHECKED')."</a>";
if ( $sme_conf{'check'} =~ /enabled/ ) {
use Net::Ping;
my($ping_obj) = Net::Ping->new("icmp");
# formatage des dates
$computer[3] = substr($computer[3],0,5);
$computer[6] = substr($computer[6],0,5);
if ($ping_obj->ping("$computer[0]")) {
push(@liste_connected,$computer[12]) ;
$status = 'dhcpd_ACTIVE_DEVICE';
#alimentation de la liste des connect<63>
}
else { $status = 'dhcpd_WAKE_UP_ACTION';}
#else { $status = '<a href=/smanager/dhcpd1'."?state=wake_up&MAC=" . $computer[11] . "&name=" . $computer[12].">".$q->l('dhcpd_WAKE_UP_ACTION')."</a>";
}
my %ipdetails = (ip=>$computer[0],name=>$computer[12],wol=>$status,start=>$datedeb,end=>$datefin,mac=>$computer[11]);
push(@results,\%ipdetails);
}
else {
push(@liste_connected,$computer[12]) ;
}
}
return @results;
}
sub winpopup{
my $c = shift;
# Message in $c->param("winpopupmsg")
# .... do it
return TRUE;
}
sub do_wol{
my $c = shift;
my $retVal = Perform_Wake_Up($c);
if ($retVal) {$dhcp_data{"success"} = $retVal;}
do_leases($c);
return TRUE;
}
#===============================================================================
#SUBROUTINE: Perform wake UP
#===============================================================================
sub Perform_Wake_Up($){
###Pull CGI object from parameters array
my $q = shift;
###Get Mac Adress
my $mac = uc($q->param('mac'));
my $name = uc($q->param('name'));
#Send Wake Up to the station
#Old version obsolete since 1.5.0 - system ("/usr/bin/wol","$mac") ;
system ("/sbin/ether-wake","$mac") ;
###Return action message
return 'dhcpd_SUCCESSFULLY_CLIENT_WOL_REQUEST';
}
1;