GPL v3 Release

This commit is contained in:
chirpy
2025-08-28 15:15:56 +01:00
parent c15ade09f6
commit 2923c43f8f
494 changed files with 106870 additions and 58 deletions

100
csf/ConfigServer/AbuseIP.pm Normal file
View File

@@ -0,0 +1,100 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::AbuseIP;
use strict;
use lib '/usr/local/csf/lib';
use Carp;
use IPC::Open3;
use Net::IP;
use ConfigServer::Config;
use ConfigServer::CheckIP qw(checkip);
use Exporter qw(import);
our $VERSION = 1.03;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(abuseip);
my $abusemsg = 'Abuse Contact for [ip]: [[contact]]
The Abuse Contact of this report was provided by the Abuse Contact DB by abusix.com. abusix.com does not maintain the content of the database. All information which we pass out, derives from the RIR databases and is processed for ease of use. If you want to change or report non working abuse contacts please contact the appropriate RIR. If you have any further question, contact abusix.com directly via email (info@abusix.com). Information about the Abuse Contact Database can be found here:
https://abusix.com/global-reporting/abuse-contact-db
abusix.com is neither responsible nor liable for the content or accuracy of this message.';
my $config = ConfigServer::Config->loadconfig();
my %config = $config->config();
# end main
###############################################################################
# start abuseip
sub abuseip {
my $ip = shift;
my $abuse = "";
my $netip;
my $reversed_ip;
if (checkip(\$ip)) {
eval {
local $SIG{__DIE__} = undef;
$netip = Net::IP->new($ip);
$reversed_ip = $netip->reverse_ip();
};
if ($reversed_ip =~ /^(\S+)\.in-addr\.arpa/) {$reversed_ip = $1}
if ($reversed_ip =~ /^(\S+)\s+(\S+)\.in-addr\.arpa/) {$reversed_ip = $2}
if ($reversed_ip =~ /^(\S+)\.ip6\.arpa/) {$reversed_ip = $1}
if ($reversed_ip =~ /^(\S+)\s+(\S+)\.ip6\.arpa/) {$reversed_ip = $2}
if ($reversed_ip ne "") {
$reversed_ip .= ".abuse-contacts.abusix.org";
my $cmdpid;
eval {
local $SIG{__DIE__} = undef;
local $SIG{'ALRM'} = sub {die};
alarm(10);
my ($childin, $childout);
$cmdpid = open3($childin, $childout, $childout, $config{HOST},"-W","5","-t","TXT",$reversed_ip);
close $childin;
my @results = <$childout>;
waitpid ($cmdpid, 0);
chomp @results;
if ($results[0] =~ /^${reversed_ip}.+"(.*)"$/) {$abuse = $1}
alarm(0);
};
alarm(0);
if ($cmdpid =~ /\d+/ and $cmdpid > 1 and kill(0,$cmdpid)) {kill(9,$cmdpid)}
if ($abuse ne "") {
my $msg = $abusemsg;
$msg =~ s/\[ip\]/$ip/g;
$msg =~ s/\[contact\]/$abuse/g;
return $abuse, $msg;
}
}
}
}
# end abuseip
###############################################################################
1;

160
csf/ConfigServer/CheckIP.pm Normal file
View File

@@ -0,0 +1,160 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::CheckIP;
use strict;
use lib '/usr/local/csf/lib';
use Carp;
use Net::IP;
use ConfigServer::Config;
use Exporter qw(import);
our $VERSION = 1.03;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(checkip cccheckip);
my $ipv4reg = ConfigServer::Config->ipv4reg;
my $ipv6reg = ConfigServer::Config->ipv6reg;
# end main
###############################################################################
# start checkip
sub checkip {
my $ipin = shift;
my $ret = 0;
my $ipref = 0;
my $ip;
my $cidr;
if (ref $ipin) {
($ip,$cidr) = split(/\//,${$ipin});
$ipref = 1;
} else {
($ip,$cidr) = split(/\//,$ipin);
}
my $testip = $ip;
if ($cidr ne "") {
unless ($cidr =~ /^\d+$/) {return 0}
}
if ($ip =~ /^$ipv4reg$/) {
$ret = 4;
if ($cidr) {
unless ($cidr >= 1 && $cidr <= 32) {return 0}
}
if ($ip eq "127.0.0.1") {return 0}
}
if ($ip =~ /^$ipv6reg$/) {
$ret = 6;
if ($cidr) {
unless ($cidr >= 1 && $cidr <= 128) {return 0}
}
$ip =~ s/://g;
$ip =~ s/^0*//g;
if ($ip == 1) {return 0}
if ($ipref) {
eval {
local $SIG{__DIE__} = undef;
my $netip = Net::IP->new($testip);
my $myip = $netip->short();
if ($myip ne "") {
if ($cidr eq "") {
${$ipin} = $myip;
} else {
${$ipin} = $myip."/".$cidr;
}
}
};
if ($@) {return 0}
}
}
return $ret;
}
# end checkip
###############################################################################
# start cccheckip
sub cccheckip {
my $ipin = shift;
my $ret = 0;
my $ipref = 0;
my $ip;
my $cidr;
if (ref $ipin) {
($ip,$cidr) = split(/\//,${$ipin});
$ipref = 1;
} else {
($ip,$cidr) = split(/\//,$ipin);
}
my $testip = $ip;
if ($cidr ne "") {
unless ($cidr =~ /^\d+$/) {return 0}
}
if ($ip =~ /^$ipv4reg$/) {
$ret = 4;
if ($cidr) {
unless ($cidr >= 1 && $cidr <= 32) {return 0}
}
if ($ip eq "127.0.0.1") {return 0}
my $type;
eval {
local $SIG{__DIE__} = undef;
my $netip = Net::IP->new($testip);
$type = $netip->iptype();
};
if ($@) {return 0}
if ($type ne "PUBLIC") {return 0}
}
if ($ip =~ /^$ipv6reg$/) {
$ret = 6;
if ($cidr) {
unless ($cidr >= 1 && $cidr <= 128) {return 0}
}
$ip =~ s/://g;
$ip =~ s/^0*//g;
if ($ip == 1) {return 0}
if ($ipref) {
eval {
local $SIG{__DIE__} = undef;
my $netip = Net::IP->new($testip);
my $myip = $netip->short();
if ($myip ne "") {
if ($cidr eq "") {
${$ipin} = $myip;
} else {
${$ipin} = $myip."/".$cidr;
}
}
};
if ($@) {return 0}
}
}
return $ret;
}
# end cccheckip
###############################################################################
1;

View File

@@ -0,0 +1,533 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
# no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::CloudFlare;
use strict;
use lib '/usr/local/csf/lib';
use Carp;
use Fcntl qw(:DEFAULT :flock);
use JSON::Tiny();
use LWP::UserAgent;
use Time::Local();
use ConfigServer::Config;
use ConfigServer::Slurp qw(slurp);
use ConfigServer::Logger qw(logfile);
use Exporter qw(import);
our $VERSION = 1.00;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw();
my $config = ConfigServer::Config->loadconfig();
my %config = $config->config();
my $slurpreg = ConfigServer::Slurp->slurpreg;
my $cleanreg = ConfigServer::Slurp->cleanreg;
my %args;
$args{"content-type"} = "application/json";
if ($config{DEBUG} >= 2) {
require Data::Dumper;
import Data::Dumper;
}
if (-e "/usr/local/cpanel/version") {
require YAML::Tiny;
}
# end main
###############################################################################
# start action
sub action {
my $action = shift;
my $ip = shift;
my $mode = shift;
my $id = shift;
my $domainlist = shift;
my $allowany = shift;
my $status;
my $return;
if ($config{DEBUG} == 1) {logfile("Debug: CloudFlare - [$action] [$ip] [$mode] [$id] [$domainlist] [$allowany]")}
unless ($config{URLGET}) {
logfile("CloudFlare: URLGET must be set to 1 to use LWP for this feature");
return;
}
if ($action eq "remove") {
my @newfile;
sysopen (my $TEMP, "/var/lib/csf/cloudflare.temp", O_RDWR | O_CREAT);
flock($TEMP, LOCK_EX);
my $hit;
while (my $line = <$TEMP>) {
chomp $line;
my ($rip,$mode,$user,$raccount,$rapikey,$rid,$time) = split(/\|/,$line);
if ($ip eq $rip) {
$args{"X-Auth-Email"} = $raccount;
$args{"X-Auth-Key"} = $rapikey;
$status = &remove($ip,$mode,$rid);
logfile($status." ($user)");
$hit = 1;
} else {
push @newfile, $line;
}
}
if ($hit) {
seek ($TEMP, 0, 0);
truncate ($TEMP, 0);
foreach my $line (@newfile) {
print $TEMP $line."\n";
}
}
close ($TEMP);
} else {
my %authlist;
my %domains;
foreach my $domain (split(/\,/,$domainlist)) {
$domain =~ s/\s//g;
if ($domain eq "") {next}
$domain =~ s/^www\.//;
$domains{$domain} = 1;
}
my $scope = &getscope();
foreach my $user (keys %{$scope->{user}}) {
if ($allowany and ($scope->{user}{$user}{domain} eq "any" or $scope->{user}{$user}{any})) {
$authlist{$scope->{user}{$user}{account}}{apikey} = $scope->{user}{$user}{apikey};
$authlist{$scope->{user}{$user}{account}}{user} = $user;
}
foreach my $domain (keys %domains) {
if ($scope->{domain}{$domain}{user} eq $user) {
$authlist{$scope->{domain}{$domain}{account}}{apikey} = $scope->{domain}{$domain}{apikey};
$authlist{$scope->{domain}{$domain}{account}}{user} = $scope->{domain}{$domain}{user};
}
foreach my $userdomain (keys %{$scope->{user}{$user}{domain}}) {
if ($user eq $domain and $scope->{user}{$user}{domain}{$userdomain} ne "") {
$authlist{$scope->{user}{$user}{account}}{apikey} = $scope->{user}{$user}{apikey};
$authlist{$scope->{user}{$user}{account}}{user} = $user;
}
}
}
}
my @list;
foreach my $account (sort keys %authlist) {
$args{"X-Auth-Email"} = $account;
$args{"X-Auth-Key"} = $authlist{$account}{apikey};
my $user = $authlist{$account}{user};
if ($action eq "deny") {
my ($id,$status) = &block($ip);
logfile($status." ($user)");
sysopen (my $TEMP, "/var/lib/csf/cloudflare.temp", O_WRONLY | O_APPEND | O_CREAT);
flock($TEMP, LOCK_EX);
print $TEMP "$ip|$mode|$user|$account|$authlist{$account}{apikey}|$id|".time."\n";
close ($TEMP);
}
elsif ($action eq "allow") {
my ($id,$status) = &whitelist($ip);
logfile($status." ($user)");
sysopen (my $TEMP, "/var/lib/csf/cloudflare.temp", O_WRONLY | O_APPEND | O_CREAT);
flock($TEMP, LOCK_EX);
print $TEMP "$ip|$mode|$user|$account|$authlist{$account}{apikey}|$id|".time."\n";
close ($TEMP);
}
elsif ($action eq "del") {
my $status = &remove($ip,$mode);
print "csf - $status ($user)\n";
}
elsif ($action eq "add") {
my $id;
my $status;
if ($mode eq "block") {($id,$status) = &block($ip)}
if ($mode eq "challenge") {($id,$status) = &challenge($ip)}
if ($mode eq "whitelist") {($id,$status) = &whitelist($ip)}
print "csf - $status ($user)\n";
}
elsif ($action eq "getlist") {
push @list, &getlist($user);
}
}
if ($action eq "getlist") {return @list}
}
return;
}
# end action
###############################################################################
# start block
sub block {
my $ip = shift;
my $target = &checktarget($ip);
my $block->{mode} = $config{CF_BLOCK};
$block->{configuration}->{target} = $target;
$block->{configuration}->{value} = $ip;
$block->{notes} = "csf $config{CF_BLOCK}";
my $content;
eval {
local $SIG{__DIE__} = undef;
$content = JSON::Tiny::encode_json($block);
};
my $ua = LWP::UserAgent->new;
my $res = $ua->post('https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules', %args, Content => $content);
if ($res->is_success) {
my $id = JSON::Tiny::decode_json($res->content);
return $id->{result}->{id},"CloudFlare: $config{CF_BLOCK} $target $ip";
} else {
if ($config{DEBUG} == 1) {print "Debug: ".$res->content."\n"}
elsif ($config{DEBUG} >= 2) {
eval {
local $SIG{__DIE__} = undef;
print Dumper(JSON::Tiny::decode_json($res->content));
};
}
return "CloudFlare: [$ip] $config{CF_BLOCK} failed: ".$res->status_line;
}
}
# end block
###############################################################################
# start whitelist
sub whitelist {
my $ip = shift;
my $target = &checktarget($ip);
my $whitelist->{mode} = "whitelist";
$whitelist->{configuration}->{target} = $target;
$whitelist->{configuration}->{value} = $ip;
$whitelist->{notes} = "csf whitelist";
my $content;
eval {
local $SIG{__DIE__} = undef;
$content = JSON::Tiny::encode_json($whitelist);
};
my $ua = LWP::UserAgent->new;
my $res = $ua->post('https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules', %args, Content => $content);
if ($res->is_success) {
my $id = JSON::Tiny::decode_json($res->content);
return $id->{result}->{id}, "CloudFlare: whitelisted $target $ip";
} else {
if ($config{DEBUG} == 1) {print "Debug: ".$res->content."\n"}
elsif ($config{DEBUG} >= 2) {
eval {
local $SIG{__DIE__} = undef;
print Dumper(JSON::Tiny::decode_json($res->content));
};
}
return "CloudFlare: [$ip] whitelist failed: ".$res->status_line;
}
}
# end whitelist
###############################################################################
# start challenge
sub challenge {
my $ip = shift;
my $target = &checktarget($ip);
my $challenge->{mode} = "challenge";
$challenge->{configuration}->{target} = $target;
$challenge->{configuration}->{value} = $ip;
$challenge->{notes} = "csf challenge";
my $content;
eval {
local $SIG{__DIE__} = undef;
$content = JSON::Tiny::encode_json($challenge);
};
my $ua = LWP::UserAgent->new;
my $res = $ua->post('https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules', %args, Content => $content);
if ($res->is_success) {
my $id = JSON::Tiny::decode_json($res->content);
return $id->{result}->{id}, "CloudFlare: challenged $target $ip";
} else {
if ($config{DEBUG} == 1) {print "Debug: ".$res->content."\n"}
elsif ($config{DEBUG} >= 2) {
eval {
local $SIG{__DIE__} = undef;
print Dumper(JSON::Tiny::decode_json($res->content));
};
}
return "CloudFlare: [$ip] challenge failed: ".$res->status_line;
}
}
# end challenge
###############################################################################
# start add
sub add {
my $ip = shift;
my $mode = shift;
my $target = &checktarget($ip);
my $add->{mode} = $mode;
$add->{configuration}->{target} = $target;
$add->{configuration}->{value} = $ip;
my $content;
eval {
local $SIG{__DIE__} = undef;
$content = JSON::Tiny::encode_json($add);
};
my $ua = LWP::UserAgent->new;
my $res = $ua->post('https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules', %args, Content => $content);
if ($res->is_success) {
my $id = JSON::Tiny::decode_json($res->content);
return $id->{result}->{id}, "CloudFlare: $mode added $target $ip";
} else {
if ($config{DEBUG} == 1) {print "Debug: ".$res->content."\n"}
elsif ($config{DEBUG} >= 2) {
eval {
local $SIG{__DIE__} = undef;
print Dumper(JSON::Tiny::decode_json($res->content));
};
}
return "CloudFlare: [$ip] $mode failed: ".$res->status_line;
}
}
# end whitelist
###############################################################################
# start remove
sub remove {
my $ip = shift;
my $mode = shift;
my $id = shift;
my $target = &checktarget($ip);
if ($id eq "") {
$id = getid($ip,$mode);
if ($id =~ /CloudFlare:/) {return $id}
if ($id eq "") {return "CloudFlare: [$ip] remove failed: id not found"}
}
my $ua = LWP::UserAgent->new;
my $res = $ua->delete('https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/'.$id, %args);
if ($res->is_success) {
return "CloudFlare: removed $target $ip";
} else {
if ($config{DEBUG} == 1) {print "Debug: ".$res->content."\n"}
elsif ($config{DEBUG} >= 2) {
eval {
local $SIG{__DIE__} = undef;
print Dumper(JSON::Tiny::decode_json($res->content));
};
}
return "CloudFlare: [$ip] [$id] remove failed: ".$res->status_line;
}
}
# end remove
###############################################################################
# start getid
sub getid {
my $ip = shift;
my $mode = shift;
my $target = &checktarget($ip);
my $ua = LWP::UserAgent->new;
my $res = $ua->get('https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?page=1&per_page=100&configuration.target='.$target.'&configuration.value='.$ip.'&match=all&order=mode&direction=desc', %args);
if ($res->is_success) {
my $result = JSON::Tiny::decode_json($res->content);
my $entry = @{$result->{result}}[0];
return $entry->{id};
} else {
if ($config{DEBUG} == 1) {print "Debug: ".$res->content."\n"}
elsif ($config{DEBUG} >= 2) {
eval {
local $SIG{__DIE__} = undef;
print Dumper(JSON::Tiny::decode_json($res->content));
};
}
return "CloudFlare: [$ip] id [$mode] failed: ".$res->status_line;
}
}
# end getid
###############################################################################
# start getlist
sub getlist {
my $domain = shift;
my %ips;
my $page = 1;
my $pages = 1;
my $result;
my $ua = LWP::UserAgent->new;
while (1) {
my $res = $ua->get('https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?page='.$page.'&per_page=100&order=created_on&direction=asc&match=all', %args);
if ($res->is_success) {
my $result = JSON::Tiny::decode_json($res->content);
$pages = $result->{result_info}->{total_pages};
foreach my $entry (@{$result->{result}}) {
if ($entry->{configuration}->{target} eq "ip" or $entry->{configuration}->{target} eq "country" or $entry->{configuration}->{target} eq "ip_range") {
my ($date, $time) = split /T/ => $entry->{created_on};
my ($year, $mon, $mday) = split /-/ => $date;
$year -= 1900;
$mon -= 1;
my ($hour, $min, $sec) = split /:/ => $time;
my $timelocal = Time::Local::timelocal($sec, $min, $hour, $mday, $mon, $year);
$ips{$entry->{configuration}->{value}}{notes} = $entry->{notes};
$ips{$entry->{configuration}->{value}}{mode} = $entry->{mode};
$ips{$entry->{configuration}->{value}}{created_on} = $timelocal;
$ips{$entry->{configuration}->{value}}{domain} = $domain;
$ips{$entry->{configuration}->{value}}{success} = 1;
}
}
} else {
if ($config{DEBUG} >= 2) {
eval {
local $SIG{__DIE__} = undef;
print Dumper(JSON::Tiny::decode_json($res->content));
};
}
$ips{$domain}{success} = 0;
$ips{$domain}{domain} = "CloudFlare: list failed for ($domain): ".$res->status_line;
return \%ips;
}
$page++;
if ($pages < $page) {last}
}
return \%ips;
}
# end getlist
###############################################################################
# start getscope
sub getscope {
my %scope;
my %disabled;
my %any;
my @entries = slurp("/etc/csf/csf.cloudflare");
foreach my $line (@entries) {
if ($line =~ /^Include\s*(.*)$/) {
my @incfile = slurp($1);
push @entries,@incfile;
}
}
foreach my $line (@entries) {
$line =~ s/$cleanreg//g;
if ($line eq "") {next}
if ($line =~ /^\s*\#|Include/) {next}
my @setting = split(/\:/,$line);
if ($setting[0] eq "DOMAIN") {
my $domain = $setting[1];
my $user = $setting[3];
my $account = $setting[5];
my $apikey = $setting[7];
$scope{domain}{$domain}{account} = $account;
$scope{domain}{$domain}{apikey} = $apikey;
$scope{domain}{$domain}{user} = $user;
$scope{user}{$user}{account} = $account;
$scope{user}{$user}{apikey} = $apikey;
$scope{user}{$user}{domain}{$domain} = $domain;
if ($domain eq "any") {$scope{user}{$user}{any} = 1}
}
if ($setting[0] eq "DISABLE") {
$disabled{$setting[1]} = 1;
}
if ($setting[0] eq "ANY") {
$any{$setting[1]} = 1;
}
}
if ($config{CF_CPANEL}) {
my %userdomains;
my %accounts;
my %creds;
open (my $IN, "<","/etc/userdomains");
flock ($IN, LOCK_SH);
my @localusers = <$IN>;
close ($IN);
chomp @localusers;
foreach my $line (@localusers) {
my ($domain,$user) = split(/\:\s*/,$line,2);
$userdomains{$domain} = $user;
$accounts{$user} = 1;
}
foreach my $user (keys %accounts) {
if ($disabled{$user}) {next}
my $userhome = (getpwnam($user))[7];
if (-e "$userhome/.cpanel/datastore/cloudflare_data.yaml") {
my $yaml = YAML::Tiny->read("$userhome/.cpanel/datastore/cloudflare_data.yaml");
if ($yaml->[0]->{client_api_key} ne "") {
$creds{$user}{account} = $yaml->[0]->{cloudflare_email};
$creds{$user}{apikey} = $yaml->[0]->{client_api_key};
}
}
}
foreach my $domain (keys %userdomains) {
my $user = $userdomains{$domain};
if ($disabled{$user}) {next}
if ($creds{$user}{apikey} ne "") {
$scope{domain}{$domain}{account} = $creds{$user}{account};
$scope{domain}{$domain}{apikey} = $creds{$user}{apikey};
$scope{domain}{$domain}{user} = $user;
$scope{user}{$user}{account} = $creds{$user}{account};
$scope{user}{$user}{apikey} = $creds{$user}{apikey};
$scope{user}{$user}{domain}{$domain} = $domain;
if ($any{$user}) {
$scope{domain}{any}{account} = $creds{$user}{account};
$scope{domain}{any}{apikey} = $creds{$user}{apikey};
$scope{domain}{any}{user} = $user;
$scope{user}{$user}{domain}{any} = "any";
$scope{user}{$user}{any} = 1;
}
}
}
}
return \%scope;
}
# end getscope
###############################################################################
# start checktarget
sub checktarget {
my $arg = shift;
if ($arg =~ /^\w\w$/) {return "country"}
elsif ($arg =~ /\/16$/) {return "ip_range"}
elsif ($arg =~ /\/24$/) {return "ip_range"}
else {return "ip"}
}
# end checktarget
###############################################################################
1;

457
csf/ConfigServer/Config.pm Normal file
View File

@@ -0,0 +1,457 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::Config;
use strict;
use lib '/usr/local/csf/lib';
use version;
use Fcntl qw(:DEFAULT :flock);
use Carp;
use IPC::Open3;
use ConfigServer::Slurp qw(slurp);
use Exporter qw(import);
our $VERSION = 1.05;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw();
our $ipv4reg = qr/(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/;
our $ipv6reg = qr/((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?/;
my %config;
my %configsetting;
my $warning;
my $version;
my $slurpreg = ConfigServer::Slurp->slurpreg;
my $cleanreg = ConfigServer::Slurp->cleanreg;
my $configfile = "/etc/csf/csf.conf";
# end main
###############################################################################
# start loadconfig
sub loadconfig {
my $class = shift;
my $self = {};
bless $self,$class;
if (%config) {
$self->{warning} = $warning;
return $self;
}
undef %configsetting;
undef %config;
undef $warning;
my @file = slurp($configfile);
foreach my $line (@file) {
$line =~ s/$cleanreg//g;
if ($line =~ /^(\s|\#|$)/) {next}
if ($line !~ /=/) {next}
my ($name,$value) = split (/=/,$line,2);
$name =~ s/\s//g;
if ($value =~ /\"(.*)\"/) {
$value = $1;
} else {
croak "*Error* Invalid configuration line [$line] in $configfile";
}
if ($configsetting{$name}) {
croak "*Error* Setting $name is repeated in $configfile - you must remove the duplicates and then restart csf and lfd";
}
$config{$name} = $value;
$configsetting{$name} = 1;
}
if ($config{LF_IPSET}) {
unless ($config{LF_IPSET_HASHSIZE}) {
$config{LF_IPSET_HASHSIZE} = "1024";
$configsetting{LF_IPSET_HASHSIZE} = 1;
}
unless ($config{LF_IPSET_MAXELEM}) {
$config{LF_IPSET_MAXELEM} = "65536";
$configsetting{LF_IPSET_MAXELEM} = 1;
}
}
if ($config{USE_FTPHELPER} eq "1") {
$warning .= "USE_FTPHELPER should be set to your FTP server port (21), not 1. USE_FTPHELPER has been disabled\n";
$config{USE_FTPHELPER} = 0;
}
if ($config{IPTABLES} eq "" or !(-x $config{IPTABLES})) {
croak "*Error* The path to iptables is either not set or incorrect for IPTABLES [$config{IPTABLES}] in /etc/csf/csf.conf";
}
if (-e "/proc/sys/net/netfilter/nf_conntrack_helper" and !$config{USE_FTPHELPER}) {
my $setting = slurp("/proc/sys/net/netfilter/nf_conntrack_helper");
chomp $setting;
if ($setting == 0) {
open (my $PROC, ">", "/proc/sys/net/netfilter/nf_conntrack_helper");
print $PROC "1\n";
close $PROC;
}
}
if ($config{WAITLOCK}) {$config{IPTABLESWAIT} = "--wait";}
my @results = &systemcmd("$config{IPTABLES} $config{IPTABLESWAIT} --version");
if ($results[0] =~ /iptables v(\d+\.\d+\.\d+)/) {
$version = $1;
$config{IPTABLESWAIT} = "";
if ($config{WAITLOCK}) {
my @ipdata;
eval {
local $SIG{__DIE__} = undef;
local $SIG{'ALRM'} = sub {die "alarm\n"};
alarm($config{WAITLOCK_TIMEOUT});
my ($childin, $childout);
my $cmdpid = open3($childin, $childout, $childout, "$config{IPTABLES} --wait -L OUTPUT -nv");
@ipdata = <$childout>;
waitpid ($cmdpid, 0);
chomp @ipdata;
if ($ipdata[0] =~ /# Warning: iptables-legacy tables present/) {shift @ipdata}
alarm(0);
};
alarm(0);
if ($@ eq "alarm\n") {
croak "*ERROR* Timeout after $config{WAITLOCK_TIMEOUT} seconds for iptables --wait - WAITLOCK\n";
}
if ($ipdata[0] =~ /^Chain OUTPUT/) {
$config{IPTABLESWAIT} = "--wait";
} else {
$warning .= "*WARNING* This version of iptables does not support the --wait option - disabling WAITLOCK\n";
$config{WAITLOCK} = 0;
}
}
} else {
$warning .= "*WARNING* Unable to detect iptables version [$results[0]]\n";
}
if ($config{CC_LOOKUPS} and $config{CC_LOOKUPS} != 4 and $config{CC_SRC} eq "1") {
if ($config{MM_LICENSE_KEY} eq "") {
$warning .= "*ERROR*: Country Code Lookups setting MM_LICENSE_KEY must be set in /etc/csf/csf.conf to continue using the MaxMind databases\n";
}
}
foreach my $cclist ("CC_DENY","CC_ALLOW","CC_ALLOW_FILTER","CC_ALLOW_PORTS","CC_DENY_PORTS","CC_ALLOW_SMTPAUTH") {
$config{$cclist} =~ s/\s//g;
my $newcclist;
foreach my $cc (split(/\,/,$config{$cclist})) {
if ($cc ne "" and ((length($cc) == 2 and $cc =~ /^[a-zA-Z][a-zA-Z]$/i) or (length($cc) > 2 and $cc =~ /^AS\d+$/i))) {
$cc = lc $cc;
if ($newcclist eq "") {$newcclist = "$cc"} else {$newcclist .= ",$cc"}
} else {
$warning .= "*WARNING* $cclist contains an invalid entry [$cc]\n";
}
}
$config{$cclist} = $newcclist;
}
if ($config{CC_DENY} or $config{CC_ALLOW} or $config{CC_ALLOW_FILTER} or $config{CC_ALLOW_PORTS} or $config{CC_DENY_PORTS} or $config{CC_ALLOW_SMTPAUTH}) {
if ($config{MM_LICENSE_KEY} eq "" and $config{CC_SRC} eq "1") {
$warning .= "*ERROR*: Country Code Filters setting MM_LICENSE_KEY must be set in /etc/csf/csf.conf to continue updating the MaxMind databases\n";
}
}
if ($config{DROP_OUT} ne "DROP") {
my @data = &systemcmd("$config{IPTABLES} $config{IPTABLESWAIT} -N TESTDENY");
unless ($data[0] =~ /^iptables/) {
my @ipdata = &systemcmd("$config{IPTABLES} $config{IPTABLESWAIT} -I TESTDENY -j $config{DROP_OUT}");
if ($ipdata[0] =~ /^iptables/) {
$warning .= "*WARNING* Cannot use DROP_OUT value of [$config{DROP_OUT}] on this server, set to DROP\n";
$config{DROP_OUT} = "DROP";
}
&systemcmd("$config{IPTABLES} $config{IPTABLESWAIT} -F TESTDENY");
&systemcmd("$config{IPTABLES} $config{IPTABLESWAIT} -X TESTDENY");
}
}
my @raw = &systemcmd("$config{IPTABLES} $config{IPTABLESWAIT} -L PREROUTING -t raw");
if ($raw[0] =~ /^Chain PREROUTING/) {$config{RAW} = 1} else {$config{RAW} = 0}
my @mangle = &systemcmd("$config{IPTABLES} $config{IPTABLESWAIT} -L PREROUTING -t mangle");
if ($mangle[0] =~ /^Chain PREROUTING/) {$config{MANGLE} = 1} else {$config{MANGLE} = 0}
if ($config{IPV6} and -x $config{IP6TABLES} and $version) {
if ($config{USE_CONNTRACK} and version->parse($version) <= version->parse("1.3.5")) {$config{USE_CONNTRACK} = 0}
if ($config{PORTFLOOD} and version->parse($version) >= version->parse("1.4.3")) {$config{PORTFLOOD6} = 1}
if ($config{CONNLIMIT} and version->parse($version) >= version->parse("1.4.3")) {$config{CONNLIMIT6} = 1}
if ($config{MESSENGER} and version->parse($version) >= version->parse("1.4.17")) {$config{MESSENGER6} = 1}
if ($config{SMTP_REDIRECT} and version->parse($version) >= version->parse("1.4.17")) {$config{SMTP_REDIRECT6} = 1}
my @ipdata = &systemcmd("$config{IP6TABLES} $config{IPTABLESWAIT} -t nat -L POSTROUTING -nv");
if ($ipdata[0] =~ /^Chain POSTROUTING/) {
$config{NAT6} = 1;
}
elsif (version->parse($version) >= version->parse("1.4.17")) {
if ($config{SMTP_REDIRECT}) {
$warning .= "*WARNING* ip6tables nat table not present - disabling SMTP_REDIRECT for IPv6\n";
$config{SMTP_REDIRECT6} = 0;
}
if ($config{MESSENGER}) {
$warning .= "*WARNING* ip6tables nat table not present - disabling MESSENGER Service for IPv6\n";
$config{MESSENGER6} = 0;
}
if ($config{DOCKER} and $config{DOCKER_NETWORK6} ne "") {
$warning .= "*WARNING* ip6tables nat table not present - disabling DOCKER for IPv6\n";
}
}
my @raw = &systemcmd("$config{IP6TABLES} $config{IPTABLESWAIT} -L PREROUTING -t raw");
if ($raw[0] =~ /^Chain PREROUTING/) {$config{RAW6} = 1} else {$config{RAW6} = 0}
my @mangle = &systemcmd("$config{IP6TABLES} $config{IPTABLESWAIT} -L PREROUTING -t mangle");
if ($mangle[0] =~ /^Chain PREROUTING/) {$config{MANGLE6} = 1} else {$config{MANGLE6} = 0}
}
elsif ($config{IPV6}) {
$warning .= "*WARNING* incorrect ip6tables binary location [$config{IP6TABLES}] - IPV6 disabled\n";
$config{IPV6} = 0;
}
if (!$config{GENERIC} and -e "/var/cpanel/dnsonly") {$config{DNSONLY} = 1}
if (-e "/var/cpanel/smtpgidonlytweak" and !$config{GENERIC}) {
if ($config{DNSONLY}) {
$warning .= "*WARNING* The cPanel option to 'Restrict outgoing SMTP to root, exim, and mailman' is incompatible with this firewall. The option must be disabled using \"/usr/local/cpanel/scripts/smtpmailgidonly off\" and the SMTP_BLOCK alternative in csf used instead\n";
} else {
$warning .= "*WARNING* The option \"WHM > Tweak Settings > Restrict outgoing SMTP to root, exim, and mailman (FKA SMTP Tweak)\" is incompatible with this firewall. The option must be disabled in WHM and the SMTP_BLOCK alternative in csf used instead\n";
}
}
if (-e "/proc/vz/veinfo") {$config{VPS} = 1}
else {
foreach my $line (slurp("/proc/self/status")) {
$line =~ s/$cleanreg//g;
if ($line =~ /^envID:\s*(\d+)\s*$/) {
if ($1 > 0) {
$config{VPS} = 1;
last;
}
}
}
}
if ($config{DROP_IP_LOGGING} and $config{PS_INTERVAL}) {
$warning .= "*WARNING* Cannot use PS_INTERVAL with DROP_IP_LOGGING enabled. DROP_IP_LOGGING disabled\n";
$config{DROP_IP_LOGGING} = 0;
}
if ($config{FASTSTART}) {
unless (-x $config{IPTABLES_RESTORE}) {
$warning .= "*WARNING* Unable to use FASTSTART as [$config{IPTABLES_RESTORE}] is not executable or does not exist\n";
$config{FASTSTART} = 0;
}
if ($config{IPV6}) {
unless (-x $config{IP6TABLES_RESTORE}) {
$warning .= "*WARNING* Unable to use FASTSTART as (IPv6) [$config{IP6TABLES_RESTORE}] is not executable or does not exist\n";
$config{FASTSTART} = 0;
}
}
}
if ($config{MESSENGER}) {
if ($config{MESSENGERV2}) {
if (!-e "/etc/cpanel/ea4/is_ea4") {
$warning .= "*WARNING* EA4 is not in use - disabling MESSENGERV2 and MESSENGER HTTPS Service\n";
$config{MESSENGERV2} = "0";
$config{MESSENGER_HTTPS_IN} = "";
$config{MESSENGER_HTTPS_DISABLED} = "*WARNING* EA4 is not in use - disabling MESSENGERV2 and MESSENGER HTTPS Service";
}
}
if ($config{MESSENGER_HTTPS_IN} and (!$config{MESSENGERV2} or $config{MESSENGER_HTTPS_DISABLED})) {
eval {
local $SIG{__DIE__} = undef;
require IO::Socket::SSL;
};
if ($@) {
$warning .= "*WARNING* Perl module IO::Socket::SSL missing - disabling MESSENGER HTTPS Service\n";
$config{MESSENGER_HTTPS_IN} = "";
$config{MESSENGER_HTTPS_DISABLED} = "*WARNING* Perl module IO::Socket::SSL missing - disabling MESSENGER HTTPS Service";
}
elsif (version->parse($IO::Socket::SSL::VERSION) < version->parse("1.83")) {
$warning .= "*WARNING* Perl module IO::Socket::SSL v$IO::Socket::SSL::VERSION does not support SNI - disabling MESSENGER HTTPS Service\n";
$config{MESSENGER_HTTPS_IN} = "";
$config{MESSENGER_HTTPS_DISABLED} = "*WARNING* Perl module IO::Socket::SSL v$IO::Socket::SSL::VERSION does not support SNI - disabling MESSENGER HTTPS Service";
}
}
my $pcnt = 0;
foreach my $port (split(/\,/,$config{MESSENGER_HTML_IN})) {
$pcnt++;
}
if ($pcnt > 15) {
$warning .= "*WARNING* MESSENGER_HTML_IN contains more than 15 ports - disabling MESSENGER Service\n";
$config{MESSENGER} = 0;
} else {
$pcnt = 0;
foreach my $port (split(/\,/,$config{MESSENGER_TEXT_IN})) {
$pcnt++;
}
if ($pcnt > 15) {
$warning .= "*WARNING* MESSENGER_TEXT_IN contains more than 15 ports - disabling MESSENGER Service\n";
$config{MESSENGER} = 0;
} else {
$pcnt = 0;
foreach my $port (split(/\,/,$config{MESSENGER_HTTPS_IN})) {
$pcnt++;
}
if ($pcnt > 15) {
$warning .= "*WARNING* MESSENGER_HTTPS_IN contains more than 15 ports - disabling MESSENGER Service\n";
$config{MESSENGER} = 0;
}
}
}
}
if ($config{IPV6} and $config{IPV6_SPI}) {
open (my $FH, "<", "/proc/sys/kernel/osrelease");
flock ($FH, LOCK_SH);
my @data = <$FH>;
close ($FH);
chomp @data;
if ($data[0] =~ /^(\d+)\.(\d+)\.(\d+)/) {
my $maj = $1;
my $mid = $2;
my $min = $3;
if (($maj > 2) or (($maj > 1) and ($mid > 6)) or (($maj > 1) and ($mid > 5) and ($min > 19))) {
} else {
$warning .= "*WARNING* Kernel $data[0] may not support an ip6tables SPI firewall. You should set IPV6_SPI to \"0\" in /etc/csf/csf.conf\n\n";
}
}
}
if (($config{CLUSTER_SENDTO} or $config{CLUSTER_RECVFROM})) {
if (-f $config{CLUSTER_SENDTO}) {
if ($config{DEBUG} >= 1) {$warning .= "*DEBUG* CLUSTER_SENDTO retrieved from $config{CLUSTER_SENDTO} and set to: "}
$config{CLUSTER_SENDTO} = join(",", slurp($config{CLUSTER_SENDTO}));
if ($config{DEBUG} >= 1) {$warning .= "[$config{CLUSTER_SENDTO}]\n"}
}
if (-f $config{CLUSTER_RECVFROM}) {
if ($config{DEBUG} >= 1) {$warning .= "*DEBUG* CLUSTER_RECVFROM retrieved from $config{CLUSTER_RECVFROM} and set to: "}
$config{CLUSTER_RECVFROM} = join(",", slurp($config{CLUSTER_RECVFROM}));
if ($config{DEBUG} >= 1) {$warning .= "[$config{CLUSTER_RECVFROM}]\n"}
}
}
my @ipdata = &systemcmd("$config{IPTABLES} $config{IPTABLESWAIT} -t nat -L POSTROUTING -nv");
if ($ipdata[0] =~ /^Chain POSTROUTING/) {
$config{NAT} = 1;
} else {
if ($config{MESSENGER}) {
$warning .= "*WARNING* iptables nat table not present - disabling MESSENGER Service\n";
$config{MESSENGER} = 0;
}
}
if ($config{PT_USERKILL}) {
$warning .= "*WARNING* PT_USERKILL should not normally be enabled as it can easily lead to legitimate processes being terminated, use csf.pignore instead\n";
}
$config{cc_src} = "MaxMind";
$config{asn_src} = "MaxMind";
$config{cc_country} = "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country-CSV&suffix=zip&license_key=$config{MM_LICENSE_KEY}";
$config{cc_city} = "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City-CSV&suffix=zip&license_key=$config{MM_LICENSE_KEY}";
$config{cc_asn} = "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN-CSV&suffix=zip&license_key=$config{MM_LICENSE_KEY}";
if ($config{CC_SRC} eq "2") {
$config{cc_src} = "DB-IP";
$config{asn_src} = "iptoasn.com";
$config{ccl_src} = "ipdeny.com";
my ($month,$year) = sub{ 1+shift, 1900+shift }->((localtime time)[4,5]);
$month = sprintf("%02d", $month);
$config{cc_country} = "http://download.db-ip.com/free/dbip-country-lite-$year-$month.csv.gz";
$config{cc_city} = "http://download.db-ip.com/free/dbip-city-lite-$year-$month.csv.gz";
$config{cc_asn} = "http://iptoasn.com/data/ip2asn-combined.tsv.gz";
$config{cc_cc} = "http://download.geonames.org/export/dump/countryInfo.txt";
}
$config{DOWNLOADSERVER} = &getdownloadserver;
$self->{warning} = $warning;
return $self;
}
# end loadconfig
###############################################################################
# start config
sub config {
return %config;
}
# end config
###############################################################################
# start resetconfig
sub resetconfig {
undef %config;
undef %configsetting;
undef $warning;
return;
}
# end resetconfig
###############################################################################
# start configsetting
sub configsetting {
return %configsetting;
}
# end configsetting
###############################################################################
# start ipv4reg
sub ipv4reg {
return $ipv4reg;
}
# end ipv4reg
###############################################################################
# start ipv6reg
sub ipv6reg {
return $ipv6reg;
}
# end ipv6reg
###############################################################################
# start systemcmd
sub systemcmd {
my @command = @_;
my @result;
eval {
my ($childin, $childout);
my $pid = open3($childin, $childout, $childout, @command);
@result = <$childout>;
waitpid ($pid, 0);
chomp @result;
if ($result[0] =~ /# Warning: iptables-legacy tables present/) {shift @result}
};
return @result;
}
# end systemcmd
###############################################################################
## start getdownloadserver
sub getdownloadserver {
my @servers;
my $downloadservers = "/etc/csf/downloadservers";
my $chosen;
if (-e $downloadservers) {
foreach my $line (slurp($downloadservers)) {
$line =~ s/$cleanreg//g;
if ($line =~ /^download/) {push @servers, $line}
}
$chosen = $servers[rand @servers];
}
## if ($chosen eq "") {$chosen = "download.configserver.com"}
return $chosen;
}
## end getdownloadserver
###############################################################################
1;

View File

@@ -0,0 +1,241 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
package ConfigServer::DisplayResellerUI;
use strict;
use lib '/usr/local/csf/lib';
use Fcntl qw(:DEFAULT :flock);
use POSIX qw(:sys_wait_h sysconf strftime);
use File::Basename;
use Net::CIDR::Lite;
use IPC::Open3;
use ConfigServer::Config;
use ConfigServer::CheckIP qw(checkip);
use ConfigServer::Sendmail;
use ConfigServer::Logger;
use Exporter qw(import);
our $VERSION = 1.01;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw();
umask(0177);
our ($chart, $ipscidr6, $ipv6reg, $ipv4reg, %config, %ips, $mobile,
%FORM, $script, $script_da, $images, $myv, %rprivs, $hostname,
$hostshort, $tz, $panel);
#
###############################################################################
# start main
sub main {
my $form_ref = shift;
%FORM = %{$form_ref};
$script = shift;
$script_da = shift;
$images = shift;
$myv = shift;
open (my $IN,"<","/etc/csf/csf.resellers");
flock ($IN, LOCK_SH);
while (my $line = <$IN>) {
my ($user,$alert,$privs) = split(/\:/,$line);
$privs =~ s/\s//g;
foreach my $priv (split(/\,/,$privs)) {
$rprivs{$user}{$priv} = 1;
}
$rprivs{$user}{ALERT} = $alert;
}
close ($IN);
open (my $HOSTNAME, "<","/proc/sys/kernel/hostname");
flock ($HOSTNAME, LOCK_SH);
$hostname = <$HOSTNAME>;
chomp $hostname;
close ($HOSTNAME);
$hostshort = (split(/\./,$hostname))[0];
$tz = strftime("%z", localtime);
my $config = ConfigServer::Config->loadconfig();
%config = $config->config();
$panel = "cPanel";
if ($config{GENERIC}) {$panel = "Generic"}
if ($config{INTERWORX}) {$panel = "InterWorx"}
if ($config{DIRECTADMIN}) {$panel = "DirectAdmin"}
if ($FORM{ip} ne "") {$FORM{ip} =~ s/(^\s+)|(\s+$)//g}
if ($FORM{action} ne "" and !checkip(\$FORM{ip})) {
print "<table class='table table-bordered table-striped'>\n";
print "<tr><td>";
print "[$FORM{ip}] is not a valid IP address\n";
print "</td></tr></table>\n";
print "<p><form action='$script' method='post'><input type='submit' class='btn btn-default' value='Return'></form></p>\n";
} else {
if ($FORM{action} eq "qallow" and $rprivs{$ENV{REMOTE_USER}}{ALLOW}) {
if ($FORM{comment} eq "") {
print "<table class='table table-bordered table-striped'>\n";
print "<tr><td>You must provide a Comment for this option</td></tr></table>\n";
} else {
$FORM{comment} =~ s/"//g;
print "<table class='table table-bordered table-striped'>\n";
print "<tr><td>";
print "<p>Allowing $FORM{ip}...</p>\n<p><pre style='font-family: Courier New, Courier; font-size: 12px'>\n";
my $text = &printcmd("/usr/sbin/csf","-a",$FORM{ip},"ALLOW by Reseller $ENV{REMOTE_USER} ($FORM{comment})");
print "</p>\n<p>...<b>Done</b>.</p>\n";
print "</td></tr></table>\n";
if ($rprivs{$ENV{REMOTE_USER}}{ALERT}) {
open (my $IN, "<", "/usr/local/csf/tpl/reselleralert.txt");
flock ($IN, LOCK_SH);
my @alert = <$IN>;
close ($IN);
chomp @alert;
my @message;
foreach my $line (@alert) {
$line =~ s/\[reseller\]/$ENV{REMOTE_USER}/ig;
$line =~ s/\[action\]/ALLOW/ig;
$line =~ s/\[ip\]/$FORM{ip}/ig;
$line =~ s/\[rip\]/$ENV{REMOTE_HOST}/ig;
$line =~ s/\[text\]/Result of ALLOW:\n\n$text/ig;
push @message, $line;
}
ConfigServer::Sendmail::relay("", "", @message);
}
ConfigServer::Logger::logfile("$panel Reseller [$ENV{REMOTE_USER}]: ALLOW $FORM{ip}");
}
print "<p><form action='$script' method='post'><input type='hidden' name='mobi' value='$FORM{mobi}'><input type='submit' class='btn btn-default' value='Return'></form></p>\n";
}
elsif ($FORM{action} eq "qdeny" and $rprivs{$ENV{REMOTE_USER}}{DENY}) {
if ($FORM{comment} eq "") {
print "<table class='table table-bordered table-striped'>\n";
print "<tr><td>You must provide a Comment for this option</td></tr></table>\n";
} else {
$FORM{comment} =~ s/"//g;
print "<table class='table table-bordered table-striped'>\n";
print "<tr><td>";
print "<p>Blocking $FORM{ip}...</p>\n<p><pre style='font-family: Courier New, Courier; font-size: 12px'>\n";
my $text = &printcmd("/usr/sbin/csf","-d",$FORM{ip},"DENY by Reseller $ENV{REMOTE_USER} ($FORM{comment})");
print "</p>\n<p>...<b>Done</b>.</p>\n";
print "</td></tr></table>\n";
if ($rprivs{$ENV{REMOTE_USER}}{ALERT}) {
open (my $IN, "<", "/usr/local/csf/tpl/reselleralert.txt");
flock ($IN, LOCK_SH);
my @alert = <$IN>;
close ($IN);
chomp @alert;
my @message;
foreach my $line (@alert) {
$line =~ s/\[reseller\]/$ENV{REMOTE_USER}/ig;
$line =~ s/\[action\]/DENY/ig;
$line =~ s/\[ip\]/$FORM{ip}/ig;
$line =~ s/\[rip\]/$ENV{REMOTE_HOST}/ig;
$line =~ s/\[text\]/Result of DENY:\n\n$text/ig;
push @message, $line;
}
ConfigServer::Sendmail::relay("", "", @message);
}
ConfigServer::Logger::logfile("$panel Reseller [$ENV{REMOTE_USER}]: DENY $FORM{ip}");
}
print "<p><form action='$script' method='post'><input type='hidden' name='mobi' value='$FORM{mobi}'><input type='submit' class='btn btn-default' value='Return'></form></p>\n";
}
elsif ($FORM{action} eq "qkill" and $rprivs{$ENV{REMOTE_USER}}{UNBLOCK}) {
my $text = "";
if ($rprivs{$ENV{REMOTE_USER}}{ALERT}) {
my ($childin, $childout);
my $pid = open3($childin, $childout, $childout, "/usr/sbin/csf","-g",$FORM{ip});
while (<$childout>) {$text .= $_}
waitpid ($pid, 0);
}
print "<table class='table table-bordered table-striped'>\n";
print "<tr><td>";
print "<p>Unblock $FORM{ip}, trying permanent blocks...</p>\n<p><pre style='font-family: Courier New, Courier; font-size: 12px'>\n";
my $text1 = &printcmd("/usr/sbin/csf","-dr",$FORM{ip});
print "</p>\n<p>...<b>Done</b>.</p>\n";
print "<p>Unblock $FORM{ip}, trying temporary blocks...</p>\n<p><pre style='font-family: Courier New, Courier; font-size: 12px'>\n";
my $text2 = &printcmd("/usr/sbin/csf","-tr",$FORM{ip});
print "</p>\n<p>...<b>Done</b>.</p>\n";
print "</td></tr></table>\n";
print "<p><form action='$script' method='post'><input type='hidden' name='mobi' value='$FORM{mobi}'><input type='submit' class='btn btn-default' value='Return'></form></p>\n";
if ($rprivs{$ENV{REMOTE_USER}}{ALERT}) {
open (my $IN, "<", "/usr/local/csf/tpl/reselleralert.txt");
flock ($IN, LOCK_SH);
my @alert = <$IN>;
close ($IN);
chomp @alert;
my @message;
foreach my $line (@alert) {
$line =~ s/\[reseller\]/$ENV{REMOTE_USER}/ig;
$line =~ s/\[action\]/UNBLOCK/ig;
$line =~ s/\[ip\]/$FORM{ip}/ig;
$line =~ s/\[rip\]/$ENV{REMOTE_HOST}/ig;
$line =~ s/\[text\]/Result of GREP before UNBLOCK:\n$text\n\nResult of UNBLOCK:\nPermanent:\n$text1\nTemporary:\n$text2\n/ig;
push @message, $line;
}
ConfigServer::Sendmail::relay("", "", @message);
}
ConfigServer::Logger::logfile("$panel Reseller [$ENV{REMOTE_USER}]: UNBLOCK $FORM{ip}");
}
elsif ($FORM{action} eq "grep" and $rprivs{$ENV{REMOTE_USER}}{GREP}) {
print "<table class='table table-bordered table-striped'>\n";
print "<tr><td>";
print "<p>Searching for $FORM{ip}...</p>\n<p><pre style='font-family: Courier New, Courier; font-size: 12px'>\n";
&printcmd("/usr/sbin/csf","-g",$FORM{ip});
print "</p>\n<p>...<b>Done</b>.</p>\n";
print "</td></tr></table>\n";
print "<p><form action='$script' method='post'><input type='submit' class='btn btn-default' value='Return'></form></p>\n";
}
else {
print "<table class='table table-bordered table-striped'>\n";
print "<thead><tr><th align='left' colspan='2'>csf - ConfigServer Firewall options for $ENV{REMOTE_USER}</th></tr></thead>";
if ($rprivs{$ENV{REMOTE_USER}}{ALLOW}) {print "<tr><td><form action='$script' method='post'><input type='hidden' name='action' value='qallow'><input type='submit' class='btn btn-default' value='Quick Allow'></td><td width='100%'>Allow IP address <input type='text' name='ip' id='allowip' value='' size='18' style='background-color: lightgreen'> through the firewall and add to the allow file (csf.allow).<br>Comment for Allow: <input type='text' name='comment' value='' size='30'> (required)</form></td></tr>\n"}
if ($rprivs{$ENV{REMOTE_USER}}{DENY}) {print "<tr><td><form action='$script' method='post'><input type='hidden' name='action' value='qdeny'><input type='submit' class='btn btn-default' value='Quick Deny'></td><td width='100%'>Block IP address <input type='text' name='ip' value='' size='18' style='background-color: pink'> in the firewall and add to the deny file (csf.deny).<br>Comment for Block: <input type='text' name='comment' value='' size='30'> (required)</form></td></tr>\n"}
if ($rprivs{$ENV{REMOTE_USER}}{UNBLOCK}) {print "<tr><td><form action='$script' method='post'><input type='hidden' name='action' value='qkill'><input type='submit' class='btn btn-default' value='Quick Unblock'></td><td width='100%'>Remove IP address <input type='text' name='ip' value='' size='18'> from the firewall (temp and perm blocks)</form></td></tr>\n"}
if ($rprivs{$ENV{REMOTE_USER}}{GREP}) {print "<tr><td><form action='$script' method='post'><input type='hidden' name='action' value='grep'><input type='submit' class='btn btn-default' value='Search for IP'></td><td width='100%'>Search iptables for IP address <input type='text' name='ip' value='' size='18'></form></td></tr>\n"}
print "</table><br>\n";
}
}
print "<br>\n";
print "<pre>csf: v$myv</pre>";
print "<p>&copy;2006-2023, <a href='http://www.configserver.com' target='_blank'>ConfigServer Services</a> (Jonathan Michaelson)</p>\n";
return;
}
# end main
###############################################################################
# start printcmd
sub printcmd {
my @command = @_;
my $text;
my ($childin, $childout);
my $pid = open3($childin, $childout, $childout, @command);
while (<$childout>) {print $_ ; $text .= $_}
waitpid ($pid, 0);
return $text;
}
# end printcmd
###############################################################################
1;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::GetEthDev;
use strict;
use lib '/usr/local/csf/lib';
use Carp;
use Fcntl qw(:DEFAULT :flock);
use IPC::Open3;
use POSIX qw(locale_h);
use ConfigServer::Config;
use ConfigServer::CheckIP qw(checkip);
use ConfigServer::Logger;
use Exporter qw(import);
our $VERSION = 1.01;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw();
my (%ifaces, %ipv4, %ipv6, %brd);
# end main
###############################################################################
# start new
sub new {
my $class = shift;
my $self = {};
bless $self,$class;
my $status;
my $config = ConfigServer::Config->loadconfig();
my %config = $config->config();
my $ipv4reg = $config->ipv4reg;
my $ipv6reg = $config->ipv6reg;
$brd{"255.255.255.255"} = 1;
setlocale(LC_ALL, "POSIX");
if (-e $config{IP}) {
my ($childin, $childout);
my $pid = open3($childin, $childout, $childout, $config{IP}, "-oneline", "addr");
my @ifconfig = <$childout>;
waitpid ($pid, 0);
chomp @ifconfig;
foreach my $line (@ifconfig) {
if ($line =~ /^\d+:\s+([\w\.\-]+)/ ) {
$ifaces{$1} = 1;
}
if ($line =~ /inet.*?($ipv4reg)/) {
my ($ip,undef) = split(/\//,$1);
if (checkip(\$ip)) {
$ipv4{$ip} = 1;
}
}
if ($line =~ /brd\s+($ipv4reg)/) {
my ($ip,undef) = split(/\//,$1);
if (checkip(\$ip)) {
$brd{$ip} = 1;
}
}
if ($line =~ /inet6.*?($ipv6reg)/) {
my ($ip,undef) = split(/\//,$1);
$ip .= "/128";
if (checkip(\$ip)) {
$ipv6{$ip} = 1;
}
}
}
$status = 0;
}
elsif (-e $config{IFCONFIG}) {
my ($childin, $childout);
my $pid = open3($childin, $childout, $childout, $config{IFCONFIG});
my @ifconfig = <$childout>;
waitpid ($pid, 0);
chomp @ifconfig;
foreach my $line (@ifconfig) {
if ($line =~ /^([\w\.\-]+)/ ) {
$ifaces{$1} = 1;
}
if ($line =~ /inet.*?($ipv4reg)/) {
my ($ip,undef) = split(/\//,$1);
if (checkip(\$ip)) {
$ipv4{$ip} = 1;
}
}
if ($line =~ /Bcast:($ipv4reg)/) {
my ($ip,undef) = split(/\//,$1);
if (checkip(\$ip)) {
$brd{$ip} = 1;
}
}
if ($line =~ /inet6.*?($ipv6reg)/) {
my ($ip,undef) = split(/\//,$1);
$ip .= "/128";
if (checkip(\$ip)) {
$ipv6{$ip} = 1;
}
}
}
$status = 0;
}
else {
$status = 1;
}
if (-e "/var/cpanel/cpnat") {
open (my $NAT, "<", "/var/cpanel/cpnat");
flock ($NAT, LOCK_SH);
while (my $line = <$NAT>) {
chomp $line;
if ($line =~ /^(\#|\n|\r)/) {next}
my ($internal,$external) = split(/\s+/,$line);
if (checkip(\$internal) and checkip(\$external)) {
$ipv4{$external} = 1;
}
}
close ($NAT);
}
$self->{status} = $status;
return $self;
}
# end main
###############################################################################
# start ifaces
sub ifaces {
return %ifaces;
}
# end ifaces
###############################################################################
# start ipv4
sub ipv4 {
return %ipv4;
}
# end ipv4
###############################################################################
# start ipv6
sub ipv6 {
return %ipv6;
}
# end ipv6
###############################################################################
# start brd
sub brd {
return %brd;
}
# end brd
###############################################################################
1;

View File

@@ -0,0 +1,95 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::GetIPs;
use strict;
use lib '/usr/local/csf/lib';
use Carp;
use Socket;
use IPC::Open3;
use ConfigServer::Config;
use Exporter qw(import);
our $VERSION = 1.03;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(getips);
my $config = ConfigServer::Config->loadconfig();
my %config = $config->config();
my $ipv4reg = ConfigServer::Config->ipv4reg;
my $ipv6reg = ConfigServer::Config->ipv6reg;
# end main
###############################################################################
# start getips
sub getips {
my $hostname = shift;
my @ips;
if (-e $config{HOST} and -x $config{HOST}) {
my $cmdpid;
eval {
local $SIG{__DIE__} = undef;
local $SIG{'ALRM'} = sub {die};
alarm(10);
my ($childin, $childout);
$cmdpid = open3($childin, $childout, $childout, $config{HOST},"-W","5",$hostname);
close $childin;
my @results = <$childout>;
waitpid ($cmdpid, 0);
chomp @results;
foreach my $line (@results) {
if ($line =~ /($ipv4reg|$ipv6reg)/) {push @ips, $1}
}
alarm(0);
};
alarm(0);
if ($cmdpid =~ /\d+/ and $cmdpid > 1 and kill(0,$cmdpid)) {kill(9,$cmdpid)}
} else {
local $SIG{__DIE__} = undef;
eval ('use Socket6;');
if ($@) {
my @iplist;
my (undef, undef, undef, undef, @addrs) = gethostbyname($hostname);
foreach (@addrs) {push(@iplist,join(".",unpack("C4", $_)))}
push @ips,$_ foreach(@iplist);
} else {
eval ('
use Socket6;
my @res = getaddrinfo($hostname, undef, AF_UNSPEC, SOCK_STREAM);
while(scalar(@res)>=5){
my $saddr;
(undef, undef, undef, $saddr, undef, @res) = @res;
my ($host, undef) = getnameinfo($saddr,NI_NUMERICHOST | NI_NUMERICSERV);
push @ips,$host;
}
');
}
}
return @ips;
}
# end getips
###############################################################################
1;

107
csf/ConfigServer/KillSSH.pm Normal file
View File

@@ -0,0 +1,107 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::KillSSH;
use strict;
use lib '/usr/local/csf/lib';
use Fcntl qw(:DEFAULT :flock);
use ConfigServer::Logger;
use Exporter qw(import);
our $VERSION = 1.00;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw();
# end main
###############################################################################
# start iplookup
sub find {
my $ip = shift;
my $ports = shift;
my %inodes;
if ($ports eq "" or $ip eq "") {return}
foreach my $proto ("tcp","tcp6") {
open (my $IN, "<", "/proc/net/$proto");
flock ($IN, LOCK_SH);
while (<$IN>) {
my @rec = split();
if ($rec[9] =~ /uid/) {next}
my ($dip,$dport) = split(/:/,$rec[2]);
$dport = hex($dport);
my ($sip,$sport) = split(/:/,$rec[1]);
$sport = hex($sport);
$dip = &hex2ip($dip);
$sip = &hex2ip($sip);
if ($sip eq '0.0.0.1') {next}
if ($dip eq $ip) {
foreach my $port (split(/\,/, $ports)) {
if ($port eq $sport) {
$inodes{$rec[9]} = 1;
}
}
}
}
close ($IN);
}
opendir (my $PROCDIR, "/proc");
while (my $pid = readdir($PROCDIR)) {
if ($pid !~ /^\d+$/) {next}
opendir (DIR, "/proc/$pid/fd") or next;
while (my $file = readdir (DIR)) {
if ($file =~ /^\./) {next}
my $fd = readlink("/proc/$pid/fd/$file");
if ($fd =~ /^socket:\[?([0-9]+)\]?$/) {
if ($inodes{$1} and readlink("/proc/$pid/exe") =~ /sshd/) {
kill (9,$pid);
ConfigServer::Logger::logfile("*PT_SSHDKILL*: Process PID:[$pid] killed for blocked IP:[$ip]");
}
}
}
closedir (DIR);
}
closedir ($PROCDIR);
return;
}
# end find
###############################################################################
## start hex2ip
sub hex2ip {
my $bin = pack "C*" => map hex, $_[0] =~ /../g;
my @l = unpack "L*", $bin;
if (@l == 4) {
return join ':', map { sprintf "%x:%x", $_ >> 16, $_ & 0xffff } @l;
}
elsif (@l == 1) {
return join '.', map { $_ >> 24, ($_ >> 16 ) & 0xff, ($_ >> 8) & 0xff, $_ & 0xff } @l;
}
}
## end hex2ip
###############################################################################
1;

View File

@@ -0,0 +1,83 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::Logger;
use strict;
use lib '/usr/local/csf/lib';
use Carp;
use Fcntl qw(:DEFAULT :flock);
use ConfigServer::Config;
use Exporter qw(import);
our $VERSION = 1.02;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(logfile);
my $config = ConfigServer::Config->loadconfig();
my %config = $config->config();
my $hostname;
if (-e "/proc/sys/kernel/hostname") {
open (my $IN, "<", "/proc/sys/kernel/hostname");
flock ($IN, LOCK_SH);
$hostname = <$IN>;
chomp $hostname;
close ($IN);
} else {
$hostname = "unknown";
}
my $hostshort = (split(/\./,$hostname))[0];
my $sys_syslog;
if ($config{SYSLOG}) {
eval('use Sys::Syslog;'); ##no critic
unless ($@) {$sys_syslog = 1}
}
# end main
###############################################################################
# start logfile
sub logfile {
my $line = shift;
my @ts = split(/\s+/,scalar localtime);
if ($ts[2] < 10) {$ts[2] = " ".$ts[2]}
my $logfile = "/var/log/lfd.log";
if ($< != 0) {$logfile = "/var/log/lfd_messenger.log"}
sysopen (my $LOGFILE, $logfile, O_WRONLY | O_APPEND | O_CREAT);
flock ($LOGFILE, LOCK_EX);
print $LOGFILE "$ts[1] $ts[2] $ts[3] $hostshort lfd[$$]: $line\n";
close ($LOGFILE);
if ($config{SYSLOG} and $sys_syslog) {
eval {
local $SIG{__DIE__} = undef;
openlog('lfd', 'ndelay,pid', 'user');
syslog('info', $line);
closelog();
}
}
return;
}
# end logfile
###############################################################################
1;

View File

@@ -0,0 +1,439 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::LookUpIP;
use strict;
use lib '/usr/local/csf/lib';
use Carp;
use Fcntl qw(:DEFAULT :flock);
use IPC::Open3;
use JSON::Tiny;
use Net::IP;
use Socket;
use ConfigServer::CheckIP qw(checkip);
use ConfigServer::Config;
use ConfigServer::URLGet;
use Exporter qw(import);
our $VERSION = 2.00;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(iplookup);
my $config = ConfigServer::Config->loadconfig();
my %config = $config->config();
my $urlget;
if ($config{CC_LOOKUPS} == 4) {
$urlget = ConfigServer::URLGet->new($config{URLGET}, "", $config{URLPROXY});
unless (defined $urlget) {
$config{URLGET} = 1;
$urlget = ConfigServer::URLGet->new($config{URLGET}, "", $config{URLPROXY});
}
}
# end main
###############################################################################
# start iplookup
sub iplookup {
my $ip = shift;
my $cconly = shift;
my $host = "-";
my $iptype = checkip(\$ip);
if ($config{LF_LOOKUPS} and !$cconly) {
my $dnsip;
my $dnsrip;
my $dnshost;
my $cachehit;
open (my $DNS, "<", "/var/lib/csf/csf.dnscache");
flock ($DNS, LOCK_SH);
while (my $line = <$DNS>) {
chomp $line;
($dnsip,$dnsrip,$dnshost) = split(/\|/,$line);
if ($ip eq $dnsip) {
$cachehit = 1;
last;
}
}
close ($DNS);
if ($cachehit) {
$host = $dnshost;
} else {
if (-e $config{HOST} and -x $config{HOST}) {
my $cmdpid;
eval {
local $SIG{__DIE__} = undef;
local $SIG{'ALRM'} = sub {die};
alarm(10);
my ($childin, $childout);
$cmdpid = open3($childin, $childout, $childout, $config{HOST},"-W","5",$ip);
close $childin;
my @results = <$childout>;
waitpid ($cmdpid, 0);
chomp @results;
if ($results[0] =~ /(\S+)\.$/) {$host = $1}
alarm(0);
};
alarm(0);
if ($cmdpid =~ /\d+/ and $cmdpid > 1 and kill(0,$cmdpid)) {kill(9,$cmdpid)}
} else {
if ($iptype == 4) {
eval {
local $SIG{__DIE__} = undef;
local $SIG{'ALRM'} = sub {die};
alarm(10);
my $ipaddr = inet_aton($ip);
$host = gethostbyaddr($ipaddr, AF_INET);
alarm(0);
};
alarm(0);
}
elsif ($iptype == 6) {
eval {
local $SIG{__DIE__} = undef;
local $SIG{'ALRM'} = sub {die};
alarm(10);
eval('use Socket6;'); ##no critic
my $ipaddr = inet_pton(AF_INET6, $ip);
$host = gethostbyaddr($ipaddr, AF_INET6);
alarm(0);
};
alarm(0);
}
}
sysopen (DNS, "/var/lib/csf/csf.dnscache", O_WRONLY | O_APPEND | O_CREAT);
flock (DNS, LOCK_EX);
print DNS "$ip|$ip|$host\n";
close (DNS);
}
if ($host eq "") {$host = "-"}
}
if (($config{CC_LOOKUPS} and $iptype == 4) or ($config{CC_LOOKUPS} and $config{CC6_LOOKUPS} and $iptype == 6)) {
my @result;
eval {
local $SIG{__DIE__} = undef;
@result = &geo_binary($ip,$iptype);
};
my $asn = $result[4];
if ($result[0] eq "") {$result[0] = "-"}
if ($result[1] eq "") {$result[1] = "-"}
if ($result[2] eq "") {$result[2] = "-"}
if ($result[3] eq "") {$result[3] = "-"}
if ($result[4] eq "") {$result[4] = "-"} else {$result[4] = "[$result[4]]"}
if ($config{CC_LOOKUPS} == 3) {
if ($cconly) {return ($result[0],$asn)}
my $return = "$ip ($result[0]/$result[1]/$result[2]/$result[3]/$host/$result[4])";
if ($result[0] eq "-") {$return = "$ip ($host)"}
$return =~ s/'|"//g;
return $return;
}
elsif ($config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 4) {
if ($cconly) {return $result[0]}
my $return = "$ip ($result[0]/$result[1]/$result[2]/$result[3]/$host)";
if ($result[0] eq "-") {$return = "$ip ($host)"}
$return =~ s/'|"//g;
return $return;
}
else {
if ($cconly) {return $result[0]}
my $return = "$ip ($result[0]/$result[1]/$host)";
if ($result[0] eq "-") {$return = "$ip ($host)"}
$return =~ s/'|"//g;
return $return;
}
}
if ($config{LF_LOOKUPS}) {
if ($host eq "-") {$host = "Unknown"}
my $return = "$ip ($host)";
$return =~ s/'//g;
return $return;
} else {
return $ip;
}
}
# end iplookup
###############################################################################
# start geo_binary
sub geo_binary {
my $myip = shift;
my $ipv = shift;
my @return;
my $netip = Net::IP->new($myip);
my $ip = $netip->binip();
my $type = $netip->iptype();
if ($type eq "PRIVATE") {return}
if ($config{CC_LOOKUPS} == 4) {
my ($status, $text) = $urlget->urlget("http://api.db-ip.com/v2/free/$myip");
if ($status) {$text = ""}
if ($text ne "") {
my $json = JSON::Tiny::decode_json($text);
return ($json->{countryCode},$json->{countryName},$json->{stateProv},$json->{city});
} else {
return;
}
return;
}
if ($config{CC_SRC} eq "" or $config{CC_SRC} eq "1") {
my $file = "/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv${ipv}.csv";
if ($config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 3) {
$file = "/var/lib/csf/Geo/GeoLite2-City-Blocks-IPv${ipv}.csv";
}
my $start = 0;
my $end = -s $file;
$end += 4;
my $cnt = 0;
my $last;
my $range;
my $geoid;
open (my $CSV, "<", $file);
flock ($CSV, LOCK_SH);
while (1) {
my $mid = int (($end + $start) / 2);
seek ($CSV, $mid, 0);
my $a = <$CSV>;
my $b = <$CSV>;
chomp $b;
($range,$geoid,undef) = split(/\,/,$b);
if ($range !~ /^\d/ or $range eq $last or $range eq "") {return}
$last = $range;
my $netip = Net::IP->new($range);
my $lastip = $netip->last_ip();
$lastip = Net::IP::ip_iptobin($lastip,$ipv);
my $firstip = $netip->ip();
$firstip = Net::IP::ip_iptobin($firstip,$ipv);
if (Net::IP::ip_bincomp($ip,'lt',$firstip) == 1) {
$end = $mid;
}
elsif (Net::IP::ip_bincomp($ip,'gt',$lastip) == 1) {
$start = $mid;
} else {
last;
}
$cnt++;
if ($cnt > 200) {return}
}
close ($CSV);
if ($geoid > 0) {
my $file = "/var/lib/csf/Geo/GeoLite2-Country-Locations-en.csv";
if ($config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 3) {
$file = "/var/lib/csf/Geo/GeoLite2-City-Locations-en.csv";
}
my $start = 0;
my $end = -s $file;
$end += 4;
my $cnt = 0;
my $last;
open (my $CSV, "<", $file);
flock ($CSV, LOCK_SH);
while (1) {
my $mid = int (($end + $start) / 2);
seek ($CSV, $mid, 0);
my $a = <$CSV>;
my $b = <$CSV>;
chomp $b;
my @bits = split(/\,/,$b);
if ($range !~ /^\d/ or $bits[0] eq $last or $bits[0] eq "") {last}
$last = $bits[0];
if ($geoid < $bits[0]) {
$end = $mid;
}
elsif ($geoid > $bits[0]) {
$start = $mid + 1;
} else {
$b =~ s/\"//g;
my ($geoname_id, $locale_code, $continent_code, $continent_name, $country_iso_code, $country_name, $subdivision_1_iso_code, $subdivision_1_name, $subdivision_2_iso_code, $subdivision_2_name, $city_name, $metro_code, $time_zone) = split(/\,/,$b);
my $region = $subdivision_2_name;
if ($region eq "" or $region eq $city_name) {$region = $subdivision_1_name}
$return[0] = $country_iso_code;
$return[1] = $country_name;
$return[2] = $region;
$return[3] = $city_name;
last;
}
$cnt++;
if ($cnt > 200) {return}
}
close ($CSV);
}
if ($config{CC_LOOKUPS} == 3) {
my $file = "/var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv${ipv}.csv";
my $start = 0;
my $end = -s $file;
$end += 4;
my $cnt = 0;
my $last;
my $range;
my $asn;
my $asnorg;
open (my $CSV, "<", $file);
flock ($CSV, LOCK_SH);
while (1) {
my $mid = int (($end + $start) / 2);
seek ($CSV, $mid, 0);
my $a = <$CSV>;
my $b = <$CSV>;
chomp $b;
($range,$asn,$asnorg) = split(/\,/,$b,3);
if ($range !~ /^\d/ or $range eq $last or $range eq "") {last}
$last = $range;
my $netip = Net::IP->new($range);
my $lastip = $netip->last_ip();
$lastip = Net::IP::ip_iptobin($lastip,$ipv);
my $firstip = $netip->ip();
$firstip = Net::IP::ip_iptobin($firstip,$ipv);
if (Net::IP::ip_bincomp($ip,'lt',$firstip) == 1) {
$end = $mid;
}
elsif (Net::IP::ip_bincomp($ip,'gt',$lastip) == 1) {
$start = $mid + 1;
} else {
$return[4] = "AS$asn $asnorg";
last;
}
$cnt++;
if ($cnt > 200) {last}
}
close ($CSV);
}
} elsif ($config{CC_SRC} eq "2") {
my %country_name;
open (my $CC, "<", "/var/lib/csf/Geo/countryInfo.txt");
flock ($CC, LOCK_SH);
foreach my $line (<$CC>) {
if ($line eq "" or $line =~ /^\#/ or $line =~ /^\s/) {next}
my ($cc,undef,undef,undef,$country,undef) = split(/\t/, $line);
if ($cc ne "" and $country ne "") {$country_name{$cc} = $country}
}
close ($CC);
my $file = "/var/lib/csf/Geo/dbip-country-lite.csv";
if ($config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 3) {
$file = "/var/lib/csf/Geo/dbip-city-lite.csv";
}
my $start = 0;
my $end = -s $file;
$end += 4;
my $cnt = 0;
my $last;
my $range;
my $geoid;
open (my $CSV, "<", $file);
flock ($CSV, LOCK_SH);
while (1) {
my $mid = int (($end + $start) / 2);
seek ($CSV, $mid, 0);
my $a = <$CSV>;
my $b = <$CSV>;
chomp $b;
my ($firstip,$lastip,$cc_lookups1,$country_iso_code,$region,$city_name,undef) = split(/\,/,$b);
if ($firstip eq $lastip or $firstip eq "") {return}
if (checkip(\$firstip) ne $ipv) {
if ($ipv eq "6") {
$start = $mid;
} else {
$end = $mid;
}
} else {
my $netfirstip = Net::IP->new($firstip);
my $firstip = $netfirstip->binip();
my $netlastip = Net::IP->new($lastip);
my $lastip = $netlastip->binip();
if (Net::IP::ip_bincomp($ip,'lt',$firstip) == 1) {
$end = $mid;
}
elsif (Net::IP::ip_bincomp($ip,'gt',$lastip) == 1) {
$start = $mid + 1;
} else {
if ($config{CC_LOOKUPS} == 1) {$country_iso_code = $cc_lookups1}
if ($country_iso_code eq "ZZ") {last}
$return[0] = $country_iso_code;
$return[1] = $country_name{$country_iso_code};
$return[2] = $region;
$return[3] = $city_name;
last;
}
}
$cnt++;
if ($cnt > 200) {return}
}
close ($CSV);
if ($config{CC_LOOKUPS} == 3) {
my $file = "/var/lib/csf/Geo/ip2asn-combined.tsv";
my $start = 0;
my $end = -s $file;
$end += 4;
my $cnt = 0;
my $last;
my $range;
my $asn;
my $asnorg;
open (my $CSV, "<", $file);
flock ($CSV, LOCK_SH);
while (1) {
my $mid = int (($end + $start) / 2);
seek ($CSV, $mid, 0);
my $a = <$CSV>;
my $b = <$CSV>;
chomp $b;
my ($firstip,$lastip,$asn,undef,$asnorg) = split(/\t/,$b);
if ($firstip eq $lastip or $firstip eq "") {last}
if (checkip(\$firstip) ne $ipv) {
if ($ipv eq "6") {
$start = $mid;
} else {
$end = $mid;
}
} else {
my $netfirstip = Net::IP->new($firstip);
my $firstip = $netfirstip->binip();
my $netlastip = Net::IP->new($lastip);
my $lastip = $netlastip->binip();
if (Net::IP::ip_bincomp($ip,'lt',$firstip) == 1) {
$end = $mid;
}
elsif (Net::IP::ip_bincomp($ip,'gt',$lastip) == 1) {
$start = $mid + 1;
} else {
if ($asn eq "0") {last}
$return[4] = "AS$asn $asnorg";
last;
}
}
$cnt++;
if ($cnt > 200) {last}
}
close ($CSV);
}
}
return @return;
}
# end geo_binary
###############################################################################
1;

File diff suppressed because it is too large Load Diff

226
csf/ConfigServer/Ports.pm Normal file
View File

@@ -0,0 +1,226 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::Ports;
use strict;
use lib '/usr/local/csf/lib';
use Fcntl qw(:DEFAULT :flock);
use ConfigServer::Config;
use Exporter qw(import);
our $VERSION = 1.02;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw();
my %printable = ( ( map { chr($_), unpack('H2', chr($_)) } (0..255) ), "\\"=>'\\', "\r"=>'r', "\n"=>'n', "\t"=>'t', "\""=>'"' ); ##no critic
my %tcpstates = ("01" => "ESTABLISHED",
"02" => "SYN_SENT",
"03" => "SYN_RECV",
"04" => "FIN_WAIT1",
"05" => "FIN_WAIT2",
"06" => "TIME_WAIT",
"07" => "CLOSE",
"08" => "CLOSE_WAIT",
"09" => "LAST_ACK",
"0A" => "LISTEN",
"0B" => "CLOSING");
# end main
###############################################################################
# start listening
sub listening {
my %net;
my %conn;
my %listen;
foreach my $proto ("tcp","udp","tcp6","udp6") {
open (my $IN, "<","/proc/net/$proto");
flock ($IN, LOCK_SH);
while (<$IN>) {
my @rec = split();
if ($rec[9] =~ /uid/) {next}
my ($dip,$dport) = split(/:/,$rec[1]);
$dport = hex($dport);
my ($sip,$sport) = split(/:/,$rec[2]);
$sport = hex($sport);
$dip = &hex2ip($dip);
$sip = &hex2ip($sip);
my $inode = $rec[9];
my $state = $tcpstates{$rec[3]};
my $protocol = $proto;
$protocol =~ s/6//;
if ($protocol eq "udp" and $state eq "CLOSE") {$state = "LISTEN"}
if ($state eq "ESTABLISHED") {$conn{$dport}{$protocol}++}
if ($dip =~ /^127\./) {next}
if ($dip =~ /^0\.0\.0\.1/) {next}
if ($state eq "LISTEN") {$net{$inode}{$protocol} = $dport}
}
close ($IN);
}
opendir (PROCDIR, "/proc");
while (my $pid = readdir(PROCDIR)) {
if ($pid !~ /^\d+$/) {next}
my $exe = readlink("/proc/$pid/exe") || "";
my $cwd = readlink("/proc/$pid/cwd") || "";
my $uid;
my $user;
if (defined $exe) {$exe =~ s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$printable{$1}/sg}
open (my $CMDLINE,"<","/proc/$pid/cmdline");
flock ($CMDLINE, LOCK_SH);
my $cmdline = <$CMDLINE>;
close ($CMDLINE);
if (defined $cmdline) {
chomp $cmdline;
$cmdline =~ s/\0$//g;
$cmdline =~ s/\0/ /g;
$cmdline =~ s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$printable{$1}/sg;
$cmdline =~ s/\s+$//;
$cmdline =~ s/^\s+//;
}
if ($exe eq "") {next}
my @fd;
opendir (DIR, "/proc/$pid/fd") or next;
while (my $file = readdir (DIR)) {
if ($file =~ /^\./) {next}
push (@fd, readlink("/proc/$pid/fd/$file"));
}
closedir (DIR);
open (my $STATUS,"<", "/proc/$pid/status") or next;
flock ($STATUS, LOCK_SH);
my @status = <$STATUS>;
close ($STATUS);
chomp @status;
foreach my $line (@status) {
if ($line =~ /^Uid:(.*)/) {
my $uidline = $1;
my @uids;
foreach my $bit (split(/\s/,$uidline)) {
if ($bit =~ /^(\d*)$/) {push @uids, $1}
}
$uid = $uids[-1];
$user = getpwuid($uid);
if ($user eq "") {$user = $uid}
}
}
my $files;
my $sockets;
foreach my $file (@fd) {
if ($file =~ /^socket:\[?([0-9]+)\]?$/) {
my $ino = $1;
if ($net{$ino}) {
foreach my $protocol (keys %{$net{$ino}}) {
$listen{$protocol}{$net{$ino}{$protocol}}{$pid}{user} = $user;
$listen{$protocol}{$net{$ino}{$protocol}}{$pid}{exe} = $exe;
$listen{$protocol}{$net{$ino}{$protocol}}{$pid}{cmd} = $cmdline;
$listen{$protocol}{$net{$ino}{$protocol}}{$pid}{cmd} = $cmdline;
$listen{$protocol}{$net{$ino}{$protocol}}{$pid}{conn} = $conn{$net{$ino}{$protocol}}{$protocol} | "-";
}
}
}
}
}
closedir (PROCDIR);
return %listen;
}
# end listening
###############################################################################
# start openports
sub openports {
my $config = ConfigServer::Config->loadconfig();
my %config = $config->config();
my %ports;
$config{TCP_IN} =~ s/\s//g;
foreach my $entry (split(/,/,$config{TCP_IN})) {
if ($entry =~ /^(\d+):(\d+)$/) {
my $from = $1;
my $to = $2;
for (my $port = $from; $port < $to ; $port++) {
$ports{tcp}{$port} = 1;
}
} else {
$ports{tcp}{$entry} = 1;
}
}
$config{TCP6_IN} =~ s/\s//g;
foreach my $entry (split(/,/,$config{TCP6_IN})) {
if ($entry =~ /^(\d+):(\d+)$/) {
my $from = $1;
my $to = $2;
for (my $port = $from; $port < $to ; $port++) {
$ports{tcp6}{$port} = 1;
}
} else {
$ports{tcp6}{$entry} = 1;
}
}
$config{UDP_IN} =~ s/\s//g;
foreach my $entry (split(/,/,$config{UDP_IN})) {
if ($entry =~ /^(\d+):(\d+)$/) {
my $from = $1;
my $to = $2;
for (my $port = $from; $port < $to ; $port++) {
$ports{udp}{$port} = 1;
}
} else {
$ports{udp}{$entry} = 1;
}
}
$config{UDP6_IN} =~ s/\s//g;
foreach my $entry (split(/,/,$config{UDP6_IN})) {
if ($entry =~ /^(\d+):(\d+)$/) {
my $from = $1;
my $to = $2;
for (my $port = $from; $port < $to ; $port++) {
$ports{udp6}{$port} = 1;
}
} else {
$ports{udp6}{$entry} = 1;
}
}
return %ports;
}
# end openports
###############################################################################
## start hex2ip
sub hex2ip {
my $bin = pack "C*" => map hex, $_[0] =~ /../g;
my @l = unpack "L*", $bin;
if (@l == 4) {
return join ':', map { sprintf "%x:%x", $_ >> 16, $_ & 0xffff } @l;
}
elsif (@l == 1) {
return join '.', map { $_ >> 24, ($_ >> 16 ) & 0xff, ($_ >> 8) & 0xff, $_ & 0xff } @l;
}
}
## end hex2ip
###############################################################################
1;

View File

@@ -0,0 +1,255 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::RBLCheck;
use strict;
use lib '/usr/local/csf/lib';
use Fcntl qw(:DEFAULT :flock);
use ConfigServer::Config;
use ConfigServer::CheckIP qw(checkip);
use ConfigServer::Slurp qw(slurp);
use ConfigServer::GetIPs qw(getips);
use ConfigServer::RBLLookup qw(rbllookup);
use IPC::Open3;
use Net::IP;
use ConfigServer::GetEthDev;
use Exporter qw(import);
our $VERSION = 1.01;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw();
my ($ui, $failures, $verbose, $cleanreg, $status, %ips, $images, %config,
$ipresult, $output);
my $ipv4reg = ConfigServer::Config->ipv4reg;
my $ipv6reg = ConfigServer::Config->ipv6reg;
# end main
###############################################################################
# start report
sub report {
$verbose = shift;
$images = shift;
$ui = shift;
my $config = ConfigServer::Config->loadconfig();
%config = $config->config();
$cleanreg = ConfigServer::Slurp->cleanreg;
$failures = 0;
$| = 1;
&startoutput;
&getethdev;
my @RBLS = slurp("/usr/local/csf/lib/csf.rbls");
if (-e "/etc/csf/csf.rblconf") {
my @entries = slurp("/etc/csf/csf.rblconf");
foreach my $line (@entries) {
if ($line =~ /^Include\s*(.*)$/) {
my @incfile = slurp($1);
push @entries,@incfile;
}
}
foreach my $line (@entries) {
$line =~ s/$cleanreg//g;
if ($line eq "") {next}
if ($line =~ /^\s*\#|Include/) {next}
if ($line =~ /^enablerbl:(.*)$/) {
push @RBLS, $1;
}
elsif ($line =~ /^disablerbl:(.*)$/) {
my $hit = $1;
for (0..@RBLS) {
my $x = $_;
my ($rbl,$rblurl) = split(/:/,$RBLS[$x],2);
if ($rbl eq $hit) {$RBLS[$x] = ""}
}
}
if ($line =~ /^enableip:(.*)$/) {
if (checkip(\$1)) {$ips{$1} = 1}
}
elsif ($line =~ /^disableip:(.*)$/) {
if (checkip(\$1)) {delete $ips{$1}}
}
}
}
@RBLS = sort @RBLS;
foreach my $ip (sort keys %ips) {
my $netip = Net::IP->new($ip);
my $type = $netip->iptype();
if ($type eq "PUBLIC") {
if ($verbose and -e "/var/lib/csf/${ip}.rbls") {
unlink "/var/lib/csf/${ip}.rbls";
}
if (-e "/var/lib/csf/${ip}.rbls") {
my $text = join("\n",slurp("/var/lib/csf/${ip}.rbls"));
if ($ui) {print $text} else {$output .= $text}
} else {
if ($verbose) {
$ipresult = "";
my $hits = 0;
&addtitle("Checked $ip ($type) on ".localtime());
foreach my $line (@RBLS) {
my ($rbl,$rblurl) = split(/:/,$line,2);
if ($rbl eq "") {next}
my ($rblhit,$rbltxt) = rbllookup($ip,$rbl);
my @tmptxt = $rbltxt;
$rbltxt = "";
foreach my $line (@tmptxt) {
$line =~ s/(http(\S+))/<a target="_blank" href="$1">$1<\/a>/g;
$rbltxt .= "${line}\n";
}
$rbltxt =~ s/\n/<br>\n/g;
if ($rblhit eq "timeout") {
&addline(0,$rbl,$rblurl,"TIMEOUT");
}
elsif ($rblhit eq "") {
if ($verbose == 2) {
&addline(0,$rbl,$rblurl,"OK");
}
}
else {
&addline(1,$rbl,$rblurl,$rbltxt);
$hits++;
}
}
unless ($hits) {
my $text;
$text .= "<div style='clear: both;background: #BDECB6;padding: 8px;border: 1px solid #DDDDDD;'>OK</div>\n";
if ($ui) {print $text} else {$output .= $text}
$ipresult .= $text;
}
sysopen (my $OUT, "/var/lib/csf/${ip}.rbls", O_WRONLY | O_CREAT);
flock($OUT, LOCK_EX);
print $OUT $ipresult;
close ($OUT);
} else {
&addtitle("New $ip ($type)");
my $text;
$text .= "<div style='clear: both;background: #FFD1DC;padding: 8px;border: 1px solid #DDDDDD;'>Not Checked</div>\n";
if ($ui) {print $text} else {$output .= $text}
}
}
} else {
if ($verbose == 2) {
&addtitle("Skipping $ip ($type)");
my $text;
$text .= "<div style='clear: both;background: #BDECB6;padding: 8px;border: 1px solid #DDDDDD;'>OK</div>\n";
if ($ui) {print $text} else {$output .= $text}
}
}
}
&endoutput;
return ($failures,$output);
}
# end report
###############################################################################
# start startoutput
sub startoutput {
return;
}
# end startoutput
###############################################################################
# start addline
sub addline {
my $status = shift;
my $rbl = shift;
my $rblurl = shift;
my $comment = shift;
my $text;
my $check = $rbl;
if ($rblurl ne "") {$check = "<a href='$rblurl' target='_blank'>$rbl</a>"}
if ($status) {
$text .= "<div style='display: flex;width: 100%;clear: both;'>\n";
$text .= "<div style='width: 250px;background: #FFD1DC;padding: 8px;border-bottom: 1px solid #DDDDDD;border-left: 1px solid #DDDDDD;border-right: 1px solid #DDDDDD;'>$check</div>\n";
$text .= "<div style='flex: 1;padding: 8px;border-bottom: 1px solid #DDDDDD;border-right: 1px solid #DDDDDD;'>$comment</div>\n";
$text .= "</div>\n";
$failures ++;
$ipresult .= $text;
}
elsif ($verbose) {
$text .= "<div style='display: flex;width: 100%;clear: both;'>\n";
$text .= "<div style='width: 250px;background: #BDECB6;padding: 8px;border-bottom: 1px solid #DDDDDD;border-left: 1px solid #DDDDDD;border-right: 1px solid #DDDDDD;'>$check</div>\n";
$text .= "<div style='flex: 1;padding: 8px;border-bottom: 1px solid #DDDDDD;border-right: 1px solid #DDDDDD;'>$comment</div>\n";
$text .= "</div>\n";
}
if ($ui) {print $text} else {$output .= $text}
return;
}
# end addline
###############################################################################
# start addtitle
sub addtitle {
my $title = shift;
my $text;
$text .= "<br><div style='clear: both;padding: 8px;background: #F4F4EA;border: 1px solid #DDDDDD;border-top-right-radius: 5px;border-top-left-radius: 5px;'><strong>$title</strong></div>\n";
$ipresult .= $text;
if ($ui) {print $text} else {$output .= $text}
return;
}
# end addtitle
###############################################################################
# start endoutput
sub endoutput {
if ($ui) {print "<br>\n"} else {$output .= "<br>\n"}
return;
}
# end endoutput
###############################################################################
# start getethdev
sub getethdev {
my $ethdev = ConfigServer::GetEthDev->new();
my %g_ipv4 = $ethdev->ipv4;
my %g_ipv6 = $ethdev->ipv6;
foreach my $key (keys %g_ipv4) {
$ips{$key} = 1;
}
# if ($config{IPV6}) {
# foreach my $key (keys %g_ipv6) {
# eval {
# local $SIG{__DIE__} = undef;
# $ipscidr6->add($key);
# };
# }
# }
return;
}
# end getethdev
###############################################################################
1;

View File

@@ -0,0 +1,116 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::RBLLookup;
use strict;
use lib '/usr/local/csf/lib';
use Fcntl qw(:DEFAULT :flock);
use IPC::Open3;
use Net::IP;
use ConfigServer::Config;
use ConfigServer::CheckIP qw(checkip);
use Exporter qw(import);
our $VERSION = 1.01;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(rbllookup);
my $config = ConfigServer::Config->loadconfig();
my %config = $config->config();
my $ipv4reg = ConfigServer::Config->ipv4reg;
my $ipv6reg = ConfigServer::Config->ipv6reg;
# end main
###############################################################################
# start rbllookup
sub rbllookup {
my $ip = shift;
my $rbl = shift;
my %rblhits;
my $netip;
my $reversed_ip;
my $timeout = 4;
my $rblhit;
my $rblhittxt;
if (checkip(\$ip)) {
eval {
local $SIG{__DIE__} = undef;
$netip = Net::IP->new($ip);
$reversed_ip = $netip->reverse_ip();
};
if ($reversed_ip =~ /^(\S+)\.in-addr\.arpa/) {$reversed_ip = $1}
if ($reversed_ip =~ /^(\S+)\s+(\S+)\.in-addr\.arpa/) {$reversed_ip = $2}
if ($reversed_ip =~ /^(\S+)\.ip6\.arpa/) {$reversed_ip = $1}
if ($reversed_ip =~ /^(\S+)\s+(\S+)\.ip6\.arpa/) {$reversed_ip = $2}
if ($reversed_ip ne "") {
my $lookup_ip = $reversed_ip.".".$rbl;
my $cmdpid;
eval {
local $SIG{__DIE__} = undef;
local $SIG{'ALRM'} = sub {die};
alarm($timeout);
my ($childin, $childout);
$cmdpid = open3($childin, $childout, $childout, $config{HOST},"-t","A",$lookup_ip);
close $childin;
my @results = <$childout>;
waitpid ($cmdpid, 0);
chomp @results;
if ($results[0] =~ /^${reversed_ip}.+ ($ipv4reg|$ipv6reg)$/) {$rblhit = $1}
alarm(0);
};
alarm(0);
if ($@) {$rblhit = "timeout"}
if ($cmdpid =~ /\d+/ and $cmdpid > 1 and kill(0,$cmdpid)) {kill(9,$cmdpid)}
if ($rblhit ne "") {
if ($rblhit ne "timeout") {
my $cmdpid;
eval {
local $SIG{__DIE__} = undef;
local $SIG{'ALRM'} = sub {die};
alarm($timeout);
my ($childin, $childout);
$cmdpid = open3($childin, $childout, $childout, $config{HOST},"-t","TXT",$lookup_ip);
close $childin;
my @results = <$childout>;
waitpid ($cmdpid, 0);
chomp @results;
foreach my $line (@results) {
if ($line =~ /^${reversed_ip}.+ "([^\"]+)"$/) {$rblhittxt .= "$1\n"}
}
alarm(0);
};
alarm(0);
if ($cmdpid =~ /\d+/ and $cmdpid > 1 and kill(0,$cmdpid)) {kill(9,$cmdpid)}
}
}
}
}
return ($rblhit,$rblhittxt);
}
# end rbllookup
###############################################################################
1;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,86 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::Sanity;
use strict;
use lib '/usr/local/csf/lib';
use Fcntl qw(:DEFAULT :flock);
use Carp;
use ConfigServer::Config;
use Exporter qw(import);
our $VERSION = 1.02;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(sanity);
my %sanity;
my %sanitydefault;
my $sanityfile = "/usr/local/csf/lib/sanity.txt";
open (my $IN, "<", $sanityfile);
flock ($IN, LOCK_SH);
my @data = <$IN>;
close ($IN);
chomp @data;
foreach my $line (@data) {
my ($name,$value,$def) = split(/\=/,$line);
$sanity{$name} = $value;
$sanitydefault{$name} = $def;
}
my $config = ConfigServer::Config->loadconfig();
my %config = $config->config();
if ($config{IPSET}) {
delete $sanity{"DENY_IP_LIMIT"};
delete $sanitydefault{"DENY_IP_LIMIT"};
}
# end main
###############################################################################
# start sanity
sub sanity {
my $sanity_item = shift;
my $sanity_value = shift;
my $insane = 0;
$sanity_item =~ s/\s//g;
$sanity_value =~ s/\s//g;
if (defined $sanity{$sanity_item}) {
$insane = 1;
foreach my $check (split(/\|/,$sanity{$sanity_item})) {
if ($check =~ /-/) {
my ($from,$to) = split(/\-/,$check);
if (($sanity_value >= $from) and ($sanity_value <= $to)) {$insane = 0}
} else {
if ($sanity_value eq $check) {$insane = 0}
}
}
$sanity{$sanity_item} =~ s/\|/ or /g;
}
return ($insane,$sanity{$sanity_item},$sanitydefault{$sanity_item});
}
# end sanity
###############################################################################
1;

View File

@@ -0,0 +1,179 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::Sendmail;
use strict;
use lib '/usr/local/csf/lib';
use Carp;
use POSIX qw(strftime);
use Fcntl qw(:DEFAULT :flock);
use ConfigServer::Config;
use ConfigServer::CheckIP qw(checkip);
use Exporter qw(import);
our $VERSION = 1.02;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw();
my $config = ConfigServer::Config->loadconfig();
my %config = $config->config();
my $tz = strftime("%z", localtime);
my $hostname;
if (-e "/proc/sys/kernel/hostname") {
open (my $IN, "<", "/proc/sys/kernel/hostname");
flock ($IN, LOCK_SH);
$hostname = <$IN>;
chomp $hostname;
close ($IN);
} else {
$hostname = "unknown";
}
if ($config{LF_ALERT_SMTP}) {
require Net::SMTP;
import Net::SMTP;
}
# end main
###############################################################################
# start sendmail
sub relay {
my ($to, $from, @message) = @_;
my $time = localtime(time);
if ($to eq "") {$to = $config{LF_ALERT_TO}} else {$config{LF_ALERT_TO} = $to}
if ($from eq "") {$from = $config{LF_ALERT_FROM}} else {$config{LF_ALERT_FROM} = $from}
my $data;
if ($from =~ /([\w\.\=\-\_]+\@[\w\.\-\_]+)/) {$from = $1}
if ($from eq "") {$from = "root"}
if ($to =~ /([\w\.\=\-\_]+\@[\w\.\-\_]+)/) {$to = $1}
if ($to eq "") {$to = "root"}
my $header = 1;
foreach my $line (@message) {
$line =~ s/\r//;
if ($line eq "") {$header = 0}
$line =~ s/\[time\]/$time $tz/ig;
$line =~ s/\[hostname\]/$hostname/ig;
if ($header) {
if ($line =~ /^To:\s*(.*)\s*$/i) {
my $totxt = $1;
if ($config{LF_ALERT_TO} ne "") {
$line =~ s/^To:.*$/To: $config{LF_ALERT_TO}/i;
} else {
$to = $totxt;
}
}
if ($line =~ /^From:\s*(.*)\s*$/i) {
my $fromtxt = $1;
if ($config{LF_ALERT_FROM} ne "") {
$line =~ s/^From:.*$/From: $config{LF_ALERT_FROM}/i;
} else {
$from = $1;
}
}
}
$data .= $line."\n";
}
$data = &wraptext($data, 990);
if ($config{LF_ALERT_SMTP}) {
if ($from !~ /\@/) {$from .= '@'.$hostname}
if ($to !~ /\@/) {$to .= '@'.$hostname}
my $smtp = Net::SMTP->new($config{LF_ALERT_SMTP}, Timeout => 10) or carp("Unable to send SMTP alert via [$config{LF_ALERT_SMTP}]: $!");
if (defined $smtp) {
$smtp->mail($from);
$smtp->to($to);
$smtp->data();
$smtp->datasend($data);
$smtp->dataend();
$smtp->quit();
}
} else {
local $SIG{CHLD} = 'DEFAULT';
my $error = 0;
open (my $MAIL, "|-", "$config{SENDMAIL} -f $from -t") or carp("Unable to send SENDMAIL alert via [$config{SENDMAIL}]: $!");
print $MAIL $data;
close ($MAIL) or $error = 1;
if ($error and $config{DEBUG}) {
logfile("Failed to send message via sendmail binary: $?");
logfile("Failed message: [$data]");
}
}
return;
}
# end sendmail
###############################################################################
# start wraptext
sub wraptext {
my $text = shift;
my $column = shift;
my $original = $text;
my $return = "";
my $hit = 1;
my $loop = 0;
while ($hit) {
$hit = 0;
$return = "";
foreach my $line (split(/\n/, $text)) {
if (length($line) > $column) {
foreach ($line =~ /(.{1,$column})/g) {
my $chunk = $_;
my $newchunk = "";
my $thishit = 0;
my @chars = split(//,$chunk);
for (my $x = length($chunk)-1;$x >= 0; $x--) {
if ($chars[$x] =~ /\s/) {
for (0..$x) {$newchunk .= $chars[$_]}
$newchunk .= "\n";
for ($x+1..length($chunk)-1) {$newchunk .= $chars[$_]}
$thishit = 1;
last;
}
}
if ($thishit) {
$hit = 1;
$thishit = 0;
$return .= $newchunk;
} else {
$return .= $chunk."\n";
}
}
} else {
$return .= $line."\n";
}
}
$text = $return;
$loop++;
if ($loop > 1000) {
return $original;
last;
}
}
if (length($return) < length($original)) {$return = $original}
return $return;
}
# end wraptext
###############################################################################
1;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

131
csf/ConfigServer/Service.pm Normal file
View File

@@ -0,0 +1,131 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::Service;
use strict;
use lib '/usr/local/csf/lib';
use Carp;
use IPC::Open3;
use Fcntl qw(:DEFAULT :flock);
use ConfigServer::Config;
use Exporter qw(import);
our $VERSION = 1.01;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw();
my $config = ConfigServer::Config->loadconfig();
my %config = $config->config();
open (my $IN, "<", "/proc/1/comm");
flock ($IN, LOCK_SH);
my $sysinit = <$IN>;
close ($IN);
chomp $sysinit;
if ($sysinit ne "systemd") {$sysinit = "init"}
# end main
###############################################################################
# start type
sub type {
return $sysinit;
}
# end type
###############################################################################
# start startlfd
sub startlfd {
if ($sysinit eq "systemd") {
&printcmd($config{SYSTEMCTL},"start","lfd.service");
&printcmd($config{SYSTEMCTL},"status","lfd.service");
} else {
&printcmd("/etc/init.d/lfd","start");
}
return;
}
# end startlfd
###############################################################################
# start stoplfd
sub stoplfd {
if ($sysinit eq "systemd") {
&printcmd($config{SYSTEMCTL},"stop","lfd.service");
}
else {
&printcmd("/etc/init.d/lfd","stop");
}
return;
}
# end stoplfd
###############################################################################
# start restartlfd
sub restartlfd {
if ($sysinit eq "systemd") {
&printcmd($config{SYSTEMCTL},"restart","lfd.service");
&printcmd($config{SYSTEMCTL},"status","lfd.service");
}
else {
&printcmd("/etc/init.d/lfd","restart");
}
return;
}
# end restartlfd
###############################################################################
# start restartlfd
sub statuslfd {
if ($sysinit eq "systemd") {
&printcmd($config{SYSTEMCTL},"status","lfd.service");
}
else {
&printcmd("/etc/init.d/lfd","status");
}
return 0
}
# end restartlfd
###############################################################################
# start printcmd
sub printcmd {
my @command = @_;
if ($config{DIRECTADMIN}) {
my $doublepid = fork;
if ($doublepid == 0) {
my ($childin, $childout);
my $pid = open3($childin, $childout, $childout, @command);
while (<$childout>) {print $_}
waitpid ($pid, 0);
exit;
}
waitpid ($doublepid, 0);
} else {
my ($childin, $childout);
my $pid = open3($childin, $childout, $childout, @command);
while (<$childout>) {print $_}
waitpid ($pid, 0);
}
return;
}
# end printcmd
###############################################################################
1;

68
csf/ConfigServer/Slurp.pm Normal file
View File

@@ -0,0 +1,68 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::Slurp;
use strict;
use lib '/usr/local/csf/lib';
use Fcntl qw(:DEFAULT :flock);
use Carp;
use Exporter qw(import);
our $VERSION = 1.02;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(slurp);
our $slurpreg = qr/(?>\x0D\x0A?|[\x0A-\x0C\x85\x{2028}\x{2029}])/;
our $cleanreg = qr/(\r)|(\n)|(^\s+)|(\s+$)/;
# end main
###############################################################################
# start slurp
sub slurp {
my $file = shift;
if (-e $file) {
sysopen (my $FILE, $file, O_RDONLY) or carp "*Error* Unable to open [$file]: $!";
flock ($FILE, LOCK_SH) or carp "*Error* Unable to lock [$file]: $!";
my $text = do {local $/; <$FILE>};
close ($FILE);
return split(/$slurpreg/,$text);
} else {
carp "*Error* File does not exist: [$file]";
}
return;
}
# end slurp
###############################################################################
# start slurpreg
sub slurpreg {
return $slurpreg;
}
# end slurpreg
###############################################################################
# start cleanreg
sub cleanreg {
return $cleanreg;
}
# end cleanreg
###############################################################################
1;

305
csf/ConfigServer/URLGet.pm Normal file
View File

@@ -0,0 +1,305 @@
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# 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 3 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, see <https://www.gnu.org/licenses>.
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
# start main
package ConfigServer::URLGet;
use strict;
use lib '/usr/local/csf/lib';
use Fcntl qw(:DEFAULT :flock);
use Carp;
use IPC::Open3;
use ConfigServer::Config;
use Exporter qw(import);
our $VERSION = 2.00;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw();
my $agent = "ConfigServer";
my $option = 1;
my $proxy = "";
my $config = ConfigServer::Config->loadconfig();
my %config = $config->config();
$SIG{PIPE} = 'IGNORE';
# end main
###############################################################################
# start new
sub new {
my $class = shift;
$option = shift;
$agent = shift;
$proxy = shift;
my $self = {};
bless $self,$class;
if ($option == 3) {
return $self;
}
elsif ($option == 2) {
eval ('use LWP::UserAgent;'); ##no critic
if ($@) {return undef}
}
else {
eval {
local $SIG{__DIE__} = undef;
eval ('use HTTP::Tiny;'); ##no critic
};
}
return $self;
}
# end new
###############################################################################
# start urlget
sub urlget {
my $self = shift;
my $url = shift;
my $file = shift;
my $quiet = shift;
my $status;
my $text;
if (!defined $url) {carp "url not specified"; return}
if ($option == 3) {
($status, $text) = &binget($url,$file,$quiet);
}
elsif ($option == 2) {
($status, $text) = &urlgetLWP($url,$file,$quiet);
}
else {
($status, $text) = &urlgetTINY($url,$file,$quiet);
}
return ($status, $text);
}
# end urlget
###############################################################################
# start urlgetTINY
sub urlgetTINY {
my $url = shift;
my $file = shift;
my $quiet = shift;
my $status = 0;
my $timeout = 1200;
if ($proxy eq "") {undef $proxy}
my $ua = HTTP::Tiny->new(
'agent' => $agent,
'timeout' => 300,
'proxy' => $proxy
);
my $res;
my $text;
($status, $text) = eval {
local $SIG{__DIE__} = undef;
local $SIG{'ALRM'} = sub {die "Download timeout after $timeout seconds"};
alarm($timeout);
if ($file) {
local $|=1;
my $expected_length;
my $bytes_received = 0;
my $per = 0;
my $oldper = 0;
open (my $OUT, ">", "$file\.tmp") or return (1, "Unable to open $file\.tmp: $!");
flock ($OUT, LOCK_EX);
binmode ($OUT);
$res = $ua->request('GET', $url, {
data_callback => sub {
my($chunk, $res) = @_;
$bytes_received += length($chunk);
unless (defined $expected_length) {$expected_length = $res->{headers}->{'content-length'} || 0}
if ($expected_length) {
my $per = int(100 * $bytes_received / $expected_length);
if ((int($per / 5) == $per / 5) and ($per != $oldper) and !$quiet) {
print "...$per\%\n";
$oldper = $per;
}
} else {
unless ($quiet) {print "."}
}
print $OUT $chunk;
}
});
close ($OUT);
unless ($quiet) {print "\n"}
} else {
$res = $ua->request('GET', $url);
}
alarm(0);
if ($res->{success}) {
if ($file) {
rename ("$file\.tmp","$file") or return (1, "Unable to rename $file\.tmp to $file: $!");
return (0, $file);
} else {
return (0, $res->{content});
}
} else {
my $reason = $res->{reason};
if ($res->{status} == 599) {$reason = $res->{content}}
($status, $text) = &binget($url,$file,$quiet,$reason);
return ($status, $text);
}
};
alarm(0);
if ($@) {return (1, $@)}
return ($status,$text);
}
# end urlgetTINY
###############################################################################
# start urlgetLWP
sub urlgetLWP {
my $url = shift;
my $file = shift;
my $quiet = shift;
my $status = 0;
my $timeout = 300;
my $ua = LWP::UserAgent->new;
$ua->agent($agent);
$ua->timeout(30);
if ($proxy ne "") {$ua->proxy([ 'http', 'https' ], $proxy)}
#use LWP::ConnCache;
#my $cache = LWP::ConnCache->new;
#$cache->total_capacity([1]);
#$ua->conn_cache($cache);
my $req = HTTP::Request->new(GET => $url);
my $res;
my $text;
($status, $text) = eval {
local $SIG{__DIE__} = undef;
local $SIG{'ALRM'} = sub {die "Download timeout after $timeout seconds"};
alarm($timeout);
if ($file) {
local $|=1;
my $expected_length;
my $bytes_received = 0;
my $per = 0;
my $oldper = 0;
open (my $OUT, ">", "$file\.tmp") or return (1, "Unable to open $file\.tmp: $!");
flock ($OUT, LOCK_EX);
binmode ($OUT);
$res = $ua->request($req,
sub {
my($chunk, $res) = @_;
$bytes_received += length($chunk);
unless (defined $expected_length) {$expected_length = $res->content_length || 0}
if ($expected_length) {
my $per = int(100 * $bytes_received / $expected_length);
if ((int($per / 5) == $per / 5) and ($per != $oldper) and !$quiet) {
print "...$per\%\n";
$oldper = $per;
}
} else {
unless ($quiet) {print "."}
}
print $OUT $chunk;
});
close ($OUT);
unless ($quiet) {print "\n"}
} else {
$res = $ua->request($req);
}
alarm(0);
if ($res->is_success) {
if ($file) {
rename ("$file\.tmp","$file") or return (1, "Unable to rename $file\.tmp to $file: $!");
return (0, $file);
} else {
return (0, $res->content);
}
} else {
($status, $text) = &binget($url,$file,$quiet,$res->message);
return ($status, $text);
}
};
alarm(0);
if ($@) {
return (1, $@);
}
if ($text) {
return ($status,$text);
} else {
return (1, "Download timeout after $timeout seconds");
}
}
# end urlget
###############################################################################
# start binget
sub binget {
my $url = shift;
my $file = shift;
my $quiet = shift;
my $errormsg = shift;
$url = "'$url'";
my $cmd;
if (-e $config{CURL}) {
$cmd = $config{CURL}." -skLf -m 120";
if ($file) {$cmd = $config{CURL}." -kLf -m 120 -o";}
}
elsif (-e $config{WGET}) {
$cmd = $config{WGET}." -qT 120 -O-";
if ($file) {$cmd = $config{WGET}." -T 120 -O"}
}
if ($cmd ne "") {
if ($file) {
my ($childin, $childout);
my $cmdpid = open3($childin, $childout, $childout, $cmd." $file\.tmp $url");
my @output = <$childout>;
waitpid ($cmdpid, 0);
unless ($quiet and $option != 3) {
print "Using fallback [$cmd]\n";
print @output;
}
if (-e "$file\.tmp") {
rename ("$file\.tmp","$file") or return (1, "Unable to rename $file\.tmp to $file: $!");
return (0, $file);
} else {
if ($option == 3) {
return (1, "Unable to download: ".$cmd." $file\.tmp $url".join("",@output));
} else {
return (1, "Unable to download: ".$errormsg);
}
}
} else {
my ($childin, $childout);
my $cmdpid = open3($childin, $childout, $childout, $cmd." $url");
my @output = <$childout>;
waitpid ($cmdpid, 0);
if (scalar @output > 0) {
return (0, join("",@output));
} else {
if ($option == 3) {
return (1, "Unable to download: [$cmd $url]".join("",@output));
} else {
return (1, "Unable to download: ".$errormsg);
}
}
}
}
if ($option == 3) {
return (1, "Unable to download (CURL/WGET also not present, see csf.conf)");
} else {
return (1, "Unable to download (CURL/WGET also not present, see csf.conf): ".$errormsg);
}
}
# end binget
###############################################################################
1;

1042
csf/ConfigServer/cseUI.pm Normal file

File diff suppressed because it is too large Load Diff