#!/usr/bin/perl # vi: et sts=4 sw=4 ts=4 ############################################################################### # You Don't Need Pi-hole # Network-wide DNS blocking without extra hardware. # # Project URL: https://codeberg.org/h3xx/you-dont-need-pihole # # License GPLv3: GNU GPL version 3.0 (https://www.gnu.org/licenses/gpl-3.0.html) # with Commons Clause 1.0 (https://commonsclause.com/). # This is free software: you are free to change and redistribute it. # There is NO WARRANTY, to the extent permitted by law. # You may NOT use this software for commercial purposes. ############################################################################### use strict; use warnings; use Getopt::Long qw/ GetOptions :config no_ignore_case /; use FindBin qw//; my %domains; my $dupes = 0; my $skip = 0; my $removed_allowed = 0; sub add_domain_list { my $file = shift; foreach my $line (&read_stripped($file)) { my $domain = lc $line; ++$dupes if defined $domains{$domain}; $domains{$domain} = 1; } } sub add_host_file { my $file = shift; foreach my $line (&read_stripped($file)) { my @parts = split /\s+/, $line; die "Malformed line in $file: $line; @parts" unless @parts > 1; if (lc $parts[0] eq lc $parts[1]) { ++$skip; next; } unless (lc $parts[0] eq '0.0.0.0') { ++$skip; next; } my $domain = lc $parts[1]; ++$dupes if defined $domains{$domain}; $domains{$domain} = 1; } } sub read_stripped { my $file = shift; open my $fni, '<', $file or die "Failed to open file $file for reading: $!"; map { chomp; # Strip whitespace and comments s/^\s+|\s+$|\s*#.*$//; $_ || () } <$fni>; } MAIN: { my $out; my $block_ip = '0.0.0.0 ::1'; my $workdir = $FindBin::RealBin; unless (&GetOptions( 'out=s' => \$out, 'O=s' => \$out, 'i=s' => \$block_ip, 'block-ip=s' => \$block_ip, )) { exit 2; } my @domain_lists = glob "$workdir/lists/*.domains"; my @hosts_lists = glob "$workdir/lists/*.hosts"; my @allow_lists = glob "$workdir/allowlists/*.domains"; foreach my $listfile (@domain_lists) { &add_domain_list($listfile); } foreach my $hostfile (@hosts_lists) { &add_host_file($hostfile); } # Apply allowlists my @allow_domains; foreach my $allowlist (@allow_lists) { push @allow_domains, &read_stripped($allowlist); } my $before = %domains; delete %domains{@allow_domains}; # Count number removed $removed_allowed = $before - %domains; my $written = 0; my $fho = \*STDOUT; if (defined $out && length $out) { open $fho, '>', $out or die "Failed to open file $out for writing: $!"; } my @block_ip = sort split /\s+/, $block_ip; print $fho map { ++$written; my $domain = $_; map { "$_ $domain\n" } @block_ip; } sort keys %domains; printf STDERR "%d domains written to %s from\n", $written, $out; printf STDERR " - %d .domains files\n", (scalar @domain_lists); printf STDERR " - %d .hosts files\n", (scalar @hosts_lists); printf STDERR "(%d duplicates)\n", $dupes if $dupes; printf STDERR "(%d domains removed via allowlist)\n", $removed_allowed if $removed_allowed; printf STDERR "(%d skipped)\n", $skip if $skip; }