Index: names/client.py
===================================================================
--- names/client.py	(revision 1)
+++ names/client.py	(working copy)
@@ -71,7 +71,12 @@
     protocol = property(_getProtocol)
 
 
-    def __init__(self, resolv=None, servers=None, timeout=(1, 3, 11, 45), reactor=None):
+    def __init__(self, 
+                 resolv=None, 
+                 servers=None, 
+                 timeout=(1, 3, 11, 45), 
+                 reactor=None, 
+                 dnssecConfig=None):
         """
         Construct a resolver which will query domain name servers listed in
         the C{resolv.conf(5)}-format file given by C{resolv} as well as
@@ -98,10 +103,12 @@
             L{IReactorTCP} which will be used to establish connections, listen
             for DNS datagrams, and enforce timeouts.  If not provided, the
             global reactor will be used.
+            
+        @param dnssecConfig: A provider of DNSSEC parameters - see common.DnssecConfig()
 
         @raise ValueError: Raised if no nameserver addresses can be found.
         """
-        common.ResolverBase.__init__(self)
+        common.ResolverBase.__init__(self, dnssecConfig)
 
         if reactor is None:
             from twisted.internet import reactor
@@ -129,7 +136,6 @@
 
         self.maybeParseConfig()
 
-
     def __getstate__(self):
         d = self.__dict__.copy()
         d['connections'] = []
@@ -370,9 +376,17 @@
             return self.queryTCP(message.queries).addCallback(self.filterAnswers)
         if message.rCode != dns.OK:
             return failure.Failure(self.exceptionForCode(message.rCode)(message))
-        return (message.answers, message.authority, message.additional)
+        
+        #if edns0 is enabled, return a reference to the message as the 4th member of 
+        #the tuple, so the caller can access AD. You could just return AD, but if you
+        #return message, the caller can also access a reference to the query and you
+        #never know when that might come in handy.
+        if self.dnssecConfig.ednsEnabled:
+            return (message.answers, message.authority, message.additional, message)
+        else:
+            #not edns0Enabled - return a legacy 3-tuple
+            return (message.answers, message.authority, message.additional)
 
-
     def _lookup(self, name, cls, type, timeout):
         """
         Build a L{dns.Query} for the given parameters and dispatch it via UDP.
@@ -942,3 +956,18 @@
     @rtype: C{Deferred}
     """
     return getResolver().lookupNamingAuthorityPointer(name, timeout)
+
+def lookupDNSKey(name, timeout=None):
+    """
+    DNSKEY lookup.    
+
+    @type name: C{str}
+    @param name: DNS name to resolve.
+
+    @type timeout: Sequence of C{int}
+    @param timeout: Number of seconds after which to reissue the query.
+        When the last timeout expires, the query is considered failed.
+
+    @rtype: C{Deferred}
+    """
+    return getResolver().lookupDNSKey(name, timeout)
\ No newline at end of file
Index: names/common.py
===================================================================
--- names/common.py	(revision 1)
+++ names/common.py	(working copy)
@@ -18,6 +18,65 @@
 
 EMPTY_RESULT = (), (), ()
 
