Hello community, here is the log from the commit of package python-twisted-names checked in at Thu Aug 2 23:33:05 CEST 2007. -------- --- python-twisted-names/python-twisted-names.changes 2006-10-26 18:35:40.000000000 +0200 +++ /mounts/work_src_done/STABLE/python-twisted-names/python-twisted-names.changes 2007-08-02 17:09:46.000000000 +0200 @@ -1,0 +2,8 @@ +Thu Aug 2 17:09:01 CEST 2007 - jmatejek@suse.cz + +- update to 0.4.0 + * support for DNS error handling + * fixed DoS in UDP DNS server + * minor bugfixes + +------------------------------------------------------------------- Old: ---- TwistedNames-0.3.0.tar.bz2 New: ---- TwistedNames-0.4.0.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-twisted-names.spec ++++++ --- /var/tmp/diff_new_pack.x19065/_old 2007-08-02 23:32:49.000000000 +0200 +++ /var/tmp/diff_new_pack.x19065/_new 2007-08-02 23:32:49.000000000 +0200 @@ -1,7 +1,7 @@ # -# spec file for package python-twisted-names (Version 0.3.0) +# spec file for package python-twisted-names (Version 0.4.0) # -# Copyright (c) 2006 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2007 SUSE LINUX Products GmbH, Nuernberg, Germany. # This file and all modifications and additions to the pristine # package are under the same license as the package itself. # @@ -13,7 +13,7 @@ Name: python-twisted-names BuildRequires: python-devel python-twisted Summary: Twisted Names -Version: 0.3.0 +Version: 0.4.0 Release: 1 %define tarname TwistedNames Source: %{tarname}-%{version}.tar.bz2 @@ -58,7 +58,12 @@ %defattr(-,root,root) %doc LICENSE README NEWS -%changelog -n python-twisted-names +%changelog +* Thu Aug 02 2007 - jmatejek@suse.cz +- update to 0.4.0 + * support for DNS error handling + * fixed DoS in UDP DNS server + * minor bugfixes * Thu Oct 26 2006 - jmatejek@suse.cz - update to 0.3.0 - upgrade to Twisted 2.4 install system ++++++ TwistedNames-0.3.0.tar.bz2 -> TwistedNames-0.4.0.tar.bz2 ++++++ diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/doc/examples/dns-service.py new/TwistedNames-0.4.0/doc/examples/dns-service.py --- old/TwistedNames-0.3.0/doc/examples/dns-service.py 2004-07-30 14:01:15.000000000 +0200 +++ new/TwistedNames-0.4.0/doc/examples/dns-service.py 2006-12-29 06:01:40.000000000 +0100 @@ -25,7 +25,7 @@ sys.exit(1) resolver = client.Resolver('/etc/resolv.conf') -d = resolver.lookupService('_%s._%s.%s' % (service, proto, domain), 1) +d = resolver.lookupService('_%s._%s.%s' % (service, proto, domain), [1]) d.addCallbacks(printAnswer, printFailure) reactor.run() diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/doc/examples/gethostbyname.py new/TwistedNames-0.4.0/doc/examples/gethostbyname.py --- old/TwistedNames-0.3.0/doc/examples/gethostbyname.py 2004-07-30 14:01:15.000000000 +0200 +++ new/TwistedNames-0.4.0/doc/examples/gethostbyname.py 2006-12-29 06:01:40.000000000 +0100 @@ -12,7 +12,7 @@ failure.printTraceback() reactor.stop() -gethostbyname = client.theResolver.getHostByName -gethostbyname(sys.argv[1]).addCallbacks(gotResult, gotFailure) +d = client.getHostByName(sys.argv[1]) +d.addCallbacks(gotResult, gotFailure) reactor.run() diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/doc/examples/index.html new/TwistedNames-0.4.0/doc/examples/index.html --- old/TwistedNames-0.3.0/doc/examples/index.html 2006-05-25 03:08:23.000000000 +0200 +++ new/TwistedNames-0.4.0/doc/examples/index.html 2007-01-07 03:43:17.000000000 +0100 @@ -1,2 +1,2 @@ <?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><title>Twisted Documentation: Twisted Names code examples</title><link href="../howto/stylesheet.css" type="text/css" rel="stylesheet" /></head><body bgcolor="white"><h1 class="title">Twisted Names code examples</h1><div class="toc"><ol><li><a href="#auto0">DNS (Twisted Names)</a></li></ol></div><div class="content"><span></span><h2>DNS (Twisted Names)<a name="auto0"></a></h2><ul><li><a href="testdns.py">testdns.py</a></li><li><a href="dns-service.py">dns-service.py</a></li><li><a href="gethostbyname.py">gethostbyname.py</a></li></ul></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 0.3.0</span></body></html> \ No newline at end of file + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><title>Twisted Documentation: Twisted Names code examples</title><link href="../howto/stylesheet.css" type="text/css" rel="stylesheet" /></head><body bgcolor="white"><h1 class="title">Twisted Names code examples</h1><div class="toc"><ol><li><a href="#auto0">DNS (Twisted Names)</a></li></ol></div><div class="content"><span></span><h2>DNS (Twisted Names)<a name="auto0"></a></h2><ul><li><a href="testdns.py">testdns.py</a></li><li><a href="dns-service.py">dns-service.py</a></li><li><a href="gethostbyname.py">gethostbyname.py</a></li></ul></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 0.4.0</span></body></html> \ No newline at end of file diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/doc/examples/testdns.py new/TwistedNames-0.4.0/doc/examples/testdns.py --- old/TwistedNames-0.3.0/doc/examples/testdns.py 2004-07-30 14:01:15.000000000 +0200 +++ new/TwistedNames-0.4.0/doc/examples/testdns.py 2006-07-02 05:53:32.000000000 +0200 @@ -3,7 +3,7 @@ import sys from twisted.names import client from twisted.internet import reactor -from twisted.protocols import dns +from twisted.names import dns r = client.Resolver('/etc/resolv.conf') diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/doc/howto/index.html new/TwistedNames-0.4.0/doc/howto/index.html --- old/TwistedNames-0.3.0/doc/howto/index.html 2006-05-25 03:08:22.000000000 +0200 +++ new/TwistedNames-0.4.0/doc/howto/index.html 2007-01-07 03:43:17.000000000 +0100 @@ -1,2 +1,2 @@ <?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><title>Twisted Documentation: Twisted Names Documentation</title><link href="../howto/stylesheet.css" type="text/css" rel="stylesheet" /></head><body bgcolor="white"><h1 class="title">Twisted Names Documentation</h1><div class="toc"><ol></ol></div><div class="content"><span></span><ul class="toc"><li><a href="names.html">Names DNS library</a></li></ul></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 0.3.0</span></body></html> \ No newline at end of file + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><title>Twisted Documentation: Twisted Names Documentation</title><link href="../howto/stylesheet.css" type="text/css" rel="stylesheet" /></head><body bgcolor="white"><h1 class="title">Twisted Names Documentation</h1><div class="toc"><ol></ol></div><div class="content"><span></span><ul class="toc"><li><a href="names.html">Names DNS library</a></li></ul></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 0.4.0</span></body></html> \ No newline at end of file diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/doc/howto/names.html new/TwistedNames-0.4.0/doc/howto/names.html --- old/TwistedNames-0.3.0/doc/howto/names.html 2006-05-25 03:08:23.000000000 +0200 +++ new/TwistedNames-0.4.0/doc/howto/names.html 2007-01-07 03:43:17.000000000 +0100 @@ -3,8 +3,7 @@ <ul><li>act as a recursive server, forwarding queries to other servers</li><li>perform local caching of recursively discovered records</li><li>act as the authoritative server for a domain</li></ul><h3>Creating a non-authoritative server<a name="auto0"></a></h3> The first two of these are easy, and you can create a server that performs them with the command <code class="shell">mktap dns --recursive ---cache</code>, or launch <code class="shell">tkmktap</code> and configure a -dns server with it. The result should be a file named <code>dns.tap</code>. +--cache</code>. The result should be a file named <code>dns.tap</code>. Now switch to a superuser account (if required by your platform to bind to port 53) and run <code class="shell">twistd -f dns.tap</code>. The Application will run and bind to port 53. Try performing a lookup with it, @@ -60,4 +59,4 @@ </p><p>Names can also read a traditional, BIND-syntax zone file. Specify these with the <code>--bindzone</code> parameter. The $GENERATE and $INCLUDE directives are not yet supported. -</p></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 0.3.0</span></body></html> \ No newline at end of file +</p></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 0.4.0</span></body></html> \ No newline at end of file diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/LICENSE new/TwistedNames-0.4.0/LICENSE --- old/TwistedNames-0.3.0/LICENSE 2006-04-18 04:43:54.000000000 +0200 +++ new/TwistedNames-0.4.0/LICENSE 2006-06-03 22:59:55.000000000 +0200 @@ -6,6 +6,7 @@ Bob Ippolito Canonical Limited Christopher Armstrong +David Reid Donovan Preston Eric Mangold Itamar Shtull-Trauring diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/NEWS new/TwistedNames-0.4.0/NEWS --- old/TwistedNames-0.3.0/NEWS 2006-05-22 00:28:51.000000000 +0200 +++ new/TwistedNames-0.4.0/NEWS 2007-01-06 22:50:02.000000000 +0100 @@ -1,3 +1,23 @@ +0.4.0 (2007-01-06) +================== + +Features +-------- + + - In the twisted.names client, DNS responses which represent errors + are now translated to informative exception objects, rather than + empty lists. This means that client requests which fail will now + errback their Deferreds (#2248) + +Fixes +----- + - A major DoS vulnerability in the UDP DNS server was fixed (#1708) + +Misc +---- + - #1799, #1636, #2149, #2181 + + 0.3.0 (2006-05-21) ================== diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/README new/TwistedNames-0.4.0/README --- old/TwistedNames-0.3.0/README 2006-05-22 00:28:51.000000000 +0200 +++ new/TwistedNames-0.4.0/README 2007-01-06 22:42:54.000000000 +0100 @@ -1,3 +1,3 @@ -Twisted Names 0.3.0 +Twisted Names 0.4.0 Twisted Names depends on Twisted Core. diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/twisted/names/cache.py new/TwistedNames-0.4.0/twisted/names/cache.py --- old/TwistedNames-0.3.0/twisted/names/cache.py 2006-03-17 04:29:41.000000000 +0100 +++ new/TwistedNames-0.4.0/twisted/names/cache.py 2006-07-01 18:08:17.000000000 +0200 @@ -8,7 +8,7 @@ from zope.interface import implements from twisted.names import dns -from twisted.python import failure, log, components +from twisted.python import failure, log from twisted.internet import interfaces, defer import common diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/twisted/names/client.py new/TwistedNames-0.4.0/twisted/names/client.py --- old/TwistedNames-0.3.0/twisted/names/client.py 2006-03-17 04:29:41.000000000 +0100 +++ new/TwistedNames-0.4.0/twisted/names/client.py 2006-12-29 06:01:40.000000000 +0100 @@ -1,5 +1,5 @@ # -*- test-case-name: twisted.names.test.test_names -*- -# Copyright (c) 2001-2004 Twisted Matrix Laboratories. +# Copyright (c) 2001-2006 Twisted Matrix Laboratories. # See LICENSE for details. @@ -8,6 +8,13 @@ API Stability: Unstable +The functions exposed in this module can be used for asynchronous name +resolution and dns queries. + +If you need to create a resolver with specific requirements, such as needing to +do queries against a particular host, the L{createResolver} function will +return an C{IResolver}. + Future plans: Proper nameserver acquisition on Windows/MacOS, better caching, respect timeouts @@ -19,13 +26,17 @@ import os import errno +from zope.interface import implements + # Twisted imports from twisted.python.runtime import platform from twisted.internet import error, defer, protocol, interfaces -from twisted.python import log, failure, components -from twisted.names import dns -from zope.interface import implements -import common +from twisted.python import log, failure +from twisted.names import dns, common +from twisted.names.error import DNSFormatError, DNSServerError, DNSNameError +from twisted.names.error import DNSNotImplementedError, DNSQueryRefusedError +from twisted.names.error import DNSUnknownError + class Resolver(common.ResolverBase): implements(interfaces.IResolver) @@ -44,6 +55,13 @@ _lastResolvTime = None _resolvReadInterval = 60 + _errormap = { + dns.EFORMAT: DNSFormatError, + dns.ESERVER: DNSServerError, + dns.ENAME: DNSNameError, + dns.ENOTIMP: DNSNotImplementedError, + dns.EREFUSED: DNSQueryRefusedError} + def __init__(self, resolv = None, servers = None, timeout = (1, 3, 11, 45)): """ Construct a resolver which will query domain name servers listed in @@ -53,7 +71,7 @@ for modification and re-parsed if it is noticed to have changed. @type servers: C{list} of C{(str, int)} or C{None} - @param servers: If not None, interpreted as a list of addresses of + @param servers: If not C{None}, interpreted as a list of addresses of domain name servers to attempt to use for this lookup. Addresses should be in dotted-quad form. If specified, overrides C{resolv}. @@ -271,10 +289,23 @@ def filterAnswers(self, message): + """ + Extract results from the given message. + + If the message was truncated, re-attempt the query over TCP and return + a Deferred which will fire with the results of that query. + + If the message's result code is not L{dns.OK}, return a Failure + indicating the type of error which occurred. + + Otherwise, return a three-tuple of lists containing the results from + the answers section, the authority section, and the additional section. + """ if message.trunc: return self.queryTCP(message.queries).addCallback(self.filterAnswers) - else: - return (message.answers, message.authority, message.additional) + if message.rCode != dns.OK: + return failure.Failure(self._errormap.get(message.rCode, DNSUnknownError)(message)) + return (message.answers, message.authority, message.additional) def _lookup(self, name, cls, type, timeout): @@ -385,7 +416,27 @@ -def createResolver(servers = None, resolvconf = None, hosts = None): +def createResolver(servers=None, resolvconf=None, hosts=None): + """ + Create and return a Resolver. + + @type servers: C{list} of C{(str, int)} or C{None} + @param servers: If not C{None}, interpreted as a list of addresses of + domain name servers to attempt to use. Addresses should be in dotted-quad + form. + + @type resolvconf: C{str} or C{None} + @param resolvconf: If not C{None}, on posix systems will be interpreted as + an alternate resolv.conf to use. Will do nothing on windows systems. If + C{None}, /etc/resolv.conf will be used. + + @type hosts: C{str} or C{None} + @param hosts: If not C{None}, an alternate hosts file to use. If C{None} + on posix systems, /etc/hosts will be used. On windows, C:\windows\hosts + will be used. + + @rtype: C{IResolver} + """ from twisted.names import resolve, cache, root, hosts as hostsModule if platform.getType() == 'posix': if resolvconf is None: @@ -406,20 +457,357 @@ return resolve.ResolverChain(L) theResolver = None -def _makeLookup(method): - def lookup(*a, **kw): - global theResolver - if theResolver is None: - try: - theResolver = createResolver() - except ValueError: - theResolver = createResolver(servers=[('127.0.0.1', 53)]) - - return getattr(theResolver, method)(*a, **kw) - return lookup - -for method in common.typeToMethod.values(): - globals()[method] = _makeLookup(method) -del method +def getResolver(): + """ + Get a Resolver instance. + + Create twisted.names.client.theResolver if it is C{None}, and then return + that value. + + @rtype: C{IResolver} + """ + global theResolver + if theResolver is None: + try: + theResolver = createResolver() + except ValueError: + theResolver = createResolver(servers=[('127.0.0.1', 53)]) + return theResolver + +def getHostByName(name, timeout=None, effort=10): + """ + Resolve a name to a valid ipv4 or ipv6 address. + + Will errback with C{DNSQueryTimeoutError} on a timeout, C{DomainError} or + C{AuthoritativeDomainError} (or subclasses) on other errors. + + @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. + + @type effort: C{int} + @param effort: How many times CNAME and NS records to follow while + resolving this name. + + @rtype: C{Deferred} + """ + return getResolver().getHostByName(name, timeout, effort) + +def lookupAddress(name, timeout=None): + """ + Perform an A record 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().lookupAddress(name, timeout) + +def lookupIPV6Address(name, timeout=None): + """ + Perform an AAAA record 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().lookupIPV6Address(name, timeout) + +def lookupAddress6(name, timeout=None): + """ + Perform an A6 record 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().lookupAddress6(name, timeout) + +def lookupMailExchange(name, timeout=None): + """ + Perform an MX record 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().lookupMailExchange(name, timeout) + +def lookupNameservers(name, timeout=None): + """ + Perform an NS record 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().lookupNameservers(name, timeout) + +def lookupCanonicalName(name, timeout=None): + """ + Perform a CNAME record 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().lookupCanonicalName(name, timeout) + +def lookupMailBox(name, timeout=None): + """ + Perform an MB record 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().lookupMailBox(name, timeout) + +def lookupMailGroup(name, timeout=None): + """ + Perform an MG record 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().lookupMailGroup(name, timeout) + +def lookupMailRename(name, timeout=None): + """ + Perform an MR record 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().lookupMailRename(name, timeout) + +def lookupPointer(name, timeout=None): + """ + Perform a PTR record 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().lookupPointer(name, timeout) + +def lookupAuthority(name, timeout=None): + """ + Perform an SOA record 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().lookupAuthority(name, timeout) + +def lookupNull(name, timeout=None): + """ + Perform a NULL record 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().lookupNull(name, timeout) + +def lookupWellKnownServices(name, timeout=None): + """ + Perform a WKS record 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().lookupWellKnownServices(name, timeout) + +def lookupService(name, timeout=None): + """ + Perform an SRV record 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().lookupService(name, timeout) + +def lookupHostInfo(name, timeout=None): + """ + Perform a HINFO record 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().lookupHostInfo(name, timeout) + +def lookupMailboxInfo(name, timeout=None): + """ + Perform an MINFO record 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().lookupMailboxInfo(name, timeout) + +def lookupText(name, timeout=None): + """ + Perform a TXT record 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().lookupText(name, timeout) + +def lookupResponsibility(name, timeout=None): + """ + Perform an RP record 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().lookupResponsibility(name, timeout) + +def lookupAFSDatabase(name, timeout=None): + """ + Perform an AFSDB record 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().lookupAFSDatabase(name, timeout) + +def lookupZone(name, timeout=None): + """ + Perform an AXFR record lookup. + + @type name: C{str} + @param name: DNS name to resolve. + + @type timeout: C{int} + @param timeout: When this timeout expires, the query is considered failed. + + @rtype: C{Deferred} + """ + # XXX: timeout here is not a list of ints, it is a single int. + return getResolver().lookupZone(name, timeout) + +def lookupAllRecords(name, timeout=None): + """ + ALL_RECORD 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().lookupAllRecords(name, timeout) -getHostByName = _makeLookup('getHostByName') diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/twisted/names/common.py new/TwistedNames-0.4.0/twisted/names/common.py --- old/TwistedNames-0.3.0/twisted/names/common.py 2006-03-17 04:29:41.000000000 +0100 +++ new/TwistedNames-0.4.0/twisted/names/common.py 2006-12-29 06:01:40.000000000 +0100 @@ -29,69 +29,135 @@ return defer.fail(NotImplementedError("ResolverBase._lookup")) def lookupAddress(self, name, timeout = None): + """ + @see: twisted.names.client.lookupAddress + """ return self._lookup(name, dns.IN, dns.A, timeout) def lookupIPV6Address(self, name, timeout = None): + """ + @see: twisted.names.client.lookupIPV6Address + """ return self._lookup(name, dns.IN, dns.AAAA, timeout) def lookupAddress6(self, name, timeout = None): + """ + @see: twisted.names.client.lookupAddress6 + """ return self._lookup(name, dns.IN, dns.A6, timeout) def lookupMailExchange(self, name, timeout = None): + """ + @see: twisted.names.client.lookupMailExchange + """ return self._lookup(name, dns.IN, dns.MX, timeout) def lookupNameservers(self, name, timeout = None): + """ + @see: twisted.names.client.lookupNameservers + """ return self._lookup(name, dns.IN, dns.NS, timeout) def lookupCanonicalName(self, name, timeout = None): + """ + @see: twisted.names.client.lookupCanonicalName + """ return self._lookup(name, dns.IN, dns.CNAME, timeout) def lookupMailBox(self, name, timeout = None): + """ + @see: twisted.names.client.lookupMailBox + """ return self._lookup(name, dns.IN, dns.MB, timeout) def lookupMailGroup(self, name, timeout = None): + """ + @see: twisted.names.client.lookupMailGroup + """ return self._lookup(name, dns.IN, dns.MG, timeout) def lookupMailRename(self, name, timeout = None): + """ + @see: twisted.names.client.lookupMailRename + """ return self._lookup(name, dns.IN, dns.MR, timeout) def lookupPointer(self, name, timeout = None): + """ + @see: twisted.names.client.lookupPointer + """ return self._lookup(name, dns.IN, dns.PTR, timeout) def lookupAuthority(self, name, timeout = None): + """ + @see: twisted.names.client.lookupAuthority + """ return self._lookup(name, dns.IN, dns.SOA, timeout) def lookupNull(self, name, timeout = None): + """ + @see: twisted.names.client.lookupNull + """ return self._lookup(name, dns.IN, dns.NULL, timeout) def lookupWellKnownServices(self, name, timeout = None): + """ + @see: twisted.names.client.lookupWellKnownServices + """ return self._lookup(name, dns.IN, dns.WKS, timeout) def lookupService(self, name, timeout = None): + """ + @see: twisted.names.client.lookupService + """ return self._lookup(name, dns.IN, dns.SRV, timeout) def lookupHostInfo(self, name, timeout = None): + """ + @see: twisted.names.client.lookupHostInfo + """ return self._lookup(name, dns.IN, dns.HINFO, timeout) def lookupMailboxInfo(self, name, timeout = None): + """ + @see: twisted.names.client.lookupMailboxInfo + """ return self._lookup(name, dns.IN, dns.MINFO, timeout) def lookupText(self, name, timeout = None): + """ + @see: twisted.names.client.lookupText + """ return self._lookup(name, dns.IN, dns.TXT, timeout) def lookupResponsibility(self, name, timeout = None): + """ + @see: twisted.names.client.lookupResponsibility + """ return self._lookup(name, dns.IN, dns.RP, timeout) def lookupAFSDatabase(self, name, timeout = None): + """ + @see: twisted.names.client.lookupAFSDatabase + """ return self._lookup(name, dns.IN, dns.AFSDB, timeout) def lookupZone(self, name, timeout = None): + """ + @see: twisted.names.client.lookupZone + """ return self._lookup(name, dns.IN, dns.AXFR, timeout) def lookupAllRecords(self, name, timeout = None): + """ + @see: twisted.names.client.lookupAllRecords + """ return self._lookup(name, dns.IN, dns.ALL_RECORDS, timeout) def getHostByName(self, name, timeout = None, effort = 10): + """ + @see: twisted.names.client.getHostByName + """ # XXX - respect timeout return self.lookupAllRecords(name, timeout ).addCallback(self._cbRecords, name, effort diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/twisted/names/dns.py new/TwistedNames-0.4.0/twisted/names/dns.py --- old/TwistedNames-0.3.0/twisted/names/dns.py 2006-03-17 04:29:41.000000000 +0100 +++ new/TwistedNames-0.4.0/twisted/names/dns.py 2006-12-06 04:15:37.000000000 +0100 @@ -43,7 +43,6 @@ return struct.unpack('H', r(2))[0] break else: - import warnings warnings.warn( "PyCrypto not available - proceeding with non-cryptographically " "secure random source", @@ -56,55 +55,80 @@ else: def randomSource(r = randpool.RandomPool().get_bytes): return struct.unpack('H', r(2))[0] -from zope.interface import implements +from zope.interface import implements, Interface # Twisted imports from twisted.internet import protocol, defer from twisted.python import log, failure from twisted.python import util as tputil -from twisted.python import components PORT = 53 +(A, NS, MD, MF, CNAME, SOA, MB, MG, MR, NULL, WKS, PTR, HINFO, MINFO, MX, TXT, + RP, AFSDB) = range(1, 19) +AAAA = 28 +SRV = 33 +A6 = 38 +DNAME = 39 + QUERY_TYPES = { - 1: 'A', 2: 'NS', 3: 'MD', 4: 'MF', - 5: 'CNAME', 6: 'SOA', 7: 'MB', 8: 'MG', - 9: 'MR', 10: 'NULL', 11: 'WKS', 12: 'PTR', - 13: 'HINFO', 14: 'MINFO', 15: 'MX', 16: 'TXT', + A: 'A', + NS: 'NS', + MD: 'MD', + MF: 'MF', + CNAME: 'CNAME', + SOA: 'SOA', + MB: 'MB', + MG: 'MG', + MR: 'MR', + NULL: 'NULL', + WKS: 'WKS', + PTR: 'PTR', + HINFO: 'HINFO', + MINFO: 'MINFO', + MX: 'MX', + TXT: 'TXT', + RP: 'RP', + AFSDB: 'AFSDB', - 17: 'RP', 18: 'AFSDB', # 19 through 27? Eh, I'll get to 'em. - 28: 'AAAA', - - 33: 'SRV', + AAAA: 'AAAA', + SRV: 'SRV', - 38: 'A6', 39: 'DNAME' + A6: 'A6', + DNAME: 'DNAME' } +IXFR, AXFR, MAILB, MAILA, ALL_RECORDS = range(251, 256) + # "Extended" queries (Hey, half of these are deprecated, good job) EXT_QUERIES = { - 251: 'IXFR', 252: 'AXFR', 253: 'MAILB', - 254: 'MAILA', 255: 'ALL_RECORDS' + IXFR: 'IXFR', + AXFR: 'AXFR', + MAILB: 'MAILB', + MAILA: 'MAILA', + ALL_RECORDS: 'ALL_RECORDS' } + REV_TYPES = dict([ (v, k) for (k, v) in QUERY_TYPES.items() + EXT_QUERIES.items() ]) -for (k, v) in REV_TYPES.items(): - exec "%s = %d" % (k, v) -del k, v +IN, CS, CH, HS = range(1, 5) +ANY = 255 QUERY_CLASSES = { - 1: 'IN', 2: 'CS', 3: 'CH', 4: 'HS', 255: 'ANY' + IN: 'IN', + CS: 'CS', + CH: 'CH', + HS: 'HS', + ANY: 'ANY' } REV_CLASSES = dict([ (v, k) for (k, v) in QUERY_CLASSES.items() ]) -for (k, v) in REV_CLASSES.items(): - exec "%s = %d" % (k, v) -del k, v # Opcodes @@ -113,22 +137,18 @@ # Response Codes OK, EFORMAT, ESERVER, ENAME, ENOTIMP, EREFUSED = range(6) -class IRecord(components.Interface): +class IRecord(Interface): """An single entry in a zone of authority. @cvar TYPE: An indicator of what kind of record this is. """ -class DomainError(ValueError): - pass -class AuthoritativeDomainError(ValueError): - pass +# Backwards compatibility aliases - these should be deprecated or something I +# suppose. -exarkun +from twisted.names.error import DomainError, AuthoritativeDomainError +from twisted.names.error import DNSQueryTimeoutError -class DNSQueryTimeoutError(defer.TimeoutError): - def __init__(self, id): - self.id = id - defer.TimeoutError.__init__(self) def str2time(s): suffixes = ( @@ -154,12 +174,12 @@ return buff -class IEncodable(components.Interface): +class IEncodable(Interface): """ Interface for something which can be encoded to and decoded from a file object. """ - def encode(self, strio, compDict = None): + def encode(strio, compDict = None): """ Write a representation of this object to the given file object. @@ -173,7 +193,7 @@ compression. """ - def decode(self, strio, length = None): + def decode(strio, length = None): """ Reconstruct an object from data read from the given file object. @@ -1085,7 +1105,19 @@ def datagramReceived(self, data, addr): m = Message() - m.fromStr(data) + try: + m.fromStr(data) + except EOFError: + log.msg("Truncated packet (%d bytes) from %s" % (len(data), addr)) + return + except: + # Nothing should trigger this, but since we're potentially + # invoking a lot of different decoding methods, we might as well + # be extra cautious. Anything that triggers this is itself + # buggy. + log.err(failure.Failure(), "Unexpected decoding error") + return + if m.id in self.liveMessages: d, canceller = self.liveMessages[m.id] del self.liveMessages[m.id] diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/twisted/names/error.py new/TwistedNames-0.4.0/twisted/names/error.py --- old/TwistedNames-0.3.0/twisted/names/error.py 1970-01-01 01:00:00.000000000 +0100 +++ new/TwistedNames-0.4.0/twisted/names/error.py 2006-12-06 04:15:37.000000000 +0100 @@ -0,0 +1,88 @@ +# -*- test-case-name: twisted.names.test -*- +# Copyright (c) 2001-2004 Twisted Matrix Laboratories. +# See LICENSE for details. + +""" +Exception class definitions for Twisted Names. +""" + +from twisted.internet.defer import TimeoutError + + +class DomainError(ValueError): + """ + Indicates a lookup failed because there were no records matching the given + C{name, class, type} triple. + """ + + + +class AuthoritativeDomainError(ValueError): + """ + Indicates a lookup failed for a name for which this server is authoritative + because there were no records matching the given C{name, class, type} + triple. + """ + + + +class DNSQueryTimeoutError(TimeoutError): + """ + Indicates a lookup failed due to a timeout. + + @ivar id: The id of the message which timed out. + """ + def __init__(self, id): + TimeoutError.__init__(self) + self.id = id + + + +class DNSFormatError(DomainError): + """ + Indicates a query failed with a result of L{twisted.names.dns.EFORMAT}. + """ + + + +class DNSServerError(DomainError): + """ + Indicates a query failed with a result of L{twisted.names.dns.ESERVER}. + """ + + + +class DNSNameError(DomainError): + """ + Indicates a query failed with a result of L{twisted.names.dns.ENAME}. + """ + + + +class DNSNotImplementedError(DomainError): + """ + Indicates a query failed with a result of L{twisted.names.dns.ENOTIMP}. + """ + + + +class DNSQueryRefusedError(DomainError): + """ + Indicates a query failed with a result of L{twisted.names.dns.EREFUSED}. + """ + + + +class DNSUnknownError(DomainError): + """ + Indicates a query failed with an unknown result. + """ + + + +__all__ = [ + 'DomainError', 'AuthoritativeDomainError', 'DNSQueryTimeoutError', + + 'DNSFormatError', 'DNSServerError', 'DNSNameError', + 'DNSNotImplementedError', 'DNSQueryRefusedError', + 'DNSUnknownError'] diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/twisted/names/resolve.py new/TwistedNames-0.4.0/twisted/names/resolve.py --- old/TwistedNames-0.3.0/twisted/names/resolve.py 2006-03-17 04:29:41.000000000 +0100 +++ new/TwistedNames-0.4.0/twisted/names/resolve.py 2006-07-01 18:08:17.000000000 +0200 @@ -15,7 +15,6 @@ from twisted.internet import defer, interfaces from twisted.names import dns -from twisted.python import components from zope.interface import implements import common diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/twisted/names/test/test_client.py new/TwistedNames-0.4.0/twisted/names/test/test_client.py --- old/TwistedNames-0.3.0/twisted/names/test/test_client.py 1970-01-01 01:00:00.000000000 +0100 +++ new/TwistedNames-0.4.0/twisted/names/test/test_client.py 2006-12-29 06:01:40.000000000 +0100 @@ -0,0 +1,241 @@ +# -*- test-case-name: twisted.names.test.test_client -*- +# Copyright (c) 2001-2006 Twisted Matrix Laboratories. +# See LICENSE for details. + +""" +Test cases for twisted.names.client +""" + +from twisted.names import client, dns +from twisted.trial import unittest +from twisted.names.common import ResolverBase +from twisted.internet import defer + +class FakeResolver(ResolverBase): + + def _lookup(self, name, cls, qtype, timeout): + """ + The getHostByNameTest does a different type of query that requires it + return an A record from an ALL_RECORDS lookup, so we accomodate that + here. + """ + if name == 'getHostByNameTest': + rr = dns.RRHeader(name=name, type=dns.A, cls=cls, ttl=60, + payload=dns.Record_A(address='127.0.0.1', ttl=60)) + else: + rr = dns.RRHeader(name=name, type=qtype, cls=cls, ttl=60) + + results = [rr] + authority = [] + addtional = [] + return defer.succeed((results, authority, addtional)) + +class ClientTestCase(unittest.TestCase): + + def setUp(self): + """ + Replace the resolver with a FakeResolver + """ + client.theResolver = FakeResolver() + self.hostname = 'example.com' + self.ghbntest = 'getHostByNameTest' + + def tearDown(self): + """ + By setting the resolver to None, it will be recreated next time a name + lookup is done. + """ + client.theResolver = None + + def checkResult(self, (results, authority, additional), qtype): + """ + Verify that the result is the same query type as what is expected. + """ + result = results[0] + self.assertEquals(str(result.name), self.hostname) + self.assertEquals(result.type, qtype) + + def checkGetHostByName(self, result): + """ + Test that the getHostByName query returns the 127.0.0.1 address. + """ + self.assertEquals(result, '127.0.0.1') + + def test_getHostByName(self): + """ + do a getHostByName of a value that should return 127.0.0.1. + """ + d = client.getHostByName(self.ghbntest) + d.addCallback(self.checkGetHostByName) + return d + + def test_lookupAddress(self): + """ + Do a lookup and test that the resolver will issue the correct type of + query type. We do this by checking that FakeResolver returns a result + record with the same query type as what we issued. + """ + d = client.lookupAddress(self.hostname) + d.addCallback(self.checkResult, dns.A) + return d + + def test_lookupIPV6Address(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupIPV6Address(self.hostname) + d.addCallback(self.checkResult, dns.AAAA) + return d + + def test_lookupAddress6(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupAddress6(self.hostname) + d.addCallback(self.checkResult, dns.A6) + return d + + def test_lookupNameservers(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupNameservers(self.hostname) + d.addCallback(self.checkResult, dns.NS) + return d + + def test_lookupCanonicalName(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupCanonicalName(self.hostname) + d.addCallback(self.checkResult, dns.CNAME) + return d + + def test_lookupAuthority(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupAuthority(self.hostname) + d.addCallback(self.checkResult, dns.SOA) + return d + + def test_lookupMailBox(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupMailBox(self.hostname) + d.addCallback(self.checkResult, dns.MB) + return d + + def test_lookupMailGroup(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupMailGroup(self.hostname) + d.addCallback(self.checkResult, dns.MG) + return d + + def test_lookupMailRename(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupMailRename(self.hostname) + d.addCallback(self.checkResult, dns.MR) + return d + + def test_lookupNull(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupNull(self.hostname) + d.addCallback(self.checkResult, dns.NULL) + return d + + def test_lookupWellKnownServices(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupWellKnownServices(self.hostname) + d.addCallback(self.checkResult, dns.WKS) + return d + + def test_lookupPointer(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupPointer(self.hostname) + d.addCallback(self.checkResult, dns.PTR) + return d + + def test_lookupHostInfo(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupHostInfo(self.hostname) + d.addCallback(self.checkResult, dns.HINFO) + return d + + def test_lookupMailboxInfo(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupMailboxInfo(self.hostname) + d.addCallback(self.checkResult, dns.MINFO) + return d + + def test_lookupMailExchange(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupMailExchange(self.hostname) + d.addCallback(self.checkResult, dns.MX) + return d + + def test_lookupText(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupText(self.hostname) + d.addCallback(self.checkResult, dns.TXT) + return d + + def test_lookupResponsibility(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupResponsibility(self.hostname) + d.addCallback(self.checkResult, dns.RP) + return d + + def test_lookupAFSDatabase(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupAFSDatabase(self.hostname) + d.addCallback(self.checkResult, dns.AFSDB) + return d + + def test_lookupService(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupService(self.hostname) + d.addCallback(self.checkResult, dns.SRV) + return d + + def test_lookupZone(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupZone(self.hostname) + d.addCallback(self.checkResult, dns.AXFR) + return d + + def test_lookupAllRecords(self): + """ + See L{test_lookupAddress} + """ + d = client.lookupAllRecords(self.hostname) + d.addCallback(self.checkResult, dns.ALL_RECORDS) + return d + diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/twisted/names/test/test_dns.py new/TwistedNames-0.4.0/twisted/names/test/test_dns.py --- old/TwistedNames-0.3.0/twisted/names/test/test_dns.py 2005-11-10 04:18:15.000000000 +0100 +++ new/TwistedNames-0.4.0/twisted/names/test/test_dns.py 2006-06-07 01:56:12.000000000 +0200 @@ -11,6 +11,7 @@ except ImportError: from StringIO import StringIO +from twisted.internet import address from twisted.trial import unittest from twisted.names import dns @@ -95,7 +96,43 @@ self.assertEquals(hk1, hk2, "%s != %s (for %s)" % (hk1,hk2,k)) -class Encoding(unittest.TestCase): + +class MessageTestCase(unittest.TestCase): + def testEmptyMessage(self): + """ + Test that a message which has been truncated causes an EOFError to + be raised when it is parsed. + """ + msg = dns.Message() + self.assertRaises(EOFError, msg.fromStr, '') + + + def testEmptyQuery(self): + """ + Test that bytes representing an empty query message can be decoded + as such. + """ + msg = dns.Message() + msg.fromStr( + '\x01\x00' # Message ID + '\x00' # answer bit, opCode nibble, auth bit, trunc bit, recursive bit + '\x00' # recursion bit, empty bit, empty bit, empty bit, response code nibble + '\x00\x00' # number of queries + '\x00\x00' # number of answers + '\x00\x00' # number of authorities + '\x00\x00' # number of additionals + ) + self.assertEquals(msg.id, 256) + self.failIf(msg.answer, "Message was not supposed to be an answer.") + self.assertEquals(msg.opCode, dns.OP_QUERY) + self.failIf(msg.auth, "Message was not supposed to be authoritative.") + self.failIf(msg.trunc, "Message was not supposed to be truncated.") + self.assertEquals(msg.queries, []) + self.assertEquals(msg.answers, []) + self.assertEquals(msg.authority, []) + self.assertEquals(msg.additional, []) + + def testNULL(self): bytes = ''.join([chr(i) for i in range(256)]) rec = dns.Record_NULL(bytes) @@ -111,3 +148,32 @@ self.failUnless(isinstance(msg2.answers[0].payload, dns.Record_NULL)) self.assertEquals(msg2.answers[0].payload.payload, bytes) + + +class TestController(object): + """ + Pretend to be a DNS query processor for a DNSDatagramProtocol. + """ + def __init__(self): + self.messages = [] + + + def messageReceived(self, msg, proto, addr): + self.messages.append((msg, proto, addr)) + + + +class DatagramProtocolTestCase(unittest.TestCase): + """ + Test various aspects of DNSDatagramProtocol. + """ + + def testTruncatedPacket(self): + """ + Test that when a short datagram is received, datagramReceived does + not raise an exception while processing it. + """ + controller = TestController() + proto = dns.DNSDatagramProtocol(controller) + proto.datagramReceived('', address.IPv4Address('UDP', '127.0.0.1', 12345)) + self.assertEquals(controller.messages, []) diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/twisted/names/test/test_names.py new/TwistedNames-0.4.0/twisted/names/test/test_names.py --- old/TwistedNames-0.3.0/twisted/names/test/test_names.py 2006-04-10 05:21:33.000000000 +0200 +++ new/TwistedNames-0.4.0/twisted/names/test/test_names.py 2006-12-06 04:15:37.000000000 +0100 @@ -12,8 +12,16 @@ from twisted.trial import unittest from twisted.internet import reactor, defer, error +from twisted.internet.defer import succeed from twisted.names import client, server, common, authority, hosts, dns from twisted.python import failure +from twisted.names.error import DNSFormatError, DNSServerError, DNSNameError +from twisted.names.error import DNSNotImplementedError, DNSQueryRefusedError +from twisted.names.error import DNSUnknownError +from twisted.names.dns import EFORMAT, ESERVER, ENAME, ENOTIMP, EREFUSED +from twisted.names.dns import Message +from twisted.names.client import Resolver + def justPayload(results): return [r.payload for r in results[0]] @@ -152,8 +160,10 @@ self.resolver = client.Resolver(servers=[('127.0.0.1', port)]) def tearDown(self): - self.listenerTCP.loseConnection() - d = defer.maybeDeferred(self.listenerUDP.stopListening) + """Asynchronously disconnect listenerTCP, listenerUDP and resolver""" + d1 = self.listenerTCP.loseConnection() + d2 = defer.maybeDeferred(self.listenerUDP.stopListening) + d = defer.gatherResults([d1, d2]) def disconnectTransport(ignored): if getattr(self.resolver.protocol, 'transport', None) is not None: return self.resolver.protocol.transport.stopListening() @@ -557,3 +567,87 @@ r = client.Resolver(resolv=resolvConf) self.assertEquals(r.dynServers, [('127.0.0.1', 53)]) r._parseCall.cancel() + + + +class FilterAnswersTests(unittest.TestCase): + """ + Test L{twisted.names.client.Resolver.filterAnswers}'s handling of various + error conditions it might encounter. + """ + def setUp(self): + # Create a resolver pointed at an invalid server - we won't be hitting + # the network in any of these tests. + self.resolver = Resolver(servers=[('0.0.0.0', 0)]) + + + def test_truncatedMessage(self): + """ + Test that a truncated message results in an equivalent request made via + TCP. + """ + m = Message(trunc=True) + m.addQuery('example.com') + + def queryTCP(queries): + self.assertEqual(queries, m.queries) + response = Message() + response.answers = ['answer'] + response.authority = ['authority'] + response.additional = ['additional'] + return succeed(response) + self.resolver.queryTCP = queryTCP + d = self.resolver.filterAnswers(m) + d.addCallback( + self.assertEqual, (['answer'], ['authority'], ['additional'])) + return d + + + def _rcodeTest(self, rcode, exc): + m = Message(rCode=rcode) + err = self.resolver.filterAnswers(m) + err.trap(exc) + + + def test_formatError(self): + """ + Test that a message with a result code of C{EFORMAT} results in a + failure wrapped around L{DNSFormatError}. + """ + return self._rcodeTest(EFORMAT, DNSFormatError) + + + def test_serverError(self): + """ + Like L{test_formatError} but for C{ESERVER}/L{DNSServerError}. + """ + return self._rcodeTest(ESERVER, DNSServerError) + + + def test_nameError(self): + """ + Like L{test_formatError} but for C{ENAME}/L{DNSNameError}. + """ + return self._rcodeTest(ENAME, DNSNameError) + + + def test_notImplementedError(self): + """ + Like L{test_formatError} but for C{ENOTIMP}/L{DNSNotImplementedError}. + """ + return self._rcodeTest(ENOTIMP, DNSNotImplementedError) + + + def test_refusedError(self): + """ + Like L{test_formatError} but for C{EREFUSED}/L{DNSQueryRefusedError}. + """ + return self._rcodeTest(EREFUSED, DNSQueryRefusedError) + + + def test_refusedError(self): + """ + Like L{test_formatError} but for an unrecognized error code and + L{DNSUnknownError}. + """ + return self._rcodeTest(EREFUSED + 1, DNSUnknownError) diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/TwistedNames-0.3.0/twisted/names/_version.py new/TwistedNames-0.4.0/twisted/names/_version.py --- old/TwistedNames-0.3.0/twisted/names/_version.py 2006-05-22 00:28:51.000000000 +0200 +++ new/TwistedNames-0.4.0/twisted/names/_version.py 2007-01-06 22:42:54.000000000 +0100 @@ -1,3 +1,3 @@ # This is an auto-generated file. Use admin/change-versions to update. from twisted.python import versions -version = versions.Version(__name__[:__name__.rfind('.')], 0, 3, 0) +version = versions.Version(__name__[:__name__.rfind('.')], 0, 4, 0) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Remember to have fun... --------------------------------------------------------------------- To unsubscribe, e-mail: opensuse-commit+unsubscribe@opensuse.org For additional commands, e-mail: opensuse-commit+help@opensuse.org