[Twisted-Python] twisted.names and multicast DNS

Tim Allen screwtape at froup.com
Sat Jul 9 03:08:15 EDT 2005

So after two years of leaving it alone, I decided to have another look 
at the multicast DNS module I was writing for Twisted, and lo and 
behold I discover a serious design flaw in my response cache. Here's 
the basic situation I'm dealing with:

Suppose I have two hosts on my network, called ford and arthur. I'm 
sitting at arthur, and I'm looking for SSH servers advertised via 
DNS-based Service Discovery and Multicast DNS. At the present moment, 
ford is switched off.

I turn on ford, and when the SSH server starts up, ford broadcasts an 
announcement DNS datagram that says, "In the zone '_ssh._tcp.local', 
there is a PTR record with the data 'Ford._ssh._tcp.local'. This 
information expires in 7200 seconds." (it mentions the class too, of 
course, but the class doesn't affect the discussion below so we'll 
ignore it from here on).

arthur receives the announcement, records the transmission time and the 
TTL, and caches the PTR record. Thereafter, any program on arthur that 
requests PTR records in '_ssh._tcp.local' will be given the PTR record 
about 'Ford._ssh._tcp.local'.

Now I turn off Ford, and when the SSH server shuts down, ford 
broadcasts a goodbye datagram that says, "In the zone 
'_ssh._tcp.local', there is a PTR record with the data 
'Ford._ssh._tcp.local'. This information expires in 0 seconds." - that 
is to say, exactly the same as the previous announcement but with a TTL 
of 0. The cache on arthur needs to compare this new record to its 
cache, see that it matches the definition of an existing record, and 
update the TTL of the existing record so that it will expire 

I'm trying to implement the record cache on arthur at the moment. It 
needs to be queried by (name, type, class) tuples, so I use such a 
tuple as the key of a dictionary. Since there can be multiple records 
available for a particular (name, type, class), the value associated 
with this key is a list of resource records. Because the 
t.names.dns.DNSDatagramProtocol gives me instances of the various 
t.names.dns.Record_* classes, I figured that those instances would be a 
good representation of the resource records I need to store.

In psuedo-python, then, the ideal data structure would look like this:

cache = {
	('_ssh._tcp.local', PTR, IN): [
			(<Record_PTR name=Ford._ssh._tcp.local>, 7200),

...then, when I get a new record from the network, I can look up its 
(name, type, class) tuple and get the list of records. Then I compare 
the new record to each of the records in the list: if it matches a 
record I update the TTL, otherwise I add the record to the list.

The problem is this: the Record_PTR class (just like all the other 
Record_* classes) in t.names.dns stores the TTL as an instance 
variable! Therefore, when I have an existing record and an updated 
record, they never match, and I wind up with two identical records in 
my data structure that differ only by TTL.

As far as I can tell, I have three options from here:

1. Write my own versions of all the Record_* classes that don't 
maintain a TTL value, and translate backwards and forwards whenever I 
need to talk to Twisted's DNS layer. I'd need to update this code 
whenever a new DNS record type was added.
2. Write a comparison function for comparing resource records that 
compares instance variables that aren't named 'ttl'. This would 
probably need to be updated less frequently, but is horribly hacky.
3. Complain on the twisted-list and hope that somebody has a better 
suggestion, or changes the code to be more mDNS-friendly.

As you can see, I'm trying Option 3 at the moment, but I'm open to 
suggestions. :)

More information about the Twisted-Python mailing list