Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
#!/usr/bin/perl -Tw
# build_iptables.pl domain_list [ domain_list... ]
#
# Create a firewall ruleset in iptables-restore format to create a layer 4 DNS
# proxy using NAT that filters domains from given lists.
# In this example:
# - 123.123.123.158 is the proxy address.
# - 123.123.123.{152,154,156} are the real DNS resolvers.
# Copyright (c) 2009 Terry Burton
#
# http://www.terryburton.co.uk
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
use strict;
die "Usage: $0 domain_list [ domain_list... ]" unless $#ARGV>=0;
my @doms=();
foreach my $filename (@ARGV) {
open(FILE,"<$filename") or die("Unable to open file");
@_=<FILE>;
close(FILE);
map {chomp} @_;
push @doms,@_;
}
print <<'EOF';
*mangle
:PREROUTING ACCEPT [0:0]
-A PREROUTING -d 123.123.123.158 -p udp -m udp --dport 53 -m statistic --mode nth --every 3 --packet 0 -m state --state new -j CONNMARK --set-mark 1
-A PREROUTING -d 123.123.123.158 -p udp -m udp --dport 53 -m statistic --mode nth --every 3 --packet 1 -m state --state new -j CONNMARK --set-mark 2
-A PREROUTING -d 123.123.123.158 -p udp -m udp --dport 53 -m statistic --mode nth --every 3 --packet 2 -m state --state new -j CONNMARK --set-mark 3
-A PREROUTING -d 123.123.123.158 -p tcp -m tcp --dport 53 -m statistic --mode random --probability 0.333333 -m state --state new -j CONNMARK --set-mark 1
-A PREROUTING -d 123.123.123.158 -p tcp -m tcp --dport 53 -m statistic --mode random --probability 0.5 -m state --state new -j CONNMARK --set-mark 2
-A PREROUTING -d 123.123.123.158 -p tcp -m tcp --dport 53 -m state --state new -j CONNMARK --set-mark 3
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -m connmark --mark 1 -j DNAT --to-destination 123.123.123.152
-A PREROUTING -m connmark --mark 2 -j DNAT --to-destination 123.123.123.154
-A PREROUTING -m connmark --mark 3 -j DNAT --to-destination 123.123.123.156
-A POSTROUTING -m connmark --mark 1 -j SNAT --to-destination 123.123.123.158
-A POSTROUTING -m connmark --mark 2 -j SNAT --to-destination 123.123.123.158
-A POSTROUTING -m connmark --mark 3 -j SNAT --to-destination 123.123.123.158
COMMIT
*filter
:LOGDROP - [0:0]
-A LOGDROP -m limit --limit 1/second --limit-burst 100 -j LOG
-A LOGDROP -j DROP
:DNSCHECK - [0:0]
:FORWARD ACCEPT [0:0]
-A FORWARD -s 123.123.0.0/16 -p udp --dport 53 -m u32 --u32 "0>>22&0x3C@8>>15&0x01=0" -j DNSCHECK
-A FORWARD -s 123.123.0.0/16 -p udp --dport 53 -j ACCEPT
-A FORWARD -s 123.123.0.0/16 -p tcp --dport 53 -j ACCEPT
-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -j LOGDROP
EOF
my $last_prefix='';
foreach my $dom (sort @doms) {
(my $prefix,my $suffix)=$dom=~/^(..)(.*)$/;
if ($prefix ne $last_prefix) {
$last_prefix=$prefix;
print ":DNSCHECK$prefix - [0:0]\n";
if ($prefix=~/^.\./) {
my $hex=sprintf("01%02lx", ord substr($prefix,0,1));
print "-A DNSCHECK -m u32 --u32 \"0>>22&0x3C\@18&0xffff=0x$hex\" -j DNSCHECK$prefix\n";
} else {
(my $hex=$prefix)=~s/(.)/sprintf("%02lx", ord $1)/eg;
print "-A DNSCHECK -m u32 --u32 \"0>>22&0x3C\@19&0xffff=0x$hex\" -j DNSCHECK$prefix\n";
}
}
my $enc=''; my $offset=40;
foreach (split /\./, $dom) {
$enc.='|'.(sprintf '%02x', length)."|$_";
$offset+=(length $_)+1;
}
print "-A DNSCHECK$prefix -m string --from 40 --to $offset --hex-string \"$enc|00|\" --algo bm -j LOGDROP\n";
}
print <<'EOF'
COMMIT
EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment