Can IP tables allow specific DNS queries based on domain name?

I have iptables blocking all UDP traffic at the moment, however I want to allow only certain DNS queries to get through.

Let's use google.com as an example.

I am trying to use string matching to find the domain name in the request, and allow it. This is what I came up with.

iptables -A OUTPUT -o eth0 -p udp --sport 53 -m string --string "google.com" --algo bm -j ACCEPT

I have also tried --dport 53 instead of --sport. No dice.

If anyone knows how this can be done or see's where I went wrong?


Solution 1:

the dot "." in a DNS query is not represented as a character, but as the length of the string that follows. For example www.google.com is queried as

0x03 w w w 0x06 g o o g l e 0x03 c o m

you can easily allow/block DNS queries by matching the domain names with --hex-string. In your case:

-m string --algo bm --hex-string '|06 676f6f676c65 03 636f6d|' -j ACCEPT

will accept every DNS packet containing ".google.com".

I often use this technique against the DNS query amplification attack.

source: DNS RFC 1035

Solution 2:

To complement nrc's anwser, where is a quick command to convert domains to the hexadecimal string:

DOMAIN=google.com
perl -e 'print map {chr(length($_)).$_} split /\./, "'$DOMAIN'" | xxd -p

So, in your case:

DOMAIN=google.com
HEX=$(perl -e 'print map {chr(length($_)).$_} split /\./, "'$DOMAIN'"' | xxd -p)

iptables -A OUTPUT -o eth0 -p udp --sport 53 \
  -m string --hex-string "|$HEX|" --algo bm -j ACCEPT