]> git.ipfire.org Git - ipfire-2.x.git/commitdiff
dnsbl.cgi: Alllow to specify custom allowed or blocked domains
authorStefan Schantl <stefan.schantl@ipfire.org>
Sun, 25 Jan 2026 09:26:17 +0000 (10:26 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 6 Mar 2026 14:04:26 +0000 (14:04 +0000)
Signed-off-by: Stefan Schantl <stefan.schantl@ipfire.org>
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
html/cgi-bin/dnsbl.cgi [changed mode: 0755->0644]
langs/de/cgi-bin/de.pl
langs/en/cgi-bin/en.pl

old mode 100755 (executable)
new mode 100644 (file)
index 7b3a109..8f2b106
@@ -35,14 +35,22 @@ my %color = ();
 my %mainsettings = ();
 my %settings = ();
 my %cgiparams = ();
+my %custom_domains = ();
 my $dnsbl;
 
+# Arrays which contain the custom defined domain names.
+my @custom_allowed_domains = ();
+my @custom_blocked_domains = ();
+
 # File which contains the available filtering categories.
 my $dnsbl_json_file = "${General::swroot}/dns/dnsbl.json";
 
 # File wich contains the configured filtering rules.
 my $settings_file = "${General::swroot}/dns/dnsbl";
 
+# File which contains the elements of the custom allow and block lists.
+my $custom_domains_file = "${General::swroot}/dns/custom_domains";
+
 # Read-in main settings, for language, theme and colors.
 &General::readhash("${General::swroot}/main/settings", \%mainsettings);
 &General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color);
@@ -155,6 +163,86 @@ if ($cgiparams{'ACTION'} eq "$Lang::tr{'save'}") {
 
        # Reload Unbound
        &General::system("/usr/local/bin/unboundctrl", "reload");
+
+# Save changed custom domains to allow or block
+} elsif ($cgiparams{'CUSTOM_DOMAINS'} eq "$Lang::tr{'save'}") {
+       my @cgi_allowed_domains;
+       my @cgi_blocked_domains;
+
+       # Get the current configured custom domains to allow or block
+       &readsettings("$custom_domains_file", \%custom_domains) if (-f "$custom_domains_file");
+
+       # Grab custom configured domains and assign them to the corresponding arrays.
+       @custom_allowed_domains = @{ $custom_domains{"CUSTOM_ALLOWED_DOMAINS"} } if ($custom_domains{"CUSTOM_ALLOWED_DOMAINS"});
+       @custom_blocked_domains = @{ $custom_domains{"CUSTOM_BLOCKED_DOMAINS"} } if ($custom_domains{"CUSTOM_BLOCKED_DOMAINS"});
+
+       # Assign the posted domains from cgi to the corresponding arrays.
+       @cgi_allowed_domains = split(/\s+/, $cgiparams{"CUSTOM_ALLOWED_DOMAINS"});
+       @cgi_blocked_domains = split(/\s+/, $cgiparams{"CUSTOM_BLOCKED_DOMAINS"});
+
+       # Remove any duplicate entries from the arrays.
+       @cgi_allowed_domains = &General::uniq(@cgi_allowed_domains);
+       @cgi_blocked_domains = &General::uniq(@cgi_blocked_domains);
+
+       # Merge temporary merge both arrays for duplicate and valid check.
+       my @merged = (@cgi_allowed_domains, @cgi_blocked_domains);
+
+       # Check if there are duplicate entries on the merged list.
+       # This assumes a domain which has been entered on both
+       my $dup = &check_for_duplicates(@merged);
+
+       # If a duplicate has been found, raise an error.
+       if($dup) {
+               $errormessage = "$dup - $Lang::tr{'dnsbl error domain specified twice'}";
+       }
+
+       # Check for valid domains.
+       #
+       unless ($errormessage) {
+               # Loop through the arrays and check for valid domains and duplicates.
+               foreach my $domain (@merged) {
+                       # Check if the domain is valid.
+                       unless(&General::validdomainname($domain)) {
+                               $errormessage = "$domain - $Lang::tr{'invalid domain name'}";
+
+                               # Stop loop.
+                               last;
+                       }
+               }
+       }
+
+       unless ($errormessage) {
+               # Check if a domain from the posted blocked domains array is allready part of the saved allowed domains
+               # array.
+               $dup = &compare_arrays(\@custom_allowed_domains, \@cgi_blocked_domains);
+
+               if ($dup) {
+                       $errormessage = "$dup - $Lang::tr{'dnsbl error domain specified twice'}";
+               }
+       }
+
+       unless ($errormessage) {
+               # Check if a domain from the posted allowed domains array is allready part of the saved blocked domains
+               # array.
+               $dup = &compare_arrays(\@custom_blocked_domains, \@cgi_allowed_domains);
+
+               if ($dup) {
+                       $errormessage = "$dup - $Lang::tr{'dnsbl error domain specified twice'}";
+               }
+       }
+
+       unless ($errormessage) {
+               my %tmp;
+
+               # Assign the allowed and blocked domain arrays to the temporary hash.
+               $tmp{"CUSTOM_ALLOWED_DOMAINS"} = [ @cgi_allowed_domains ];
+               $tmp{"CUSTOM_BLOCKED_DOMAINS"} = [ @cgi_blocked_domains ];
+
+               # Save the domains.
+               &writesettings("$custom_domains_file", \%tmp);
+
+               # XXX - Add code to alter the conf and restart unbound.
+       }
 }
 
 &Header::openpage($Lang::tr{"dnsbl dns firewall"}, 1, '');
