On Mon, Oct 06, 2008 at 04:09:38PM +0100,
John L <[EMAIL PROTECTED]> wrote
a message of 22 lines which said:
> I'd suggest doing a lookup for A for _domainkey.$DOMAIN, and if you
> get NOERROR (that is, 0 records but no error code), then check
> *.$DOMAIN.
I've finally used such an algorithm, but more detailed (you can find a
lot of strange things in the DNS). I attach the Python module which
tests if a domain has a wildcard.
But it does not solve completely my "find DKIM records" problem since,
now, I find the records only in non-wildcarded domains. The wildcarded
domains may be have DKIM and may be not, there is no way of finding it
in the DNS. So, it seems you cannot really survey the deployment of
DKIM by just asking the DNS (unlike what happens with SPF).
Thanks for all those who helped.
#!/usr/bin/python
"""
Tries to detect if a DNS domain uses DNS wildcards (records that
match every name queried) or not.
Code and bugs by Stephane Bortzmeyer <[EMAIL PROTECTED]>
Advices by Joe Abley <[EMAIL PROTECTED]>
Licence: do what you want, except if you work for Verisign, in which
case contact the author for a specifically designed licence.
The algorithm is a bit complicated because the DNS is a complicated
world and almost every corner case is sure to happen one day or the
other. So, the obvious algorithm (ask for the star) does not always
work. That's also why there is an option 'analysis_depth' which
allows the user to specify the level of "false positives" that is acceptable.
Algorithm, with the maximum 'analysis_depth':
1) Query a A record for the star (*). (.BZ used to reply to star but
not to random names)
2) Query a A record for one (or more if there was no reply to the
star query above) random name (only LDH, 30 to 40
characters). If there is NXDOMAIN or NOERROR with no answers
(stupid but .VA does it), yield "no wildcard"
3) Query a A record for another random name and check the reply
is the same as before. Otherwise, yield"no wildcard"
4) If all A record sets matches (some TLD have a set of records,
unlike what Verisign did for .COM), yield "wildcard"
5) Otherwise, yield "no wildcard"
$Id: wildcards.py,v 1.2 2003-11-14 12:55:21 bortzmeyer Exp $
"""
import re
import sys
import os
import string
import random
import time
import getopt
# www.dnspython.org
import dns.resolver
# Defaults
qtype = 'A'
number_of_random_tests = 3
analysis_depth = 3
number_of_random_tests = 3
number_of_tests = 2 * number_of_random_tests
class Wildcards:
# Defaults
min_domain_length = 40
max_domain_length = 55
verbosity = 0
def __init__ (self, addresses = None):
self.generator = random.Random (time.time())
self.myresolver = dns.resolver.Resolver()
if addresses is not None and addresses != []:
self.myresolver.nameservers = addresses
def random_name (self):
""" Returns a legal DNS name, choosen randomly. """
number = self.generator.randrange (self.min_domain_length,
self.max_domain_length)
name = ""
for i in range (number):
name = name + chr (self.generator.randrange (65, 89))
# Only Letters
return name
def test_name (self, qname, qtype='A'):
""" Test if there is a DNS record for this name and this
type. Returns the values if there are some (or if there is no
answer for this QTYPE but no error either) and None otherwise."""
values = []
if self.verbosity:
print >>sys.stderr, ("Testing a %s record in %s..." % (qtype, qname))
try:
result = self.myresolver.query (qname, qtype)
except dns.resolver.NXDOMAIN:
if self.verbosity > 3:
print >>sys.stderr, "No such domain %s" % qname
return None
except dns.resolver.NoAnswer:
if self.verbosity > 3:
print >>sys.stderr, "No answer for %s (rr type %s)" % (qname, qtype)
return values # Empty array, not None
except (dns.resolver.NoNameservers, dns.resolver.Timeout):
if self.verbosity > 3:
print >>sys.stderr, "Zone %s broken" % qname
raise
if qtype == 'A':
values = [a.address for a in result]
else:
values = [a for a in result]
return values
def has_wildcard (self, domain, type='A'):
""" Test if the domain uses DNS wildcards and return the
values if so (it may be an empty array, if there is no data
for this resource record type), and None otherwise."""
records_needed = 0
replies_to_star = False
records_star = self.test_name ("*." + domain, type)
if records_star is not None:
if self.verbosity:
print >>sys.stderr, ("%s has probably a %s wildcard (replies to star)" % (domain, type))
records_star.sort()
replies_to_star = True
if analysis_depth == 0:
if records_star:
return records_star
else:
return None
records_needed = number_of_random_tests # If they do not reply to
# star requests (to evade detection?), we need
# more certainty
records_array = []
for i in range (records_needed):
random_domain = self.random_name() + "." + domain
records_random = self.test_name (random_domain, type)
if records_random is not None:
if self.verbosity:
print >>sys.stderr, ("%s has probably a %s wildcard" % (domain, type))
records_random.sort()
records_array.append (records_random)
else:
return None
if replies_to_star:
if records_array[0] == records_star:
if self.verbosity:
print >>sys.stderr, ("%s has regular %s wildcards" % (domain, type))
return records_star
else:
wildcard_seen = 0
records_random = records_array[0]
for i in range (records_needed-1):
if records_random != records_array[i+1]:
if self.verbosity:
print >>sys.stderr, ("%s has different data for type %s" % (domain, type))
return None
if self.verbosity:
print >>sys.stderr, ("%s has %s wildcards (but does not reply to star)" % (domain, type))
return records_random
if self.verbosity:
print >>sys.stderr, ("%s does not really have %s wildcards" % (domain, type))
print records_random
print records_array
print records_star
return None
def usage():
print >>sys.stderr, ("Usage: %s [-v N] [-r addr1,addr2 ] [-t qtype] zone\n" % sys.argv[0])
if __name__ == '__main__':
resolvers = []
try:
optlist, args = getopt.getopt (sys.argv[1:], "v:t:r:",
["verbosity=", "type=", "resolvers="])
for option, value in optlist:
if option == "--verbosity" or option == "-v":
checker.verbosity = int(value)
elif option == "--type" or option == "-t":
qtype = string.upper(value)
elif option == "--resolvers" or option == "-r":
resolvers = string.split(value, ',')
except getopt.error, reason:
print >>sys.stderr, ("%s\n" % reason)
usage()
sys.exit(1)
if len(args) < 1:
print >>sys.stderr, ("Not enough arguments\n")
usage()
sys.exit(1)
checker = Wildcards(resolvers)
for domain in args:
records = checker.has_wildcard (domain, type=qtype)
if records is not None and len(records) == 0:
print "%s has wildcards but no data for type %s" % (domain, qtype)
elif records is not None:
print "%s has %s wildcards (%s)" % (domain, qtype, str(records))
else:
print "%s does not have %s wildcards" % (domain, qtype)
_______________________________________________
NOTE WELL: This list operates according to
http://mipassoc.org/dkim/ietf-list-rules.html