Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / names / common.py
1 # -*- test-case-name: twisted.names.test -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 Base functionality useful to various parts of Twisted Names.
7 """
8
9 import socket
10
11 from twisted.names import dns
12 from twisted.names.error import DNSFormatError, DNSServerError, DNSNameError
13 from twisted.names.error import DNSNotImplementedError, DNSQueryRefusedError
14 from twisted.names.error import DNSUnknownError
15
16 from twisted.internet import defer, error
17 from twisted.python import failure
18
19 EMPTY_RESULT = (), (), ()
20
21 class ResolverBase:
22     """
23     L{ResolverBase} is a base class for L{IResolver} implementations which
24     deals with a lot of the boilerplate of implementing all of the lookup
25     methods.
26
27     @cvar _errormap: A C{dict} mapping DNS protocol failure response codes
28         to exception classes which will be used to represent those failures.
29     """
30     _errormap = {
31         dns.EFORMAT: DNSFormatError,
32         dns.ESERVER: DNSServerError,
33         dns.ENAME: DNSNameError,
34         dns.ENOTIMP: DNSNotImplementedError,
35         dns.EREFUSED: DNSQueryRefusedError}
36
37     typeToMethod = None
38
39     def __init__(self):
40         self.typeToMethod = {}
41         for (k, v) in typeToMethod.items():
42             self.typeToMethod[k] = getattr(self, v)
43
44
45     def exceptionForCode(self, responseCode):
46         """
47         Convert a response code (one of the possible values of
48         L{dns.Message.rCode} to an exception instance representing it.
49
50         @since: 10.0
51         """
52         return self._errormap.get(responseCode, DNSUnknownError)
53
54
55     def query(self, query, timeout = None):
56         try:
57             return self.typeToMethod[query.type](str(query.name), timeout)
58         except KeyError, e:
59             return defer.fail(failure.Failure(NotImplementedError(str(self.__class__) + " " + str(query.type))))
60
61     def _lookup(self, name, cls, type, timeout):
62         return defer.fail(NotImplementedError("ResolverBase._lookup"))
63
64     def lookupAddress(self, name, timeout = None):
65         """
66         @see: twisted.names.client.lookupAddress
67         """
68         return self._lookup(name, dns.IN, dns.A, timeout)
69
70     def lookupIPV6Address(self, name, timeout = None):
71         """
72         @see: twisted.names.client.lookupIPV6Address
73         """
74         return self._lookup(name, dns.IN, dns.AAAA, timeout)
75
76     def lookupAddress6(self, name, timeout = None):
77         """
78         @see: twisted.names.client.lookupAddress6
79         """
80         return self._lookup(name, dns.IN, dns.A6, timeout)
81
82     def lookupMailExchange(self, name, timeout = None):
83         """
84         @see: twisted.names.client.lookupMailExchange
85         """
86         return self._lookup(name, dns.IN, dns.MX, timeout)
87
88     def lookupNameservers(self, name, timeout = None):
89         """
90         @see: twisted.names.client.lookupNameservers
91         """
92         return self._lookup(name, dns.IN, dns.NS, timeout)
93
94     def lookupCanonicalName(self, name, timeout = None):
95         """
96         @see: twisted.names.client.lookupCanonicalName
97         """
98         return self._lookup(name, dns.IN, dns.CNAME, timeout)
99
100     def lookupMailBox(self, name, timeout = None):
101         """
102         @see: twisted.names.client.lookupMailBox
103         """
104         return self._lookup(name, dns.IN, dns.MB, timeout)
105
106     def lookupMailGroup(self, name, timeout = None):
107         """
108         @see: twisted.names.client.lookupMailGroup
109         """
110         return self._lookup(name, dns.IN, dns.MG, timeout)
111
112     def lookupMailRename(self, name, timeout = None):
113         """
114         @see: twisted.names.client.lookupMailRename
115         """
116         return self._lookup(name, dns.IN, dns.MR, timeout)
117
118     def lookupPointer(self, name, timeout = None):
119         """
120         @see: twisted.names.client.lookupPointer
121         """
122         return self._lookup(name, dns.IN, dns.PTR, timeout)
123
124     def lookupAuthority(self, name, timeout = None):
125         """
126         @see: twisted.names.client.lookupAuthority
127         """
128         return self._lookup(name, dns.IN, dns.SOA, timeout)
129
130     def lookupNull(self, name, timeout = None):
131         """
132         @see: twisted.names.client.lookupNull
133         """
134         return self._lookup(name, dns.IN, dns.NULL, timeout)
135
136     def lookupWellKnownServices(self, name, timeout = None):
137         """
138         @see: twisted.names.client.lookupWellKnownServices
139         """
140         return self._lookup(name, dns.IN, dns.WKS, timeout)
141
142     def lookupService(self, name, timeout = None):
143         """
144         @see: twisted.names.client.lookupService
145         """
146         return self._lookup(name, dns.IN, dns.SRV, timeout)
147
148     def lookupHostInfo(self, name, timeout = None):
149         """
150         @see: twisted.names.client.lookupHostInfo
151         """
152         return self._lookup(name, dns.IN, dns.HINFO, timeout)
153
154     def lookupMailboxInfo(self, name, timeout = None):
155         """
156         @see: twisted.names.client.lookupMailboxInfo
157         """
158         return self._lookup(name, dns.IN, dns.MINFO, timeout)
159
160     def lookupText(self, name, timeout = None):
161         """
162         @see: twisted.names.client.lookupText
163         """
164         return self._lookup(name, dns.IN, dns.TXT, timeout)
165
166     def lookupSenderPolicy(self, name, timeout = None):
167         """
168         @see: twisted.names.client.lookupSenderPolicy
169         """
170         return self._lookup(name, dns.IN, dns.SPF, timeout)
171
172     def lookupResponsibility(self, name, timeout = None):
173         """
174         @see: twisted.names.client.lookupResponsibility
175         """
176         return self._lookup(name, dns.IN, dns.RP, timeout)
177
178     def lookupAFSDatabase(self, name, timeout = None):
179         """
180         @see: twisted.names.client.lookupAFSDatabase
181         """
182         return self._lookup(name, dns.IN, dns.AFSDB, timeout)
183
184     def lookupZone(self, name, timeout = None):
185         """
186         @see: twisted.names.client.lookupZone
187         """
188         return self._lookup(name, dns.IN, dns.AXFR, timeout)
189
190
191     def lookupNamingAuthorityPointer(self, name, timeout=None):
192         """
193         @see: twisted.names.client.lookupNamingAuthorityPointer
194         """
195         return self._lookup(name, dns.IN, dns.NAPTR, timeout)
196
197
198     def lookupAllRecords(self, name, timeout = None):
199         """
200         @see: twisted.names.client.lookupAllRecords
201         """
202         return self._lookup(name, dns.IN, dns.ALL_RECORDS, timeout)
203
204     def getHostByName(self, name, timeout = None, effort = 10):
205         """
206         @see: twisted.names.client.getHostByName
207         """
208         # XXX - respect timeout
209         return self.lookupAllRecords(name, timeout
210             ).addCallback(self._cbRecords, name, effort
211             )
212
213     def _cbRecords(self, (ans, auth, add), name, effort):
214         result = extractRecord(self, dns.Name(name), ans + auth + add, effort)
215         if not result:
216             raise error.DNSLookupError(name)
217         return result
218
219
220 def extractRecord(resolver, name, answers, level=10):
221     if not level:
222         return None
223     if hasattr(socket, 'inet_ntop'):
224         for r in answers:
225             if r.name == name and r.type == dns.A6:
226                 return socket.inet_ntop(socket.AF_INET6, r.payload.address)
227         for r in answers:
228             if r.name == name and r.type == dns.AAAA:
229                 return socket.inet_ntop(socket.AF_INET6, r.payload.address)
230     for r in answers:
231         if r.name == name and r.type == dns.A:
232             return socket.inet_ntop(socket.AF_INET, r.payload.address)
233     for r in answers:
234         if r.name == name and r.type == dns.CNAME:
235             result = extractRecord(
236                 resolver, r.payload.name, answers, level - 1)
237             if not result:
238                 return resolver.getHostByName(
239                     str(r.payload.name), effort=level - 1)
240             return result
241     # No answers, but maybe there's a hint at who we should be asking about
242     # this
243     for r in answers:
244         if r.type == dns.NS:
245             from twisted.names import client
246             r = client.Resolver(servers=[(str(r.payload.name), dns.PORT)])
247             return r.lookupAddress(str(name)
248                 ).addCallback(
249                     lambda (ans, auth, add):
250                         extractRecord(r, name, ans + auth + add, level - 1))
251
252
253 typeToMethod = {
254     dns.A:     'lookupAddress',
255     dns.AAAA:  'lookupIPV6Address',
256     dns.A6:    'lookupAddress6',
257     dns.NS:    'lookupNameservers',
258     dns.CNAME: 'lookupCanonicalName',
259     dns.SOA:   'lookupAuthority',
260     dns.MB:    'lookupMailBox',
261     dns.MG:    'lookupMailGroup',
262     dns.MR:    'lookupMailRename',
263     dns.NULL:  'lookupNull',
264     dns.WKS:   'lookupWellKnownServices',
265     dns.PTR:   'lookupPointer',
266     dns.HINFO: 'lookupHostInfo',
267     dns.MINFO: 'lookupMailboxInfo',
268     dns.MX:    'lookupMailExchange',
269     dns.TXT:   'lookupText',
270     dns.SPF:   'lookupSenderPolicy',
271
272     dns.RP:    'lookupResponsibility',
273     dns.AFSDB: 'lookupAFSDatabase',
274     dns.SRV:   'lookupService',
275     dns.NAPTR: 'lookupNamingAuthorityPointer',
276     dns.AXFR:         'lookupZone',
277     dns.ALL_RECORDS:  'lookupAllRecords',
278 }