@@ -181,6 +269,13 @@ sub show_mainpage() {
        # Read-in settings file
        &readsettings("$settings_file", \%settings);
 
+       # Read-in custom allow and blocklist file.
+       &readsettings("$custom_domains_file", \%custom_domains) if (-f "$custom_domains_file");
+
+       # Grab the list elements and assign them to the corresponding arrays.
+       @custom_allowed_domains = @{ $custom_domains{"CUSTOM_ALLOWED_DOMAINS"} } if ($custom_domains{"CUSTOM_ALLOWED_DOMAINS"});
+       @custom_blocked_domains = @{ $custom_domains{"CUSTOM_BLOCKED_DOMAINS"} } if ($custom_domains{"CUSTOM_BLOCKED_DOMAINS"});
+
        &Header::openbox('100%', 'center', $Lang::tr{"dnsbl lists"});
 
 print <<END;
@@ -230,6 +325,49 @@ print <<END;
 END
 
        &Header::closebox();
+
+       # Section for custom allow and blocklist.
+       &Header::openbox('100%', 'center', $Lang::tr{"settings"});
+
+print <<END;
+       <form method='post' action='$ENV{'SCRIPT_NAME'}'>
+       <table width='100%'>
+               <tr>
+                       <td>
+                               <strong>$Lang::tr{"urlfilter allowed domains"}</strong>
+                       </td>
+
+                       <td width="5%">
+
+                       <td>
+                               <strong>$Lang::tr{"urlfilter blocked domains"}</strong>
+                       </td>
+               </tr>
+
+               <tr>
+                       <td>
+                               <textarea name='CUSTOM_ALLOWED_DOMAINS' rows='9' placeholder='example.com\nsub.exmaple.com'
+                               >@{[ join("\n", @custom_allowed_domains) ]}</textarea>
+                       </td>
+
+                       <td>
+                       </td>
+
+                       <td>
+                               <textarea name='CUSTOM_BLOCKED_DOMAINS' rows='9' placeholder='example.com\nsub.example.com'
+                               >@{[ join("\n", @custom_blocked_domains) ]}</textarea>
+                       </td>
+               </tr>
+       </table>
+
+       <br>
+       <div align='right'>
+               <input type='submit' name='CUSTOM_DOMAINS' value='$Lang::tr{'save'}'>
+       </div>
+       </form>
+END
+
+       &Header::closebox();
 }
 
 #
@@ -370,3 +508,41 @@ sub get_list($) {
 
        return undef;
 }
+
+sub check_for_duplicates (@) {
+       my @array = @_;
+       my $lastelement;
+
+       # Sort and loop through the given array.
+       foreach my $element (sort(@array)) {
+               # Check if the current element is the same than the last one.
+               return $element if ($element eq $lastelement);
+
+               # Store last processed element.
+               $lastelement = $element;
+       }
+
+       return;
+}
+
+sub compare_arrays (\@\@) {
+       my ($data, $test) = @_;
+
+       my @data = @{ $data };
+       my @test = @{ $test };
+
+       # Early exit if there are no entries in one of the given arrays.
+       return unless (@data);
+       return unless (@test);
+
+
+       # Loop through the content of the test array and check
+       # if the current processed element is part of the data array.
+       foreach my $element (@test) {
+               if (grep(/$element/, @data)) {
+                       return "$element"
+               }
+       }
+
+       return;
+}
index 4cc14f7e027042b94d2968c23cb260de709f2ca1..2ccb720b9876759d7d199cc135fa34a79bdb67a9 100644 (file)
 'dnsbl acl' => 'Zugriffskontrolle',
 'dnsbl custom source' => 'Benutzerdefinierte Quelle',
 'dnsbl dns firewall' => 'DNS-Firewall',
+'dnsbl error domain specified twice' => 'Die Domain kann nicht gleichzeitig erlaubt und geblockt sein.',
 'dnsbl lists' => 'Listen',
 'dnsforward' => 'DNS-Weiterleitung',
 'dnsforward add a new entry' => 'Neuen Eintrag hinzufügen',
index b23a73d70626e36f852306b9f7758624505a97ae..3e1d4b72209582231bb34c907eeabfffadefb719 100644 (file)
 'dnsbl acl' => 'Access Control',
 'dnsbl custom source' => 'Custom source',
 'dnsbl dns firewall' => 'DNS Firewall',
+'dnsbl error domain specified twice' => 'The domain cannot be allowed and blocked at the same time.',
 'dnsbl lists' => 'Lists',
 'dnsforward' => 'DNS Forwarding',
 'dnsforward add a new entry' => 'Add a new entry',