+class DnssecConfig():
+    """
+    Sets recDes and the DNSSEC parameters. See RFC's 4033, 4034 and 4035.
+
+    recDes (RD)   - Recursion Desired. Not a DNSSEC parameter and does not require ednsEnabled,
+                    but still nice to be able to control whether or not you're asking for recursion.
+                    (bool)
+
+    ednsEnabled   - EDNS Enabled adds an OPT record to the query. The OPT record contains a
+                    version field that indicates the version of EDNS that you can handle.
+                    Presently, only EDNS Version 0 is defined.
+                    (bool)
+                    
+    maxUdpPktSz   - The max size UDP packet (bytes) that your end-to-end network can handle
+                    (on a modern network a reliable size is 1492, although up to 65535 is possible).
+                    Requires ednsEnabled.
+                    (int)
+                    
+    dnssecOK (DO) - Dnssec Ok. A bit that indicates you want DNSSEC RR's and validation 
+                    if the resolver validates. If DO is set, AD will be set in the response 
+                    if the answer validates. Requires ednsEnabled.
+                    (bool)
+
+    chkDis (CD)   - Checking Disabled. If DO and CD are set, a validating resolver won't do
+                    validation but will return the DNSSEC RR's so that YOU can.
+                    (bool)
+                    
+    version       - sets the edns version level. Currently, only 0 is defined and supported.
+                    (int)
+                    
+    authData (AD) - Authentic Data. If set in a response from a validating resolver that you can
+                    trust, indicates that the validating resolver validated the answer. Requires 
+                    ednsEnabled and DO set. (See note below.)
+                    (bool)
+                    
+    Note - RFC-4035 does not define AD for a query. However, most validating resolvers seem 
+    to honor it set in a query and return AD set in a response to a query that validates. But 
+    you should not to rely on this behavior - set DO instead.
+    """
+    def __init__(self, 
+                 recDes=True,
+                 ednsEnabled=False, 
+                 maxUdpPktSz=512, 
+                 dnssecOk=False,
+                 chkDis=False,
+                 version=0,
+                 authData=False):
+
+        self.recDes = recDes
+        self.ednsEnabled = ednsEnabled
+        self.maxUdpPktSz = maxUdpPktSz
+        self.dnssecOk = dnssecOk
+        self.chkDis = chkDis
+        self.version = version
+        self.authData = authData
+
+        assert not self.ednsEnabled or 512 <= self.maxUdpPktSz <= 65535
+        assert version == 0
+
 class ResolverBase:
     """
     L{ResolverBase} is a base class for L{IResolver} implementations which
@@ -36,12 +95,14 @@
 
     typeToMethod = None
 
-    def __init__(self):
+    def __init__(self, dnssecConfig=None):
         self.typeToMethod = {}
         for (k, v) in typeToMethod.items():
             self.typeToMethod[k] = getattr(self, v)
+        self.dnssecConfig = dnssecConfig
+        if self.dnssecConfig == None:
+            self.dnssecConfig = DnssecConfig()
 
-
     def exceptionForCode(self, responseCode):
         """
         Convert a response code (one of the possible values of
@@ -200,6 +261,12 @@
         @see: twisted.names.client.lookupAllRecords
         """
         return self._lookup(name, dns.IN, dns.ALL_RECORDS, timeout)
+    
+    def lookupDNSKey(self, name, timeout=None):
+        """
+        @see: twisted.names.client.lookupDNSKey
+        """
+        return self._lookup(name, dns.IN, dns.DNSKEY, timeout)
 
     def getHostByName(self, name, timeout = None, effort = 10):
         """
@@ -268,6 +335,7 @@
     dns.MX:    'lookupMailExchange',
     dns.TXT:   'lookupText',
     dns.SPF:   'lookupSenderPolicy',
+    dns.DNSKEY:'lookupDNSKey',
 
     dns.RP:    'lookupResponsibility',
     dns.AFSDB: 'lookupAFSDatabase',
Index: names/dns.py
===================================================================
--- names/dns.py	(revision 15)
+++ names/dns.py	(working copy)
@@ -2503,8 +2503,14 @@
             query, or errbacked with any errors that could happen (exceptions
             during writing of the query, timeout errors, ...).
         """
-        m = Message(id, recDes=1)
+        m = Message(id, recDes=self.controller.dnssecConfig.recDes)
         m.queries = queries
+        if self.controller.dnssecConfig.ednsEnabled:
+            authData=self.controller.dnssecConfig.authData
+            chkDis=self.controller.dnssecConfig.chkDis
+            m.additional = [Record_OPT(payload_size = self.controller.dnssecConfig.maxUdpPktSz,
+                                       version = self.controller.dnssecConfig.version,
+                                       dnssecOk = self.controller.dnssecConfig.dnssecOk)]
 
         try:
             writeMessage(m)
@@ -2558,7 +2564,10 @@
         self.transport.write(message.toStr(), address)
 
     def startListening(self):
-        self._reactor.listenUDP(0, self, maxPacketSize=512)
+        maxPacketSize = 512
+        if self.controller.dnssecConfig.ednsEnabled:
+            maxPacketSize = self.controller.dnssecConfig.maxUdpPktSz
+        self._reactor.listenUDP(0, self, maxPacketSize=maxPacketSize)
 
     def datagramReceived(self, data, addr):
         """
Index: names/test/test_client.py
===================================================================
--- names/test/test_client.py	(revision 1)
+++ names/test/test_client.py	(working copy)
@@ -642,7 +642,15 @@
         d.addCallback(self.checkResult, dns.NAPTR)
         return d
 
+    def test_lookupDNSKey(self):
+        """
+        See L{test_lookupAddress}
+        """
+        d = client.lookupDNSKey(self.hostname)
+        d.addCallback(self.checkResult, dns.DNSKEY)
+        return d
 
+
 class ThreadedResolverTests(unittest.TestCase):
     """
     Tests for L{client.ThreadedResolver}.
Index: names/test/test_names.py
===================================================================
--- names/test/test_names.py	(revision 1)
+++ names/test/test_names.py	(working copy)
@@ -93,13 +93,18 @@
                            '\x12\x01\x16\xfe\xc1\x00\x01'),
             dns.Record_NAPTR(100, 10, "u", "sip+E2U",
                              "!^.*$!sip:information@domain.tld!"),
-            dns.Record_AAAA('AF43:5634:1294:AFCB:56AC:48EF:34C3:01FF')],
+            dns.Record_AAAA('AF43:5634:1294:AFCB:56AC:48EF:34C3:01FF'),
+            dns.Record_DNSKEY(0x10, 3, 5, 
+                              "M+u8Uxm2y2Q142qED0kVNIiSOHBkfiU3xBhMq9H4T/K+oeC7Y81HIOFEh9k6ZS/Ba5X0/Fr1yyq5Z/+0/Q845Kya8Lmkp/ikJVe/9id2TC2hoffpZ9pbZRjIeBTAvdTboGmGuqG/ljnDLJrJpoF6g8g6fHR9eekIWis8LJ55Y1k="
+                             )],
         'http.tcp.test-domain.com': [
             dns.Record_SRV(257, 16383, 43690, 'some.other.place.fool')
         ],
         'host.test-domain.com': [
             dns.Record_A('123.242.1.5'),
             dns.Record_A('0.255.0.255'),
+            dns.Record_RRSIG(dns.A, 5, 3, 86400, '20120101000000', '20120201000000', 2642, 'test-domain.com', 
+                            "M+u8Uxm2y2Q142qED0kVNIiSOHBkfiU3xBhMq9H4T/K+oeC7Y81HIOFEh9k6ZS/Ba5X0/Fr1yyq5Z/+0/Q845Kya8Lmkp/ikJVe/9id2TC2hoffpZ9pbZRjIeBTAvdTboGmGuqG/ljnDLJrJpoF6g8g6fHR9eekIWis8LJ55Y1k=")
         ],
         'host-two.test-domain.com': [
 #
@@ -429,9 +434,15 @@
             [dns.Record_NAPTR(100, 10, "u", "sip+E2U",
                               "!^.*$!sip:information@domain.tld!",
                               ttl=19283784)])
+    
+    def test_DNSKEY(self):
+        """Test DNS 'DNSKEY' record queries."""
+        return self.namesTest(
+            self.resolver.lookupDNSKey('test-domain.com'),
+            [dns.Record_DNSKEY(0x10, 3, 5, 
+                               "M+u8Uxm2y2Q142qED0kVNIiSOHBkfiU3xBhMq9H4T/K+oeC7Y81HIOFEh9k6ZS/Ba5X0/Fr1yyq5Z/+0/Q845Kya8Lmkp/ikJVe/9id2TC2hoffpZ9pbZRjIeBTAvdTboGmGuqG/ljnDLJrJpoF6g8g6fHR9eekIWis8LJ55Y1k=",
+                               ttl=19283784)])
 
-
-
 class DNSServerFactoryTests(unittest.TestCase):
     """
     Tests for L{server.DNSServerFactory}.
