Merge tag 'upstream/1.16.0' into debian/master
Upstream version 1.16.0
Scott Kitterman
5 years ago
0 | build | |
1 | dist | |
2 | MANIFEST | |
3 | html | |
4 | html.zip | |
5 | html.tar.gz | |
6 | tests/*.out | |
7 | *.pyc | |
8 | .coverage | |
9 | .tox | |
10 | dnspython.egg-info/ | |
11 | .eggs/ |
0 | language: python | |
1 | python: | |
2 | - "2.7" | |
3 | - "3.4" | |
4 | - "3.5" | |
5 | - "3.6" | |
6 | #- "nightly" | |
7 | matrix: | |
8 | include: | |
9 | - python: 3.7 # https://github.com/travis-ci/travis-ci/issues/9815 | |
10 | dist: xenial | |
11 | sudo: true | |
12 | allow_failures: | |
13 | - python: nightly | |
14 | branches: | |
15 | except: | |
16 | - python3 | |
17 | install: | |
18 | - pip install typing pylint pycryptodome ecdsa idna | |
19 | script: | |
20 | - make typecheck | |
21 | - make lint | |
22 | - make test |
0 | 2016-09-29 Bob Halley <halley@dnspython.org> | |
1 | ||
2 | * IDNA 2008 support is now available if the "idna" module has been | |
3 | installed and IDNA 2008 is requested. The default IDNA behavior | |
4 | is still IDNA 2003. The new IDNA codec mechanism is currently | |
5 | only useful for direct calls to dns.name.from_text() or | |
6 | dns.name.from_unicode(), but in future releases it will be | |
7 | deployed throughout dnspython, e.g. so that you can read a | |
8 | masterfile with an IDNA 2008 codec in force. | |
9 | ||
10 | * By default, dns.name.to_unicode() is not strict about which | |
11 | version of IDNA the input complies with. Strictness can be | |
12 | requested by using one of the strict IDNA codecs. | |
13 | ||
14 | * Add AVC RR support. | |
15 | ||
16 | * Some problems with newlines in various output modes have been | |
17 | addressed. | |
18 | ||
19 | * dns.name.to_text() now returns text and not bytes on Python 3.x | |
20 | ||
21 | * More miscellaneous fixes for the Python 2/3 codeline merge. | |
22 | ||
23 | 2016-05-27 Bob Halley <halley@dnspython.org> | |
24 | ||
25 | * (Version 1.14.0 released) | |
26 | ||
27 | * Add CSYNC RR support | |
28 | ||
29 | * Fix bug in LOC which destroyed N/S and E/W distinctions within | |
30 | a degree of the equator or prime merdian respectively. | |
31 | ||
32 | * Misc. fixes to deal with fallout from the Python 2 & 3 merge. | |
33 | [issue #156], [issue #157], [issue #158], [issue #159], | |
34 | [issue #160]. | |
35 | ||
36 | * Running with python optimization on caused issues when | |
37 | stripped docstrings were referenced. [issue #154] | |
38 | ||
39 | * dns.zone.from_text() erroneously required the zone to be provided. | |
40 | [issue #153] | |
41 | ||
42 | 2016-05-13 Bob Halley <halley@dnspython.org> | |
43 | ||
44 | * dns/message.py (make_query): Setting any value which implies | |
45 | EDNS will turn on EDNS if 'use_edns' has not been specified. | |
46 | ||
47 | 2016-05-12 Bob Halley <halley@dnspython.org> | |
48 | ||
49 | * TSIG signature algorithm setting was broken by the Python 2 | |
50 | and Python 3 code line merge. Fixed. | |
51 | ||
52 | 2016-05-10 Bob Halley <halley@dnspython.org> | |
53 | ||
54 | * (Version 1.13.0 released) | |
55 | ||
56 | 2016-05-10 Bob Halley <halley@dnspython.org> | |
57 | ||
58 | * Dropped support for Python 2.4 and 2.5. | |
59 | ||
60 | * Zone origin can be specified as a string. | |
61 | ||
62 | * Support string representation for all DNSExceptions. | |
63 | ||
64 | * Use setuptools not distutils | |
65 | ||
66 | * A number of Unicode name bug fixes. | |
67 | ||
68 | * Added support for CAA, CDS, CDNSKEY, EUI48, EUI64, and URI RR | |
69 | types. | |
70 | ||
71 | * Names now support the pickle protocol. | |
72 | ||
73 | * NameDicts now keep the max-depth value correct, and update | |
74 | properly. | |
75 | ||
76 | * resolv.conf processing rejects lines with too few tokens. | |
77 | ||
78 | * Ports can be specified per-nameserver in the stub resolver. | |
79 | ||
80 | 2016-05-03 Arthur Gautier | |
81 | ||
82 | * Single source support for python 2.6+ and 3.3+ | |
83 | ||
84 | 2014-09-04 Bob Halley <halley@dnspython.org> | |
85 | ||
86 | * Comparing two rdata is now always done by comparing the binary | |
87 | data of the DNSSEC digestable forms. This corrects a number of | |
88 | errors where dnspython's rdata comparison order was not the | |
89 | DNSSEC order. | |
90 | ||
91 | * Add CAA implementation. Thanks to Brian Wellington for the | |
92 | patch. | |
93 | ||
94 | 2014-09-01 Bob Halley <halley@dnspython.org> | |
95 | ||
96 | * (Version 1.12.0 released) | |
97 | ||
98 | 2014-08-31 Bob Halley <halley@dnspython.org> | |
99 | ||
100 | * The test system can now run the tests without requiring dnspython | |
101 | to be installed. | |
102 | ||
103 | 2014-07-24 Bob Halley <halley@dnspython.org> | |
104 | ||
105 | * The 64-bit version of Python on Windows has sys.maxint set to | |
106 | 2^31-1, yet passes 2^63-1 as the "unspecified bound" value in | |
107 | slices. This is a bug in Python as the documentation says the | |
108 | unspecified bound value should be sys.maxint. We now cope with | |
109 | this. Thanks to Matthäus Wander for reporting the problem. | |
110 | ||
111 | 2014-06-21 Bob Halley <halley@dnspython.org> | |
112 | ||
113 | * When reading from a masterfile, if the first content line | |
114 | started with leading whitespace, we raised an ugly exception | |
115 | instead of doing the right thing, namely using the zone origin as | |
116 | the name. [#73] Thanks to Tassatux for reporting the issue. | |
117 | ||
118 | * Added dns.zone.to_text() convenience method. Thanks to Brandon | |
119 | Whaley <redkrieg@gmail.com> for the patch. | |
120 | ||
121 | * The /etc/resolv.conf setting "options rotate" is now understood | |
122 | by the resolver. If present, the resolver will shuffle the | |
123 | nameserver list each time dns.resolver.query() is called. Thanks | |
124 | to underrun for the patch. Note that you don't want to add | |
125 | "options rotate" to your /etc/resolv.conf if your system's | |
126 | resolver library does not understand it. In this case, just set | |
127 | resolver.rotate = True by hand. | |
128 | ||
129 | 2014-06-19 Bob Halley <halley@dnspython.org> | |
130 | ||
131 | * Escaping of Unicode has been corrected. Previously we escaped | |
132 | and then converted to Unicode, but the right thing to do is | |
133 | convert to Unicode, then escape. Also, characters > 0x7f should | |
134 | NOT be escaped in Unicode mode. Thanks to Martin Basti for the | |
135 | patch. | |
136 | ||
137 | * dns.rdtypes.ANY.DNSKEY now has helpers functions to convert | |
138 | between the numeric form of the flags and a set of human-friendly | |
139 | strings. Thanks to Petr Spacek for the patch. | |
140 | ||
141 | * RRSIGs did not respect relativization settings in to_text(). | |
142 | Thanks to Brian Smith for reporting the bug and submitting a | |
143 | (slightly different) patch. | |
144 | ||
145 | 2014-06-18 Bob Halley <halley@dnspython.org> | |
146 | ||
147 | * dns/rdtypes/IN/APL.py: The APL from_wire() method did not accept an | |
148 | rdata length of 0 as valid. Thanks to salzmdan for reporting the | |
149 | problem. | |
150 | ||
151 | 2014-05-31 Bob Halley <halley@dnspython.org> | |
152 | ||
153 | * dns/ipv6.py: Add is_mapped() | |
154 | ||
155 | * dns/reversename.py: Lookup IPv6 mapped IPv4 addresses in the v4 | |
156 | reverse namespace. Thanks to Devin Bayer. Yes, I finally fixed | |
157 | this one :) | |
158 | ||
159 | 2014-04-11 Bob Halley <halley@dnspython.org> | |
160 | ||
161 | * dns/zone.py: Do not put back an unescaped token. This was | |
162 | causing escape processing for domain names to break. Thanks to | |
163 | connormclaud for reporting the problem. | |
164 | ||
165 | 2014-04-04 Bob Halley <halley@dnspython.org> | |
166 | ||
167 | * dns/message.py: Making a response didn't work correctly if the | |
168 | query was signed with TSIG and we knew the key. Thanks to Jeffrey | |
169 | Stiles for reporting the problem. | |
170 | ||
171 | 2013-12-11 Bob Halley <halley@dnspython.org> | |
172 | ||
173 | * dns/query.py: Fix problems with the IXFR state machine which caused | |
174 | long diffs to fail. Thanks to James Raftery for the fix and the | |
175 | repeated prodding to get it applied :) | |
176 | ||
177 | 2013-09-02 Bob Halley <halley@dnspython.org> | |
178 | ||
179 | * (Version 1.11.1 released) | |
180 | ||
181 | 2013-09-01 Bob Halley <halley@dnspython.org> | |
182 | ||
183 | * dns/tsigkeyring.py (to_text): we want keyname.to_text(), not | |
184 | dns.name.to_text(keyname). Thangs to wangwang for the fix. | |
185 | ||
186 | 2013-08-26 Bob Halley <halley@dnspython.org> | |
187 | ||
188 | * dns/tsig.py (sign): multi-message TSIGs were broken for | |
189 | algorithms other than HMAC-MD5 because we weren't passing the | |
190 | right digest module to the HMAC code. Thanks to salzmdan for | |
191 | reporting the bug. | |
192 | ||
193 | 2013-08-09 Bob Halley <halley@dnspython.org> | |
194 | ||
195 | * dns/dnssec.py (_find_candidate_keys): we tried to extract the | |
196 | key from the wrong variable name. Thanks to Andrei Fokau for the | |
197 | fix. | |
198 | ||
199 | 2013-07-08 Bob Halley <halley@dnspython.org> | |
200 | ||
201 | * dns/resolver.py: we want 'self.retry_servfail' not just | |
202 | retry_servfail. Reported by many, thanks! Thanks to | |
203 | Jeffrey C. Ollie for the fix. | |
204 | ||
205 | 2013-07-08 Bob Halley <halley@dnspython.org> | |
206 | ||
207 | * tests/grange.py: fix tests to use older-style print formatting | |
208 | for backwards compatibility with python 2.4. Thanks to | |
209 | Jeffrey C. Ollie for the fix. | |
210 | ||
211 | 2013-07-01 Bob Halley <halley@dnspython.org> | |
212 | ||
213 | * (Version 1.11.0 released) | |
214 | ||
215 | 2013-04-28 Bob Halley <halley@dnspython.org> | |
216 | ||
217 | * dns/name.py (Name.to_wire): Do not add items with offsets >= 2^14 | |
218 | to the compression table. Thanks to Casey Deccio for discovering | |
219 | this bug. | |
220 | ||
221 | 2013-04-26 Bob Halley <halley@dnspython.org> | |
222 | ||
223 | * dns/ipv6.py (inet_ntoa): We now comply with RFC 5952 section | |
224 | 5.2.2, by *not* using the :: syntax to shorten just one 16-bit | |
225 | field. Thanks to David Waitzman for reporting the bug and | |
226 | suggesting the fix. | |
227 | ||
228 | 2013-03-31 Bob Halley <halley@dnspython.org> | |
229 | ||
230 | * lock caches in case they are shared | |
231 | ||
232 | * raise YXDOMAIN if we see one | |
233 | ||
234 | * do not print empty rdatasets | |
235 | ||
236 | * Add contributed $GENERATE support (thanks uberj) | |
237 | ||
238 | * Remove DNSKEY keytag uniqueness assumption (RFC 4034, section 8) | |
239 | (thanks James Dempsey) | |
240 | ||
241 | 2012-09-25 Sean Leach | |
242 | ||
243 | * added set_flags() method to dns.resolver.Resolver | |
244 | ||
245 | 2012-09-25 Pieter Lexis | |
246 | ||
247 | * added support for TLSA RR | |
248 | ||
249 | 2012-08-28 Bob Halley <halley@dnspython.org> | |
250 | ||
251 | * dns/rdtypes/ANY/NSEC3.py (NSEC3.from_text): The NSEC3 from_text() | |
252 | method could erroneously emit empty bitmap windows (i.e. windows | |
253 | with a count of 0 bytes); such bitmaps are illegal. | |
254 | ||
255 | 2012-04-08 Bob Halley <halley@dnspython.org> | |
256 | ||
257 | * (Version 1.10.0 released) | |
258 | ||
259 | 2012-04-08 Bob Halley <halley@dnspython.org> | |
260 | ||
261 | * dns/message.py (make_query): All EDNS values may now be | |
262 | specified when calling make_query() | |
263 | ||
264 | * dns/query.py: Specifying source_port had no effect if source was | |
265 | not specified. We now use the appropriate wildcard source in | |
266 | that case. | |
267 | ||
268 | * dns/resolver.py (Resolver.query): source_port may now be | |
269 | specified. | |
270 | ||
271 | * dns/resolver.py (Resolver.query): Switch to TCP when a UDP | |
272 | response is truncated. Handle nameservers that serve on UDP | |
273 | but not TCP. | |
274 | ||
275 | 2012-04-07 Bob Halley <halley@dnspython.org> | |
276 | ||
277 | * dns/zone.py (from_xfr): dns.zone.from_xfr() now takes a | |
278 | 'check_origin' parameter which defaults to True. If set to | |
279 | False, then dnspython will not make origin checks on the zone. | |
280 | Thanks to Carlos Perez for the report. | |
281 | ||
282 | * dns/rdtypes/ANY/SSHFP.py (SSHFP.from_text): Allow whitespace in | |
283 | the text string. Thanks to Jan Andres for the report and the | |
284 | patch. | |
285 | ||
286 | * dns/message.py (from_wire): dns.message.from_wire() now takes | |
287 | an 'ignore_trailing' parameter which defaults to False. If set | |
288 | to True, then trailing junk will be ignored instead of causing | |
289 | TrailingJunk to be raised. Thanks to Shane Huntley for | |
290 | contributing the patch. | |
291 | ||
292 | 2011-08-22 Bob Halley <halley@dnspython.org> | |
293 | ||
294 | * dns/resolver.py: Added LRUCache. In this cache implementation, | |
295 | the cache size is limited to a user-specified number of nodes, and | |
296 | when adding a new node to a full cache the least-recently used | |
297 | node is removed. | |
298 | ||
299 | 2011-07-13 Bob Halley <halley@dnspython.org> | |
300 | ||
301 | * dns/resolver.py: dns.resolver.override_system_resolver() | |
302 | overrides the socket module's versions of getaddrinfo(), | |
303 | getnameinfo(), getfqdn(), gethostbyname(), gethostbyname_ex() and | |
304 | gethostbyaddr() with an implementation which uses a dnspython stub | |
305 | resolver instead of the system's stub resolver. This can be | |
306 | useful in testing situations where you want to control the | |
307 | resolution behavior of python code without having to change the | |
308 | system's resolver settings (e.g. /etc/resolv.conf). | |
309 | dns.resolver.restore_system_resolver() undoes the change. | |
310 | ||
311 | 2011-07-08 Bob Halley <halley@dnspython.org> | |
312 | ||
313 | * dns/ipv4.py: dnspython now provides its own, stricter, versions | |
314 | of IPv4 inet_ntoa() and inet_aton() instead of using the OS's | |
315 | versions. | |
316 | ||
317 | * dns/ipv6.py: inet_aton() now bounds checks embedded IPv4 addresses | |
318 | more strictly. Also, now only dns.exception.SyntaxError can be | |
319 | raised on bad input. | |
320 | ||
321 | 2011-04-05 Bob Halley <halley@dnspython.org> | |
322 | ||
323 | * Old DNSSEC types (KEY, NXT, and SIG) have been removed. | |
324 | ||
325 | * Bounds checking of slices in rdata wire processing is now more | |
326 | strict, and bounds errors (e.g. we got less data than was | |
327 | expected) now raise dns.exception.FormError rather than | |
328 | IndexError. | |
329 | ||
330 | 2011-03-28 Bob Halley <halley@dnspython.org> | |
331 | ||
332 | * (Version 1.9.4 released) | |
333 | ||
334 | 2011-03-24 Bob Halley <halley@dnspython.org> | |
335 | ||
336 | * dns/rdata.py (Rdata._wire_cmp): We need to specify no | |
337 | compression and an origin to _wire_cmp() in case names in the | |
338 | rdata are relative names. | |
339 | ||
340 | * dns/rdtypes/ANY/SIG.py (SIG._cmp): Add missing 'import struct'. | |
341 | Thanks to Arfrever Frehtes Taifersar Arahesis for reporting the | |
342 | problem. | |
343 | ||
344 | 2011-03-24 Bob Halley <halley@dnspython.org> | |
345 | ||
346 | * (Version 1.9.3 released) | |
347 | ||
348 | 2011-03-22 Bob Halley <halley@dnspython.org> | |
349 | ||
350 | * dns/resolver.py: a boolean parameter, 'raise_on_no_answer', has | |
351 | been added to the query() methods. In no-error, no-data | |
352 | situations, this parameter determines whether NoAnswer should be | |
353 | raised or not. If True, NoAnswer is raised. If False, then an | |
354 | Answer() object with a None rrset will be returned. | |
355 | ||
356 | * dns/resolver.py: Answer() objects now have a canonical_name field. | |
357 | ||
358 | 2011-01-11 Bob Halley <halley@dnspython.org> | |
359 | ||
360 | * Dnspython was erroneously doing case-insensitive comparisons | |
361 | of the names in NSEC and RRSIG RRs. Thanks to Casey Deccio for | |
362 | reporting this bug. | |
363 | ||
364 | 2010-12-17 Bob Halley <halley@dnspython.org> | |
365 | ||
366 | * dns/message.py (_WireReader._get_section): use "is" and not "==" | |
367 | when testing what section an RR is in. Thanks to James Raftery | |
368 | for reporting this bug. | |
369 | ||
370 | 2010-12-10 Bob Halley <halley@dnspython.org> | |
371 | ||
372 | * dns/resolver.py (Resolver.query): disallow metaqueries. | |
373 | ||
374 | * dns/rdata.py (Rdata.__hash__): Added a __hash__ method for rdata. | |
375 | ||
376 | 2010-11-23 Bob Halley <halley@dnspython.org> | |
377 | ||
378 | * (Version 1.9.2 released) | |
379 | ||
380 | 2010-11-23 Bob Halley <halley@dnspython.org> | |
381 | ||
382 | * dns/dnssec.py (_need_pycrypto): DSA and RSA are modules, not | |
383 | functions, and I didn't notice because the test suite masked | |
384 | the bug! *sigh* | |
385 | ||
386 | 2010-11-22 Bob Halley <halley@dnspython.org> | |
387 | ||
388 | * (Version 1.9.1 released) | |
389 | ||
390 | 2010-11-22 Bob Halley <halley@dnspython.org> | |
391 | ||
392 | * dns/dnssec.py: the "from" style import used to get DSA from | |
393 | PyCrypto trashed a DSA constant. Now a normal import is used | |
394 | to avoid namespace contamination. | |
395 | ||
396 | 2010-11-20 Bob Halley <halley@dnspython.org> | |
397 | ||
398 | * (Version 1.9.0 released) | |
399 | ||
400 | 2010-11-07 Bob Halley <halley@dnspython.org> | |
401 | ||
402 | * dns/dnssec.py: Added validate() to do basic DNSSEC validation | |
403 | (requires PyCrypto). Thanks to Brian Wellington for the patch. | |
404 | ||
405 | * dns/hash.py: Hash compatibility handling is now its own module. | |
406 | ||
407 | 2010-10-31 Bob Halley <halley@dnspython.org> | |
408 | ||
409 | * dns/resolver.py (zone_for_name): A query name resulting in a | |
410 | CNAME or DNAME response to a node which had an SOA was incorrectly | |
411 | treated as a zone origin. In these cases, we should just look | |
412 | higher. Thanks to Gert Berger for reporting this problem. | |
413 | ||
414 | * Added zonediff.py to examples. This program compares two zones | |
415 | and shows the differences either in diff-like plain text, or | |
416 | HTML. Thanks to Dennis Kaarsemaker for contributing this | |
417 | useful program. | |
418 | ||
419 | 2010-10-27 Bob Halley <halley@dnspython.org> | |
420 | ||
421 | * Incorporate a patch to use poll() instead of select() by | |
422 | default on platforms which support it. Thanks to | |
423 | Peter Schüller and Spotify for the contribution. | |
424 | ||
425 | 2010-10-17 Bob Halley <halley@dnspython.org> | |
426 | ||
427 | * Python prior to 2.5.2 doesn't compute the correct values for | |
428 | HMAC-SHA384 and HMAC-SHA512. We now detect attempts to use | |
429 | them and raise NotImplemented if the Python version is too old. | |
430 | Thanks to Kevin Chen for reporting the problem. | |
431 | ||
432 | * Various routines that took the string forms of rdata types and | |
433 | classes did not permit the strings to be Unicode strings. | |
434 | Thanks to Ryan Workman for reporting the issue. | |
435 | ||
436 | * dns/tsig.py: Added symbolic constants for the algorithm strings. | |
437 | E.g. you can now say dns.tsig.HMAC_MD5 instead of | |
438 | "HMAC-MD5.SIG-ALG.REG.INT". Thanks to Cillian Sharkey for | |
439 | suggesting this improvement. | |
440 | ||
441 | * dns/tsig.py (get_algorithm): fix hashlib compatibility; thanks to | |
442 | Kevin Chen for the patch. | |
443 | ||
444 | * dns/dnssec.py: Added key_id() and make_ds(). | |
445 | ||
446 | * dns/message.py: message.py needs to import dns.edns since it uses | |
447 | it. | |
448 | ||
449 | 2010-05-04 Bob Halley <halley@dnspython.org> | |
450 | ||
451 | * dns/rrset.py (RRset.__init__): "covers" was not passed to the | |
452 | superclass __init__(). Thanks to Shanmuga Rajan for reporting | |
453 | the problem. | |
454 | ||
455 | 2010-03-10 Bob Halley <halley@dnspython.org> | |
456 | ||
457 | * The TSIG algorithm value was passed to use_tsig() incorrectly | |
458 | in some cases. Thanks to 'ducciovigolo' for reporting the problem. | |
459 | ||
460 | 2010-01-26 Bob Halley <halley@dnspython.org> | |
461 | ||
462 | * (Version 1.8.0 released) | |
463 | ||
464 | 2010-01-13 Bob Halley <halley@dnspython.org> | |
465 | ||
466 | * dns/dnssec.py: Added RSASHA256 and RSASHA512 codepoints; added | |
467 | other missing codepoints to _algorithm_by_text. | |
468 | ||
469 | 2010-01-12 Bob Halley <halley@dnspython.org> | |
470 | ||
471 | * Escapes in masterfiles now work correctly. Previously they were | |
472 | only working correctly when the text involved was part of a domain | |
473 | name. | |
474 | ||
475 | * dns/tokenizer.py: The tokenizer's get() method now returns Token | |
476 | objects, not (type, text) tuples. | |
477 | ||
478 | 2009-11-13 Bob Halley <halley@dnspython.org> | |
479 | ||
480 | * Support has been added for hmac-sha1, hmac-sha224, hmac-sha256, | |
481 | hmac-sha384 and hmac-sha512. Thanks to Kevin Chen for a | |
482 | thoughtful, high quality patch. | |
483 | ||
484 | * dns/update.py (Update::present): A zero TTL was not added if | |
485 | present() was called with a single rdata, causing _add() to be | |
486 | unhappy. Thanks to Eugene Kim for reporting the problem and | |
487 | submitting a patch. | |
488 | ||
489 | * dns/entropy.py: Use os.urandom() if present. Don't seed until | |
490 | someone wants randomness. | |
491 | ||
492 | 2009-09-16 Bob Halley <halley@dnspython.org> | |
493 | ||
494 | * dns/entropy.py: The entropy module needs locking in order to be | |
495 | used safely in a multithreaded environment. Thanks to Beda Kosata | |
496 | for reporting the problem. | |
497 | ||
498 | 2009-07-27 Bob Halley <halley@dnspython.org> | |
499 | ||
500 | * dns/query.py (xfr): The socket was not set to nonblocking mode. | |
501 | Thanks to Erik Romijn for reporting this problem. | |
502 | ||
503 | 2009-07-23 Bob Halley <halley@dnspython.org> | |
504 | ||
505 | * dns/rdtypes/IN/SRV.py (SRV._cmp): SRV records were compared | |
506 | incorrectly due to a cut-and-paste error. Thanks to Tommie | |
507 | Gannert for reporting this bug. | |
508 | ||
509 | * dns/e164.py (query): The resolver parameter was not used. | |
510 | Thanks to Matías Bellone for reporting this bug. | |
511 | ||
512 | 2009-06-23 Bob Halley <halley@dnspython.org> | |
513 | ||
514 | * dns/entropy.py (EntropyPool.__init__): open /dev/random unbuffered; | |
515 | there's no need to consume more randomness than we need. Thanks | |
516 | to Brian Wellington for the patch. | |
517 | ||
518 | 2009-06-19 Bob Halley <halley@dnspython.org> | |
519 | ||
520 | * (Version 1.7.1 released) | |
521 | ||
522 | 2009-06-19 Bob Halley <halley@dnspython.org> | |
523 | ||
524 | * DLV.py was omitted from the kit | |
525 | ||
526 | * Negative prerequisites were not handled correctly in _get_section(). | |
527 | ||
528 | 2009-06-19 Bob Halley <halley@dnspython.org> | |
529 | ||
530 | * (Version 1.7.0 released) | |
531 | ||
532 | 2009-06-19 Bob Halley <halley@dnspython.org> | |
533 | ||
534 | * On Windows, the resolver set the domain incorrectly. Thanks | |
535 | to Brandon Carpenter for reporting this bug. | |
536 | ||
537 | * Added a to_digestable() method to rdata classes; it returns the | |
538 | digestable form (i.e. DNSSEC canonical form) of the rdata. For | |
539 | most rdata types this is the same uncompressed wire form. For | |
540 | certain older DNS RR types, however, domain names in the rdata | |
541 | are downcased. | |
542 | ||
543 | * Added support for the HIP RR type. | |
544 | ||
545 | 2009-06-18 Bob Halley <halley@dnspython.org> | |
546 | ||
547 | * Added support for the DLV RR type. | |
548 | ||
549 | * Added various DNSSEC related constants (e.g. algorithm identifiers, | |
550 | flag values). | |
551 | ||
552 | * dns/tsig.py: Added support for BADTRUNC result code. | |
553 | ||
554 | * dns/query.py (udp): When checking that addresses are the same, | |
555 | use the binary form of the address in the comparison. This | |
556 | ensures that we don't treat addresses as different if they have | |
557 | equivalent but differing textual representations. E.g. "1:00::1" | |
558 | and "1::1" represent the same address but are not textually equal. | |
559 | Thanks to Kim Davies for reporting this bug. | |
560 | ||
561 | * The resolver's query() method now has an optional 'source' parameter, | |
562 | allowing the source IP address to be specified. Thanks to | |
563 | Alexander Lind for suggesting the change and sending a patch. | |
564 | ||
565 | * Added NSEC3 and NSEC3PARAM support. | |
566 | ||
567 | 2009-06-17 Bob Halley <halley@dnspython.org> | |
568 | ||
569 | * Fixed NSEC.to_text(), which was only printing the last window. | |
570 | Thanks to Brian Wellington for finding the problem and fixing it. | |
571 | ||
572 | 2009-03-30 Bob Halley <halley@dnspython.org> | |
573 | ||
574 | * dns/query.py (xfr): Allow UDP IXFRs. Use "one_rr_per_rrset" mode when | |
575 | doing IXFR. | |
576 | ||
577 | 2009-03-30 Bob Halley <halley@dnspython.org> | |
578 | ||
579 | * Add "one_rr_per_rrset" mode switch to methods which parse | |
580 | messages from wire format (e.g. dns.message.from_wire(), | |
581 | dns.query.udp(), dns.query.tcp()). If set, each RR read is | |
582 | placed in its own RRset (instead of being coalesced). | |
583 | ||
584 | 2009-03-30 Bob Halley <halley@dnspython.org> | |
585 | ||
586 | * Added EDNS option support. | |
587 | ||
588 | 2008-10-16 Bob Halley <halley@dnspython.org> | |
589 | ||
590 | * dns/rdtypes/ANY/DS.py: The from_text() parser for DS RRs did not | |
591 | allow multiple Base64 chunks. Thanks to Rakesh Banka for | |
592 | finding this bug and submitting a patch. | |
593 | ||
594 | 2008-10-08 Bob Halley <halley@dnspython.org> | |
595 | ||
596 | * Add entropy module. | |
597 | ||
598 | * When validating TSIGs, we need to use the absolute name. | |
599 | ||
600 | 2008-06-03 Bob Halley <halley@dnspython.org> | |
601 | ||
602 | * dns/message.py (Message.set_rcode): The mask used preserved the | |
603 | extended rcode, instead of everything else in ednsflags. | |
604 | ||
605 | * dns/message.py (Message.use_edns): ednsflags was not kept | |
606 | coherent with the specified edns version. | |
607 | ||
608 | 2008-02-06 Bob Halley <halley@dnspython.org> | |
609 | ||
610 | * dns/ipv6.py (inet_aton): We could raise an exception other than | |
611 | dns.exception.SyntaxError in some cases. | |
612 | ||
613 | * dns/tsig.py: Raise an exception when the peer has set a non-zero | |
614 | TSIG error. | |
615 | ||
616 | 2007-11-25 Bob Halley <halley@dnspython.org> | |
617 | ||
618 | * (Version 1.6.0 released) | |
619 | ||
620 | 2007-11-25 Bob Halley <halley@dnspython.org> | |
621 | ||
622 | * dns/query.py (_wait_for): if select() raises an exception due to | |
623 | EINTR, we should just select() again. | |
624 | ||
625 | 2007-06-13 Bob Halley <halley@dnspython.org> | |
626 | ||
627 | * dns/inet.py: Added is_multicast(). | |
628 | ||
629 | * dns/query.py (udp): If the queried address is a multicast address, then | |
630 | don't check that the address of the response is the same as the address | |
631 | queried. | |
632 | ||
633 | 2007-05-24 Bob Halley <halley@dnspython.org> | |
634 | ||
635 | * dns/rdtypes/IN/NAPTR.py: NAPTR comparisons didn't compare the | |
636 | preference field due to a typo. | |
637 | ||
638 | 2007-02-07 Bob Halley <halley@dnspython.org> | |
639 | ||
640 | * dns/resolver.py: Integrate code submitted by Paul Marks to | |
641 | determine whether a Windows NIC is enabled. The way dnspython | |
642 | used to do this does not work on Windows Vista. | |
643 | ||
644 | 2006-12-10 Bob Halley <halley@dnspython.org> | |
645 | ||
646 | * (Version 1.5.0 released) | |
647 | ||
648 | 2006-11-03 Bob Halley <halley@dnspython.org> | |
649 | ||
650 | * dns/rdtypes/IN/DHCID.py: Added support for the DHCID RR type. | |
651 | ||
652 | 2006-11-02 Bob Halley <halley@dnspython.org> | |
653 | ||
654 | * dns/query.py (udp): Messages from unexpected sources can now be | |
655 | ignored by setting ignore_unexpected to True. | |
656 | ||
657 | 2006-10-31 Bob Halley <halley@dnspython.org> | |
658 | ||
659 | * dns/query.py (udp): When raising UnexpectedSource, add more | |
660 | detail about what went wrong to the exception. | |
661 | ||
662 | 2006-09-22 Bob Halley <halley@dnspython.org> | |
663 | ||
664 | * dns/message.py (Message.use_edns): add reasonable defaults for | |
665 | the ednsflags, payload, and request_payload parameters. | |
666 | ||
667 | * dns/message.py (Message.want_dnssec): add a convenience method for | |
668 | enabling/disabling the "DNSSEC desired" flag in requests. | |
669 | ||
670 | * dns/message.py (make_query): add "use_edns" and "want_dnssec" | |
671 | parameters. | |
672 | ||
673 | 2006-08-17 Bob Halley <halley@dnspython.org> | |
674 | ||
675 | * dns/resolver.py (Resolver.read_resolv_conf): If /etc/resolv.conf | |
676 | doesn't exist, just use the default resolver configuration (i.e. | |
677 | the same thing we would have used if resolv.conf had existed and | |
678 | been empty). | |
679 | ||
680 | 2006-07-26 Bob Halley <halley@dnspython.org> | |
681 | ||
682 | * dns/resolver.py (Resolver._config_win32_fromkey): fix | |
683 | cut-and-paste error where we passed the wrong variable to | |
684 | self._config_win32_search(). Thanks to David Arnold for finding | |
685 | the bug and submitting a patch. | |
686 | ||
687 | 2006-07-20 Bob Halley <halley@dnspython.org> | |
688 | ||
689 | * dns/resolver.py (Answer): Add more support for the sequence | |
690 | protocol, forwarding requests to the answer object's rrset. | |
691 | E.g. "for a in answer" is equivalent to "for a in answer.rrset", | |
692 | "answer[i]" is equivalent to "answer.rrset[i]", and | |
693 | "answer[i:j]" is equivalent to "answer.rrset[i:j]". | |
694 | ||
695 | 2006-07-19 Bob Halley <halley@dnspython.org> | |
696 | ||
697 | * dns/query.py (xfr): Add IXFR support. | |
698 | ||
699 | 2006-06-22 Bob Halley <halley@dnspython.org> | |
700 | ||
701 | * dns/rdtypes/IN/IPSECKEY.py: Added support for the IPSECKEY RR type. | |
702 | ||
703 | 2006-06-21 Bob Halley <halley@dnspython.org> | |
704 | ||
705 | * dns/rdtypes/ANY/SPF.py: Added support for the SPF RR type. | |
706 | ||
707 | 2006-06-02 Bob Halley <halley@dnspython.org> | |
708 | ||
709 | * (Version 1.4.0 released) | |
710 | ||
711 | 2006-04-25 Bob Halley <halley@dnspython.org> | |
712 | ||
713 | * dns/rrset.py (RRset.to_rdataset): Added a convenience method | |
714 | to convert an rrset into an rdataset. | |
715 | ||
716 | 2006-03-27 Bob Halley <halley@dnspython.org> | |
717 | ||
718 | * Added dns.e164.query(). This function can be used to look for | |
719 | NAPTR RRs for a specified number in several domains, e.g.: | |
720 | ||
721 | dns.e164.query('16505551212', | |
722 | ['e164.dnspython.org.', 'e164.arpa.']) | |
723 | ||
724 | 2006-03-26 Bob Halley <halley@dnspython.org> | |
725 | ||
726 | * dns/resolver.py (Resolver.query): The resolver deleted from | |
727 | a list while iterating it, which makes the iterator unhappy. | |
728 | ||
729 | 2006-03-17 Bob Halley <halley@dnspython.org> | |
730 | ||
731 | * dns/resolver.py (Resolver.query): The resolver needlessly | |
732 | delayed responses for successful queries. | |
733 | ||
734 | 2006-01-18 Bob Halley <halley@dnspython.org> | |
735 | ||
736 | * dns/rdata.py: added a validate() method to the rdata class. If | |
737 | you change an rdata by assigning to its fields, it is a good | |
738 | idea to call validate() when you are done making changes. | |
739 | For example, if 'r' is an MX record and then you execute: | |
740 | ||
741 | r.preference = 100000 # invalid, because > 65535 | |
742 | r.validate() | |
743 | ||
744 | The validation will fail and an exception will be raised. | |
745 | ||
746 | 2006-01-11 Bob Halley <halley@dnspython.org> | |
747 | ||
748 | * dns/ttl.py: TTLs are now bounds checked to be within the closed | |
749 | interval [0, 2^31 - 1]. | |
750 | ||
751 | * The BIND 8 TTL syntax is now accepted in the SOA refresh, retry, | |
752 | expire, and minimum fields, and in the original_ttl field of | |
753 | SIG and RRSIG records. | |
754 | ||
755 | 2006-01-04 Bob Halley <halley@dnspython.org> | |
756 | ||
757 | * dns/resolver.py: The windows registry irritatingly changes the | |
758 | list element delimiter in between ' ' and ',' (and vice-versa) | |
759 | in various versions of windows. We now cope by always looking | |
760 | for either one (' ' first). | |
761 | ||
762 | 2005-12-27 Bob Halley <halley@dnspython.org> | |
763 | ||
764 | * dns/e164.py: Added routines to convert between E.164 numbers and | |
765 | their ENUM domain name equivalents. | |
766 | ||
767 | * dns/reversename.py: Added routines to convert between IPv4 and | |
768 | IPv6 addresses and their DNS reverse-map equivalents. | |
769 | ||
770 | 2005-12-18 Bob Halley <halley@dnspython.org> | |
771 | ||
772 | * dns/rdtypes/ANY/LOC.py (_tuple_to_float): The sign was lost when | |
773 | converting a tuple into a float, which broke conversions of | |
774 | south latitudes and west longitudes. | |
775 | ||
776 | 2005-11-17 Bob Halley <halley@dnspython.org> | |
777 | ||
778 | * dns/zone.py: The 'origin' parameter to from_text() and from_file() | |
779 | is now optional. If not specified, dnspython will use the | |
780 | first $ORIGIN in the text as the zone's origin. | |
781 | ||
782 | * dns/zone.py: Sanity checks of the zone's origin node can now | |
783 | be disabled. | |
784 | ||
785 | 2005-11-12 Bob Halley <halley@dnspython.org> | |
786 | ||
787 | * dns/name.py: Preliminary Unicode support has been added for | |
788 | domain names. Running dns.name.from_text() on a Unicode string | |
789 | will now encode each label using the IDN ACE encoding. The | |
790 | to_unicode() method may be used to convert a dns.name.Name with | |
791 | IDN ACE labels back into a Unicode string. This functionality | |
792 | requires Python 2.3 or greater. | |
793 | ||
794 | 2005-10-31 Bob Halley <halley@dnspython.org> | |
795 | ||
796 | * (Version 1.3.5 released) | |
797 | ||
798 | 2005-10-12 Bob Halley <halley@dnspython.org> | |
799 | ||
800 | * dns/zone.py: Zone.iterate_rdatasets() and Zone.iterate_rdatas() | |
801 | did not have a default rdtype of dns.rdatatype.ANY as their | |
802 | docstrings said they did. They do now. | |
803 | ||
804 | 2005-10-06 Bob Halley <halley@dnspython.org> | |
805 | ||
806 | * dns/name.py: Added the parent() method, which returns the | |
807 | parent of a name. | |
808 | ||
809 | 2005-10-01 Bob Halley <halley@dnspython.org> | |
810 | ||
811 | * dns/resolver.py: Added zone_for_name() helper, which returns | |
812 | the name of the zone which contains the specified name. | |
813 | ||
814 | * dns/resolver.py: Added get_default_resolver(), which returns | |
815 | the default resolver, initializing it if necessary. | |
816 | ||
817 | 2005-09-29 Bob Halley <halley@dnspython.org> | |
818 | ||
819 | * dns/resolver.py (Resolver._compute_timeout): If time goes | |
820 | backwards a little bit, ignore it. | |
821 | ||
822 | 2005-07-31 Bob Halley <halley@dnspython.org> | |
823 | ||
824 | * (Version 1.3.4 released) | |
825 | ||
826 | 2005-07-31 Bob Halley <halley@dnspython.org> | |
827 | ||
828 | * dns/message.py (make_response): Trying to respond to a response | |
829 | threw a NameError while trying to throw a FormErr since it used | |
830 | the wrong name for the FormErr exception. | |
831 | ||
832 | * dns/query.py (_connect): We needed to ignore EALREADY too. | |
833 | ||
834 | * dns/query.py: Optional "source" and "source_port" parameters | |
835 | have been added to udp(), tcp(), and xfr(). Thanks to Ralf | |
836 | Weber for suggesting the change and providing a patch. | |
837 | ||
838 | 2005-06-05 Bob Halley <halley@dnspython.org> | |
839 | ||
840 | * dns/query.py: The requirement that the "where" parameter be | |
841 | an IPv4 or IPv6 address is now documented. | |
842 | ||
843 | 2005-06-04 Bob Halley <halley@dnspython.org> | |
844 | ||
845 | * dns/resolver.py: The resolver now does exponential backoff | |
846 | each time it runs through all of the nameservers. | |
847 | ||
848 | * dns/resolver.py: rcodes which indicate a nameserver is likely | |
849 | to be a "permanent failure" for a query cause the nameserver | |
850 | to be removed from the mix for that query. | |
851 | ||
852 | 2005-01-30 Bob Halley <halley@dnspython.org> | |
853 | ||
854 | * (Version 1.3.3 released) | |
855 | ||
856 | 2004-10-25 Bob Halley <halley@dnspython.org> | |
857 | ||
858 | * dns/rdtypes/ANY/TXT.py (TXT.from_text): The masterfile parser | |
859 | incorrectly rejected TXT records where a value was not quoted. | |
860 | ||
861 | 2004-10-11 Bob Halley <halley@dnspython.org> | |
862 | ||
863 | * dns/message.py: Added make_response(), which creates a skeletal | |
864 | response for the specified query. Added opcode() and set_opcode() | |
865 | convenience methods to the Message class. Added the request_payload | |
866 | attribute to the Message class. | |
867 | ||
868 | 2004-10-10 Bob Halley <halley@dnspython.org> | |
869 | ||
870 | * dns/zone.py (from_xfr): dns.zone.from_xfr() in relativization | |
871 | mode incorrectly set zone.origin to the empty name. | |
872 | ||
873 | 2004-09-02 Bob Halley <halley@dnspython.org> | |
874 | ||
875 | * dns/name.py (Name.to_wire): The 'file' parameter to | |
876 | Name.to_wire() is now optional; if omitted, the wire form will | |
877 | be returned as the value of the function. | |
878 | ||
879 | 2004-08-14 Bob Halley <halley@dnspython.org> | |
880 | ||
881 | * dns/message.py (Message.find_rrset): find_rrset() now uses an | |
882 | index, vastly improving the from_wire() performance of large | |
883 | messages such as zone transfers. | |
884 | ||
885 | 2004-08-07 Bob Halley <halley@dnspython.org> | |
886 | ||
887 | * (Version 1.3.2 released) | |
888 | ||
889 | 2004-08-04 Bob Halley <halley@dnspython.org> | |
890 | ||
891 | * dns/query.py: sending queries to a nameserver via IPv6 now | |
892 | works. | |
893 | ||
894 | * dns/inet.py (af_for_address): Add af_for_address(), which looks | |
895 | at a textual-form address and attempts to determine which address | |
896 | family it is. | |
897 | ||
898 | * dns/query.py: the default for the 'af' parameter of the udp(), | |
899 | tcp(), and xfr() functions has been changed from AF_INET to None, | |
900 | which causes dns.inet.af_for_address() to be used to determine the | |
901 | address family. If dns.inet.af_for_address() can't figure it out, | |
902 | we fall back to AF_INET and hope for the best. | |
903 | ||
904 | 2004-07-31 Bob Halley <halley@dnspython.org> | |
905 | ||
906 | * dns/rdtypes/ANY/NSEC.py (NSEC.from_text): The NSEC text format | |
907 | does not allow specifying types by number, so we shouldn't either. | |
908 | ||
909 | * dns/renderer.py: the renderer module didn't import random, | |
910 | causing an exception to be raised if a query id wasn't provided | |
911 | when a Renderer was created. | |
912 | ||
913 | * dns/resolver.py (Resolver.query): the resolver wasn't catching | |
914 | dns.exception.Timeout, so a timeout erroneously caused the whole | |
915 | resolution to fail instead of just going on to the next server. | |
916 | ||
917 | 2004-06-16 Bob Halley <halley@dnspython.org> | |
918 | ||
919 | * dns/rdtypes/ANY/LOC.py (LOC.from_text): LOC milliseconds values | |
920 | were converted incorrectly if the length of the milliseconds | |
921 | string was less than 3. | |
922 | ||
923 | 2004-06-06 Bob Halley <halley@dnspython.org> | |
924 | ||
925 | * (Version 1.3.1 released) | |
926 | ||
927 | 2004-05-22 Bob Halley <halley@dnspython.org> | |
928 | ||
929 | * dns/update.py (Update.delete): We erroneously specified a | |
930 | "deleting" value of dns.rdatatype.NONE instead of | |
931 | dns.rdataclass.NONE when the thing being deleted was either an | |
932 | Rdataset instance or an Rdata instance. | |
933 | ||
934 | * dns/rdtypes/ANY/SSHFP.py: Added support for the proposed SSHFP | |
935 | RR type. | |
936 | ||
937 | 2004-05-14 Bob Halley <halley@dnspython.org> | |
938 | ||
939 | * dns/rdata.py (from_text): The masterfile reader did not | |
940 | accept the unknown RR syntax when used with a known RR type. | |
941 | ||
942 | 2004-05-08 Bob Halley <halley@dnspython.org> | |
943 | ||
944 | * dns/name.py (from_text): dns.name.from_text() did not raise | |
945 | an exception if a backslash escape ended prematurely. | |
946 | ||
947 | 2004-04-09 Bob Halley <halley@dnspython.org> | |
948 | ||
949 | * dns/zone.py (_MasterReader._rr_line): The masterfile reader | |
950 | erroneously treated lines starting with leading whitespace but | |
951 | not having any RR definition as an error. It now treats | |
952 | them like a blank line (which is not an error). | |
953 | ||
954 | 2004-04-01 Bob Halley <halley@dnspython.org> | |
955 | ||
956 | * (Version 1.3.0 released) | |
957 | ||
958 | 2004-03-19 Bob Halley <halley@dnspython.org> | |
959 | ||
960 | * Added support for new DNSSEC types RRSIG, NSEC, and DNSKEY. | |
961 | ||
962 | 2004-01-16 Bob Halley <halley@dnspython.org> | |
963 | ||
964 | * dns/query.py (_connect): Windows returns EWOULDBLOCK instead | |
965 | of EINPROGRESS when trying to connect a nonblocking socket. | |
966 | ||
967 | 2003-11-13 Bob Halley <halley@dnspython.org> | |
968 | ||
969 | * dns/rdtypes/ANY/LOC.py (LOC.to_wire): We encoded and decoded LOC | |
970 | incorrectly, since we were interpreting the values of altitude, | |
971 | size, hprec, and vprec in meters instead of centimeters. | |
972 | ||
973 | * dns/rdtypes/IN/WKS.py (WKS.from_wire): The WKS protocol value is | |
974 | encoded with just one octet, not two! | |
975 | ||
976 | 2003-11-09 Bob Halley <halley@dnspython.org> | |
977 | ||
978 | * dns/resolver.py (Cache.maybe_clean): The cleaner deleted items | |
979 | from the dictionary while iterating it, causing a RuntimeError | |
980 | to be raised. Thanks to Mark R. Levinson for the bug report, | |
981 | regression test, and fix. | |
982 | ||
983 | 2003-11-07 Bob Halley <halley@dnspython.org> | |
984 | ||
985 | * (Version 1.2.0 released) | |
986 | ||
987 | 2003-11-03 Bob Halley <halley@dnspython.org> | |
988 | ||
989 | * dns/zone.py (_MasterReader.read): The saved_state now includes | |
990 | the default TTL. | |
991 | ||
992 | 2003-11-01 Bob Halley <halley@dnspython.org> | |
993 | ||
994 | * dns/tokenizer.py (Tokenizer.get): The tokenizer didn't | |
995 | handle escaped delimiters. | |
996 | ||
997 | 2003-10-27 Bob Halley <halley@dnspython.org> | |
998 | ||
999 | * dns/resolver.py (Resolver.read_resolv_conf): If no nameservers | |
1000 | are configured in /etc/resolv.conf, the default nameserver | |
1001 | list should be ['127.0.0.1']. | |
1002 | ||
1003 | 2003-09-08 Bob Halley <halley@dnspython.org> | |
1004 | ||
1005 | * dns/resolver.py (Resolver._config_win32_fromkey): We didn't | |
1006 | catch WindowsError, which can happen if a key is not defined | |
1007 | in the registry. | |
1008 | ||
1009 | 2003-09-06 Bob Halley <halley@dnspython.org> | |
1010 | ||
1011 | * (Version 1.2.0b1 released) | |
1012 | ||
1013 | 2003-09-05 Bob Halley <halley@dnspython.org> | |
1014 | ||
1015 | * dns/query.py: Timeout support has been overhauled to provide | |
1016 | timeouts under Python 2.2 as well as 2.3, and to provide more | |
1017 | accurate expiration. | |
1018 | ||
1019 | 2003-08-30 Bob Halley <halley@dnspython.org> | |
1020 | ||
1021 | * dns/zone.py: dns.exception.SyntaxError is raised for unknown | |
1022 | master file directives. | |
1023 | ||
1024 | 2003-08-28 Bob Halley <halley@dnspython.org> | |
1025 | ||
1026 | * dns/zone.py: $INCLUDE processing is now enabled/disabled using | |
1027 | the allow_include parameter. The default is to process $INCLUDE | |
1028 | for from_file(), and to disallow $INCLUDE for from_text(). The | |
1029 | master reader now calls zone.check_origin_node() by default after | |
1030 | the zone has been read. find_rdataset() called get_node() instead | |
1031 | of find_node(), which result in an incorrect exception. The | |
1032 | relativization state of a zone is now remembered and applied | |
1033 | consistently when looking up names. from_xfr() now supports | |
1034 | relativization like the _MasterReader. | |
1035 | ||
1036 | 2003-08-22 Bob Halley <halley@dnspython.org> | |
1037 | ||
1038 | * dns/zone.py: The _MasterReader now understands $INCLUDE. | |
1039 | ||
1040 | 2003-08-12 Bob Halley <halley@dnspython.org> | |
1041 | ||
1042 | * dns/zone.py: The _MasterReader now specifies the file and line | |
1043 | number when a syntax error occurs. The BIND 8 TTL format is now | |
1044 | understood when loading a zone, though it will never be emitted. | |
1045 | The from_file() function didn't pass the zone_factory parameter | |
1046 | to from_text(). | |
1047 | ||
1048 | 2003-08-10 Bob Halley <halley@dnspython.org> | |
1049 | ||
1050 | * (Version 1.1.0 released) | |
1051 | ||
1052 | 2003-08-07 Bob Halley <halley@dnspython.org> | |
1053 | ||
1054 | * dns/update.py (Update._add): A typo meant that _add would | |
1055 | fail if the thing being added was an Rdata object (as | |
1056 | opposed to an Rdataset or the textual form of an Rdata). | |
1057 | ||
1058 | 2003-08-05 Bob Halley <halley@dnspython.org> | |
1059 | ||
1060 | * dns/set.py: the simple Set class has been moved to its | |
1061 | own module, and augmented to support more set operations. | |
1062 | ||
1063 | 2003-08-04 Bob Halley <halley@dnspython.org> | |
1064 | ||
1065 | * Node and all rdata types have been "slotted". This speeds | |
1066 | things up a little and reduces memory usage noticeably. | |
1067 | ||
1068 | 2003-08-02 Bob Halley <halley@dnspython.org> | |
1069 | ||
1070 | * (Version 1.1.0c1 released) | |
1071 | ||
1072 | 2003-08-02 Bob Halley <halley@dnspython.org> | |
1073 | ||
1074 | * dns/rdataset.py: SimpleSets now support more set options. | |
1075 | ||
1076 | * dns/message.py: Added the get_rrset() method. from_file() now | |
1077 | allows Unicode filenames and turns on universal newline support if | |
1078 | it opens the file itself. | |
1079 | ||
1080 | * dns/node.py: Added the delete_rdataset() and replace_rdataset() | |
1081 | methods. | |
1082 | ||
1083 | * dns/zone.py: Added the delete_node(), delete_rdataset(), and | |
1084 | replace_rdataset() methods. from_file() now allows Unicode | |
1085 | filenames and turns on universal newline support if it opens the | |
1086 | file itself. Added a to_file() method. | |
1087 | ||
1088 | 2003-08-01 Bob Halley <halley@dnspython.org> | |
1089 | ||
1090 | * dns/opcode.py: Opcode from/to text converters now understand | |
1091 | numeric opcodes. The to_text() method will return a numeric opcode | |
1092 | string if it doesn't know a text name for the opcode. | |
1093 | ||
1094 | * dns/message.py: Added set_rcode(). Fixed code where ednsflags | |
1095 | wasn't treated as a long. | |
1096 | ||
1097 | * dns/rcode.py: ednsflags wasn't treated as a long. Rcode from/to | |
1098 | text converters now understand numeric rcodes. The to_text() | |
1099 | method will return a numeric rcode string if it doesn't know | |
1100 | a text name for the rcode. | |
1101 | ||
1102 | * examples/reverse.py: Added a new example program that builds a | |
1103 | reverse (address-to-name) mapping table from the name-to-address | |
1104 | mapping specified by A RRs in zone files. | |
1105 | ||
1106 | * dns/node.py: Added get_rdataset() method. | |
1107 | ||
1108 | * dns/zone.py: Added get_rdataset() and get_rrset() methods. Added | |
1109 | iterate_rdatas(). | |
1110 | ||
1111 | 2003-07-31 Bob Halley <halley@dnspython.org> | |
1112 | ||
1113 | * dns/zone.py: Added the iterate_rdatasets() method which returns | |
1114 | a generator which yields (name, rdataset) tuples for all the | |
1115 | rdatasets in the zone matching the specified rdatatype. | |
1116 | ||
1117 | 2003-07-30 Bob Halley <halley@dnspython.org> | |
1118 | ||
1119 | * (Version 1.1.0b2 released) | |
1120 | ||
1121 | 2003-07-30 Bob Halley <halley@dnspython.org> | |
1122 | ||
1123 | * dns/zone.py: Added find_rrset() and find_rdataset() convenience | |
1124 | methods. They let you retrieve rdata with the specified name | |
1125 | and type in one call. | |
1126 | ||
1127 | * dns/node.py: Nodes no longer have names; owner names are | |
1128 | associated with nodes in the Zone object's nodes dictionary. | |
1129 | ||
1130 | * dns/zone.py: Zone objects now implement more of the standard | |
1131 | mapping interface. __iter__ has been changed to iterate the keys | |
1132 | rather than values to match the standard mapping interface's | |
1133 | behavior. | |
1134 | ||
1135 | 2003-07-20 Bob Halley <halley@dnspython.org> | |
1136 | ||
1137 | * dns/ipv6.py (inet_ntoa): Handle embedded IPv4 addresses. | |
1138 | ||
1139 | 2003-07-19 Bob Halley <halley@dnspython.org> | |
1140 | ||
1141 | * (Version 1.1.0b1 released) | |
1142 | ||
1143 | 2003-07-18 Bob Halley <halley@dnspython.org> | |
1144 | ||
1145 | * dns/tsig.py: The TSIG validation of TCP streams where not | |
1146 | every message is signed now works correctly. | |
1147 | ||
1148 | * dns/zone.py: Zones can now be compared for equality and | |
1149 | inequality. If the other object in the comparison is also | |
1150 | a zone, then "the right thing" happens; i.e. the zones are | |
1151 | equal iff.: they have the same rdclass, origin, and nodes. | |
1152 | ||
1153 | 2003-07-17 Bob Halley <halley@dnspython.org> | |
1154 | ||
1155 | * dns/message.py (Message.use_tsig): The method now allows for | |
1156 | greater control over the various fields in the generated signature | |
1157 | (e.g. fudge). | |
1158 | (_WireReader._get_section): UnknownTSIGKey is now raised if an | |
1159 | unknown key is encountered, or if a signed message has no keyring. | |
1160 | ||
1161 | 2003-07-16 Bob Halley <halley@dnspython.org> | |
1162 | ||
1163 | * dns/tokenizer.py (Tokenizer._get_char): get_char and unget_char | |
1164 | have been renamed to _get_char and _unget_char since they are not | |
1165 | useful to clients of the tokenizer. | |
1166 | ||
1167 | 2003-07-15 Bob Halley <halley@dnspython.org> | |
1168 | ||
1169 | * dns/zone.py (_MasterReader._rr_line): owner names were being | |
1170 | unconditionally relativized; it makes much more sense for them | |
1171 | to be relativized according to the relativization setting of | |
1172 | the reader. | |
1173 | ||
1174 | 2003-07-12 Bob Halley <halley@dnspython.org> | |
1175 | ||
1176 | * dns/resolver.py (Resolver.read_resolv_conf): The resolv.conf | |
1177 | parser did not allow blank / whitespace-only lines, nor did it | |
1178 | allow comments. Both are now supported. | |
1179 | ||
1180 | 2003-07-11 Bob Halley <halley@dnspython.org> | |
1181 | ||
1182 | * dns/name.py (Name.to_digestable): to_digestable() now | |
1183 | requires an origin to be specified if the name is relative. | |
1184 | It will raise NeedAbsoluteNameOrOrigin if the name is | |
1185 | relative and there is either no origin or the origin is | |
1186 | itself relative. | |
1187 | (Name.split): returned the wrong answer if depth was 0 or depth | |
1188 | was the length of the name. split() now does bounds checking | |
1189 | on depth, and raises ValueError if depth < 0 or depth > the length | |
1190 | of the name. | |
1191 | ||
1192 | 2003-07-10 Bob Halley <halley@dnspython.org> | |
1193 | ||
1194 | * dns/ipv6.py (inet_ntoa): The routine now minimizes its output | |
1195 | strings. E.g. the IPv6 address | |
1196 | "0000:0000:0000:0000:0000:0000:0000:0001" is minimized to "::1". | |
1197 | We do not, however, make any effort to display embedded IPv4 | |
1198 | addresses in the dot-quad notation. | |
1199 | ||
1200 | 2003-07-09 Bob Halley <halley@dnspython.org> | |
1201 | ||
1202 | * dns/inet.py: We now supply our own AF_INET and AF_INET6 | |
1203 | constants since AF_INET6 may not always be available. If the | |
1204 | socket module has AF_INET6, we will use it. If not, we will | |
1205 | use our own value for the constant. | |
1206 | ||
1207 | * dns/query.py: the functions now take an optional af argument | |
1208 | specifying the address family to use when creating the socket. | |
1209 | ||
1210 | * dns/rdatatype.py (is_metatype): a typo caused the function | |
1211 | return true only for type OPT. | |
1212 | ||
1213 | * dns/message.py: message section list elements are now RRsets | |
1214 | instead of Nodes. This API change makes processing messages | |
1215 | easier for many applications. | |
1216 | ||
1217 | 2003-07-07 Bob Halley <halley@dnspython.org> | |
1218 | ||
1219 | * dns/rrset.py: added. An RRset is a named rdataset. | |
1220 | ||
1221 | * dns/rdataset.py (Rdataset.__eq__): rdatasets may now be compared | |
1222 | for equality and inequality with other objects. Rdataset instance | |
1223 | variables are now slotted. | |
1224 | ||
1225 | * dns/message.py: The wire format and text format readers are now | |
1226 | classes. Variables related to reader state have been moved out | |
1227 | of the message class. | |
1228 | ||
1229 | 2003-07-06 Bob Halley <halley@dnspython.org> | |
1230 | ||
1231 | * dns/name.py (from_text): '@' was not interpreted as the empty | |
1232 | name. | |
1233 | ||
1234 | * dns/zone.py: the master file reader derelativized names in rdata | |
1235 | relative to the zone's origin, not relative to the current origin. | |
1236 | The reader now deals with relativization in two steps. The rdata | |
1237 | is read and derelativized using the current origin. The rdata's | |
1238 | relativity is then chosen using the zone origin and the relativize | |
1239 | boolean. Here's an example. | |
1240 | ||
1241 | $ORIGIN foo.example. | |
1242 | $TTL 300 | |
1243 | bar MX 0 blaz | |
1244 | ||
1245 | If the zone origin is example., and relativization is on, then | |
1246 | This fragment will become: | |
1247 | ||
1248 | bar.foo.example. 300 IN MX 0 blaz.foo.example. | |
1249 | ||
1250 | after the first step (derelativization to current origin), and | |
1251 | ||
1252 | bar.foo 300 IN MX 0 blaz.foo | |
1253 | ||
1254 | after the second step (relativization to zone origin). | |
1255 | ||
1256 | * dns/namedict.py: added. | |
1257 | ||
1258 | * dns/zone.py: The master file reader has been made into its | |
1259 | own class. Reader-related instance variables have been moved | |
1260 | form the zone class into the reader class. | |
1261 | ||
1262 | * dns/zone.py: Add node_factory class attribute. An application | |
1263 | can now subclass Zone and Node and have a zone whose nodes are of | |
1264 | the subclassed Node type. The from_text(), from_file(), and | |
1265 | from_xfr() algorithms now take an optional zone_factory argument. | |
1266 | This allows the algorithms to be used to create zones whose class | |
1267 | is a subclass of Zone. | |
1268 | ||
1269 | ||
1270 | 2003-07-04 Bob Halley <halley@dnspython.org> | |
1271 | ||
1272 | * dns/renderer.py: added new wire format rendering module and | |
1273 | converted message.py to use it. Applications which want | |
1274 | fine-grained control over the conversion to wire format may call | |
1275 | the renderer directly, instead of having it called on their behalf | |
1276 | by the message code. | |
1277 | ||
1278 | 2003-07-02 Bob Halley <halley@dnspython.org> | |
1279 | ||
1280 | * dns/name.py (_validate_labels): The NameTooLong test was | |
1281 | incorrect. | |
1282 | ||
1283 | * dns/message.py (Message.to_wire): dns.exception.TooBig is | |
1284 | now raised if the wire encoding exceeds the specified | |
1285 | maximum size. | |
1286 | ||
1287 | 2003-07-01 Bob Halley <halley@dnspython.org> | |
1288 | ||
1289 | * dns/message.py: EDNS encoding was broken. from_text() | |
1290 | didn't parse rcodes, flags, or eflags correctly. Comparing | |
1291 | messages with other types of objects didn't work. | |
1292 | ||
1293 | 2003-06-30 Bob Halley <halley@dnspython.org> | |
1294 | ||
1295 | * (Version 1.0.0 released) | |
1296 | ||
1297 | 2003-06-30 Bob Halley <halley@dnspython.org> | |
1298 | ||
1299 | * dns/rdata.py: Rdatas now implement rich comparisons instead of | |
1300 | __cmp__. | |
1301 | ||
1302 | * dns/name.py: Names now implement rich comparisons instead of | |
1303 | __cmp__. | |
1304 | ||
1305 | * dns/inet.py (inet_ntop): Always use our code, since the code | |
1306 | in the socket module doesn't support AF_INET6 conversions if | |
1307 | IPv6 sockets are not available on the system. | |
1308 | ||
1309 | * dns/resolver.py (Answer.__init__): A dangling CNAME chain was | |
1310 | not raising NoAnswer. | |
1311 | ||
1312 | * Added a simple resolver Cache class. | |
1313 | ||
1314 | * Added an expiration attribute to answer instances. | |
1315 | ||
1316 | 2003-06-24 Bob Halley <halley@dnspython.org> | |
1317 | ||
1318 | * (Version 1.0.0b3 released) | |
1319 | ||
1320 | 2003-06-24 Bob Halley <halley@dnspython.org> | |
1321 | ||
1322 | * Renamed module "DNS" to "dns" to avoid conflicting with | |
1323 | PyDNS. | |
1324 | ||
1325 | 2003-06-23 Bob Halley <halley@dnspython.org> | |
1326 | ||
1327 | * The from_text() relativization controls now work the same way as | |
1328 | the to_text() controls. | |
1329 | ||
1330 | * DNS/rdata.py: The parsing of generic rdata was broken. | |
1331 | ||
1332 | 2003-06-21 Bob Halley <halley@dnspython.org> | |
1333 | ||
1334 | * (Version 1.0.0b2 released) | |
1335 | ||
1336 | 2003-06-21 Bob Halley <halley@dnspython.org> | |
1337 | ||
1338 | * The Python 2.2 socket.inet_aton() doesn't seem to like | |
1339 | '255.255.255.255'. We work around this. | |
1340 | ||
1341 | * Fixed bugs in rdata to_wire() and from_wire() routines of a few | |
1342 | types. These bugs were discovered by running the tests/zone.py | |
1343 | Torture1 test. | |
1344 | ||
1345 | * Added implementation of type APL. | |
1346 | ||
1347 | 2003-06-20 Bob Halley <halley@dnspython.org> | |
1348 | ||
1349 | * DNS/rdtypes/IN/AAAA.py: Use our own versions of inet_ntop and | |
1350 | inet_pton if the socket module doesn't provide them for us. | |
1351 | ||
1352 | * The resolver now does a better job handling exceptions. In | |
1353 | particular, it no longer eats all exceptions; rather it handles | |
1354 | those exceptions it understands, and leaves the rest uncaught. | |
1355 | ||
1356 | * Exceptions have been pulled into their own module. Almost all | |
1357 | exceptions raised by the code are now subclasses of | |
1358 | DNS.exception.DNSException. All form errors are subclasses of | |
1359 | DNS.exception.FormError (which is itself a subclass of | |
1360 | DNS.exception.DNSException). | |
1361 | ||
1362 | 2003-06-19 Bob Halley <halley@dnspython.org> | |
1363 | ||
1364 | * Added implementations of types DS, NXT, SIG, and WKS. | |
1365 | ||
1366 | * __cmp__ for type A and AAAA could produce incorrect results. | |
1367 | ||
1368 | 2003-06-18 Bob Halley <halley@dnspython.org> | |
1369 | ||
1370 | * Started test suites for zone.py and tokenizer.py. | |
1371 | ||
1372 | * Added implementation of type KEY. | |
1373 | ||
1374 | * DNS/rdata.py(_base64ify): \n could be emitted erroneously. | |
1375 | ||
1376 | * DNS/rdtypes/ANY/SOA.py (SOA.from_text): The SOA RNAME field could | |
1377 | be set to the value of MNAME in common cases. | |
1378 | ||
1379 | * DNS/rdtypes/ANY/X25.py: __init__ was broken. | |
1380 | ||
1381 | * DNS/zone.py (from_text): $TTL handling erroneously caused the | |
1382 | next line to be eaten. | |
1383 | ||
1384 | * DNS/tokenizer.py (Tokenizer.get): parsing was broken for empty | |
1385 | quoted strings. Quoted strings didn't handle \ddd escapes. Such | |
1386 | escapes are appear not to comply with RFC 1035, but BIND allows | |
1387 | them and they seem useful, so we allow them too. | |
1388 | ||
1389 | * DNS/rdtypes/ANY/ISDN.py (ISDN.from_text): parsing was | |
1390 | broken for ISDN RRs without subaddresses. | |
1391 | ||
1392 | * DNS/zone.py (from_file): from_file() didn't work because | |
1393 | some required parameters were not passed to from_text(). | |
1394 | ||
1395 | 2003-06-17 Bob Halley <halley@dnspython.org> | |
1396 | ||
1397 | * (Version 1.0.0b1 released) | |
1398 | ||
1399 | 2003-06-17 Bob Halley <halley@dnspython.org> | |
1400 | ||
1401 | * Added implementation of type PX. | |
1402 | ||
1403 | 2003-06-16 Bob Halley <halley@dnspython.org> | |
1404 | ||
1405 | * Added implementation of types CERT, GPOS, LOC, NSAP, NSAP-PTR. | |
1406 | ||
1407 | * DNS/rdatatype.py (_by_value): A cut-and-paste error had broken | |
1408 | NSAP and NSAP-PTR. | |
1409 | ||
1410 | 2003-06-12 Bob Halley <halley@dnspython.org> | |
1411 | ||
1412 | * Created a tests directory and started adding tests. | |
1413 | ||
1414 | * Added "and its documentation" to the permission grant in the | |
1415 | license. | |
1416 | ||
1417 | 2003-06-12 Bob Halley <halley@dnspython.org> | |
1418 | ||
1419 | * DNS/name.py (Name.is_wild): is_wild() erroneously raised IndexError | |
1420 | if the name was empty. | |
1421 | ||
1422 | 2003-06-10 Bob Halley <halley@dnspython.org> | |
1423 | ||
1424 | * Added implementations of types AFSDB, X25, and ISDN. | |
1425 | ||
1426 | * The documentation associated with the various rdata types has been | |
1427 | improved. In particular, instance variables are now described. | |
1428 | ||
1429 | 2003-06-09 Bob Halley <halley@dnspython.org> | |
1430 | ||
1431 | * Added implementations of types HINFO, RP, and RT. | |
1432 | ||
1433 | * DNS/message.py (make_query): Document that make_query() sets | |
1434 | flags to DNS.flags.RD, and chooses a random query id. | |
1435 | ||
1436 | 2003-06-05 Bob Halley <halley@dnspython.org> | |
1437 | ||
1438 | * (Version 1.0.0a2 released) | |
1439 | ||
1440 | 2003-06-05 Bob Halley <halley@dnspython.org> | |
1441 | ||
1442 | * DNS/node.py: removed __getitem__ and __setitem__, since | |
1443 | they are not used by the codebase and were not useful in | |
1444 | general either. | |
1445 | ||
1446 | * DNS/message.py (from_file): from_file() now allows a | |
1447 | filename to be specified instead of a file object. | |
1448 | ||
1449 | * DNS/rdataset.py: The is_compatible() method of the | |
1450 | DNS.rdataset.Rdataset class was deleted. | |
1451 | ||
1452 | 2003-06-04 Bob Halley <halley@dnspython.org> | |
1453 | ||
1454 | * DNS/name.py (class Name): Names are now immutable. | |
1455 | ||
1456 | * DNS/name.py: the is_comparable() method has been removed, since | |
1457 | names are always comparable. | |
1458 | ||
1459 | * DNS/resolver.py (Resolver.query): A query could run for up | |
1460 | to the lifetime + the timeout. This has been corrected and the | |
1461 | query will now only run up to the lifetime. | |
1462 | ||
1463 | 2003-06-03 Bob Halley <halley@dnspython.org> | |
1464 | ||
1465 | * DNS/resolver.py: removed the 'new' function since it is not the | |
1466 | style of the library to have such a function. Call | |
1467 | DNS.resolver.Resolver() to make a new resolver. | |
1468 | ||
1469 | 2003-06-03 Bob Halley <halley@dnspython.org> | |
1470 | ||
1471 | * DNS/resolver.py (Resolver._config_win32_fromkey): The DhcpServer | |
1472 | list is space separated, not comma separated. | |
1473 | ||
1474 | 2003-06-03 Bob Halley <halley@dnspython.org> | |
1475 | ||
1476 | * DNS/update.py: Added an update module to make generating updates | |
1477 | easier. | |
1478 | ||
1479 | 2003-06-03 Bob Halley <halley@dnspython.org> | |
1480 | ||
1481 | * Commas were missing in some of the __all__ entries in various | |
1482 | __init__.py files. | |
1483 | ||
1484 | 2003-05-30 Bob Halley <halley@dnspython.org> | |
1485 | ||
1486 | * (Version 1.0.0a1 released) |
0 | 0 | ISC License |
1 | 1 | |
2 | Copyright (C) 2001-2003 Nominum, Inc. | |
2 | Copyright (C) Dnspython Contributors | |
3 | ||
4 | Permission to use, copy, modify, and/or distribute this software for | |
5 | any purpose with or without fee is hereby granted, provided that the | |
6 | above copyright notice and this permission notice appear in all | |
7 | copies. | |
8 | ||
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL | |
10 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED | |
11 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE | |
12 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |
13 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR | |
14 | PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |
15 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
16 | PERFORMANCE OF THIS SOFTWARE. | |
17 | ||
18 | ||
19 | ||
20 | Copyright (C) 2001-2017 Nominum, Inc. | |
21 | Copyright (C) Google Inc. | |
3 | 22 | |
4 | 23 | Permission to use, copy, modify, and distribute this software and its |
5 | 24 | documentation for any purpose with or without fee is hereby granted, |
0 | include LICENSE ChangeLog TODO | |
0 | include LICENSE ChangeLog README.md | |
1 | 1 | recursive-include examples *.txt *.py |
2 | 2 | recursive-include tests *.txt *.py Makefile *.good example |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
3 | # | |
4 | # Permission to use, copy, modify, and distribute this software and its | |
5 | # documentation for any purpose with or without fee is hereby granted, | |
6 | # provided that the above copyright notice and this permission notice | |
7 | # appear in all copies. | |
8 | # | |
9 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | ||
17 | # $Id: Makefile,v 1.16 2004/03/19 00:17:27 halley Exp $ | |
18 | ||
19 | PYTHON=python | |
20 | PYTHON3=python3 | |
21 | ||
22 | all: | |
23 | ${PYTHON} ./setup.py build | |
24 | ||
25 | install: | |
26 | ${PYTHON} ./setup.py install | |
27 | ||
28 | clean: | |
29 | ${PYTHON} ./setup.py clean --all | |
30 | find . -name '*.pyc' -exec rm {} \; | |
31 | find . -name '*.pyo' -exec rm {} \; | |
32 | rm -f TAGS | |
33 | ||
34 | distclean: clean docclean | |
35 | rm -rf build dist | |
36 | rm -f MANIFEST | |
37 | ||
38 | doco: | |
39 | epydoc -v -n dnspython -u http://www.dnspython.org \ | |
40 | dns/*.py dns/rdtypes/*.py dns/rdtypes/ANY/*.py \ | |
41 | dns/rdtypes/CH/*.py \ | |
42 | dns/rdtypes/IN/*.py | |
43 | ||
44 | dockits: doco | |
45 | mv html dnspython-html | |
46 | tar czf html.tar.gz dnspython-html | |
47 | zip -r html.zip dnspython-html | |
48 | mv dnspython-html html | |
49 | ||
50 | docclean: | |
51 | rm -rf html.tar.gz html.zip html | |
52 | ||
53 | kits: | |
54 | ${PYTHON3} ./setup.py sdist --formats=gztar,zip bdist_wheel | |
55 | ||
56 | tags: | |
57 | find . -name '*.py' -print | etags - | |
58 | ||
59 | check: test | |
60 | ||
61 | test: | |
62 | cd tests; make PYTHON=${PYTHON} test | |
63 | ||
64 | test2: | |
65 | cd tests; make PYTHON=python test | |
66 | ||
67 | test3: | |
68 | cd tests; make PYTHON=${PYTHON3} test | |
69 | ||
70 | lint: | |
71 | pylint dns tests examples/*.py | |
72 | ||
73 | lint3: | |
74 | pylint3 dns tests examples/*.py | |
75 | ||
76 | typecheck: | |
77 | if [ $(shell python -c "import sys; print(sys.version_info[0])") -ne 2 ]; then pip install mypy; mypy examples tests; else echo Skipping typecheck on Python 2; fi |
0 | Metadata-Version: 1.1 | |
1 | Name: dnspython | |
2 | Version: 1.15.0 | |
3 | Summary: DNS toolkit | |
4 | Home-page: http://www.dnspython.org | |
5 | Author: Bob Halley | |
6 | Author-email: halley@dnspython.org | |
7 | License: BSD-like | |
8 | Download-URL: http://www.dnspython.org/kits/1.15.0/dnspython-1.15.0.tar.gz | |
9 | Description: dnspython is a DNS toolkit for Python. It supports almost all | |
10 | record types. It can be used for queries, zone transfers, and dynamic | |
11 | updates. It supports TSIG authenticated messages and EDNS0. | |
12 | ||
13 | dnspython provides both high and low level access to DNS. The high | |
14 | level classes perform queries for data of a given name, type, and | |
15 | class, and return an answer set. The low level classes allow | |
16 | direct manipulation of DNS zones, messages, names, and records. | |
17 | Platform: UNKNOWN | |
18 | Classifier: Development Status :: 5 - Production/Stable | |
19 | Classifier: Intended Audience :: Developers | |
20 | Classifier: Intended Audience :: System Administrators | |
21 | Classifier: License :: Freeware | |
22 | Classifier: Operating System :: Microsoft :: Windows :: Windows 95/98/2000 | |
23 | Classifier: Operating System :: POSIX | |
24 | Classifier: Programming Language :: Python | |
25 | Classifier: Topic :: Internet :: Name Service (DNS) | |
26 | Classifier: Topic :: Software Development :: Libraries :: Python Modules | |
27 | Classifier: Programming Language :: Python :: 2 | |
28 | Classifier: Programming Language :: Python :: 2.6 | |
29 | Classifier: Programming Language :: Python :: 2.7 | |
30 | Classifier: Programming Language :: Python :: 3 | |
31 | Classifier: Programming Language :: Python :: 3.3 | |
32 | Classifier: Programming Language :: Python :: 3.4 | |
33 | Classifier: Programming Language :: Python :: 3.5 | |
34 | Provides: dns |
0 | # dnspython | |
1 | ||
2 | [![Build Status](https://travis-ci.org/rthalley/dnspython.svg?branch=master)](https://travis-ci.org/rthalley/dnspython) | |
3 | ||
4 | ## INTRODUCTION | |
5 | ||
6 | dnspython is a DNS toolkit for Python. It supports almost all record types. It | |
7 | can be used for queries, zone transfers, and dynamic updates. It supports TSIG | |
8 | authenticated messages and EDNS0. | |
9 | ||
10 | dnspython provides both high and low level access to DNS. The high level classes | |
11 | perform queries for data of a given name, type, and class, and return an answer | |
12 | set. The low level classes allow direct manipulation of DNS zones, messages, | |
13 | names, and records. | |
14 | ||
15 | To see a few of the ways dnspython can be used, look in the `examples/` directory. | |
16 | ||
17 | dnspython is a utility to work with DNS, `/etc/hosts` is thus not used. For | |
18 | simple forward DNS lookups, it's better to use `socket.gethostbyname()`. | |
19 | ||
20 | dnspython originated at Nominum where it was developed | |
21 | to facilitate the testing of DNS software. | |
22 | ||
23 | ## INSTALLATION | |
24 | ||
25 | * Many distributions have dnspython packaged for you, so you should | |
26 | check there first. | |
27 | * If you have pip installed, you can do `pip install dnspython` | |
28 | * If not just download the source file and unzip it, then run | |
29 | `sudo python setup.py install` | |
30 | ||
31 | ## ABOUT THIS RELEASE | |
32 | ||
33 | This is dnspython 1.16.0 | |
34 | ||
35 | ### Notices | |
36 | ||
37 | Python 2.x support ends with the release of 1.16.0, unless there are | |
38 | critical bugs in 1.16.0. Future versions of dnspython will only | |
39 | support Python 3. | |
40 | ||
41 | Version numbering of future dnspython releases will also start at 2.0, as | |
42 | incompatible changes will be permitted. We're not planning huge changes at | |
43 | this time, but we'd like to do a better job at IDNA, and there are other | |
44 | API improvements to be made. | |
45 | ||
46 | The ChangeLog has been discontinued. Please see the git history for detailed | |
47 | change information. | |
48 | ||
49 | ### New since 1.15.0: | |
50 | ||
51 | * Much of the internals of dns.query.udp() and dns.query.tcp() have | |
52 | been factored out into dns.query.send_udp(), | |
53 | dns.query.receive_udp(), dns.query.send_tcp(), and | |
54 | dns.query.receive_tcp(). Applications which want more control over | |
55 | the socket may find the new routines helpful; for example it would | |
56 | be easy to send multiple queries over a single TCP connection. | |
57 | ||
58 | * The OPENPGPKEY RR, and the CHAOS class A RR are now supported. | |
59 | ||
60 | * EDNS0 client-subnet is supported. | |
61 | ||
62 | * dns.resover.query() now has a lifetime timeout optional parameter. | |
63 | ||
64 | * pycryptodome and pycryptodomex are now supported and recommended for use | |
65 | instead of pycrypto. | |
66 | ||
67 | * dns.message.from_wire() now has an ignore_trailing option. | |
68 | ||
69 | * type signatures have been provided. | |
70 | ||
71 | * module dns.hash is now deprecated, use standard Python libraries instead. | |
72 | ||
73 | * setup.py supports Cythonization to improve performance. | |
74 | ||
75 | ### Bugs fixed since 1.15.0: | |
76 | ||
77 | * DNSSEC signature validation didn't check names correctly. [Issue #295] | |
78 | ||
79 | * The NXDOMAIN exception should not use its docstring. [Issue #253] | |
80 | ||
81 | * Fixed regression where trailing zeros in APL RRs were not | |
82 | suppressed, and then fixed the problem where trailing zeros | |
83 | were not added back properly on python 3 when needed. | |
84 | ||
85 | * Masterfile TTL defaulting is now harmonized with BIND practice. | |
86 | ||
87 | * dns.query.xfr() now raises on a non-zero rcode. | |
88 | ||
89 | * Rdata module importing is now locked to avoid races. | |
90 | ||
91 | * Several Python 3 incompatibilities have been fixed. | |
92 | ||
93 | * NSEC3 bitmap parsing now works with mulitple NSEC3 windows. | |
94 | ||
95 | * dns.renderer.Render supports TSIG on DNS envelope sequences. | |
96 | ||
97 | * DNSSEC validation now checks names properly [Issue #295] | |
98 | ||
99 | ### New since 1.14.0: | |
100 | ||
101 | * IDNA 2008 support is now available if the "idna" module has been | |
102 | installed and IDNA 2008 is requested. The default IDNA behavior is | |
103 | still IDNA 2003. The new IDNA codec mechanism is currently only | |
104 | useful for direct calls to dns.name.from_text() or | |
105 | dns.name.from_unicode(), but in future releases it will be deployed | |
106 | throughout dnspython, e.g. so that you can read a masterfile with an | |
107 | IDNA 2008 codec in force. | |
108 | ||
109 | * By default, dns.name.to_unicode() is not strict about which | |
110 | version of IDNA the input complies with. Strictness can be | |
111 | requested by using one of the strict IDNA codecs. | |
112 | ||
113 | * The AVC RR is now supported. | |
114 | ||
115 | ### Bugs fixed since 1.14.0: | |
116 | ||
117 | * Some problems with newlines in various output modes have been | |
118 | addressed. | |
119 | ||
120 | * dns.name.to_text() now returns text and not bytes on Python 3.x | |
121 | ||
122 | * Miscellaneous fixes for the Python 2/3 codeline merge. | |
123 | ||
124 | * Many "lint" fixes after the addition of pylint support. | |
125 | ||
126 | * The random number generator reseeds after a fork(). | |
127 | ||
128 | ||
129 | ## REQUIREMENTS | |
130 | ||
131 | Python 2.7 or 3.4+. | |
132 | ||
133 | ||
134 | ## HOME PAGE | |
135 | ||
136 | For the latest in releases, documentation, and information, visit the dnspython | |
137 | home page at http://www.dnspython.org/ | |
138 | ||
139 | ||
140 | ## BUG REPORTS | |
141 | ||
142 | Bug reports may be opened at | |
143 | https://github.com/rthalley/dnspython/issues or sent to | |
144 | bugs@dnspython.org | |
145 | ||
146 | ||
147 | ## MAILING LISTS | |
148 | ||
149 | A number of mailing lists are available. Visit the dnspython home page to | |
150 | subscribe or unsubscribe. | |
151 | ||
152 | ||
153 | ## PRIOR RELEASE INFORMATION | |
154 | ||
155 | ### New since 1.13.0: | |
156 | ||
157 | * CSYNC RRs are now supported. | |
158 | ||
159 | * `dns/message.py` (`make_query`): Setting any value which implies EDNS will | |
160 | turn on EDNS if `use_edns` has not been specified. | |
161 | ||
162 | ### Bugs fixed since 1.13.0: | |
163 | ||
164 | * TSIG signature algorithm setting was broken by the Python 2 and Python 3 code | |
165 | line merge. | |
166 | ||
167 | * A bug in the LOC RR destroyed N/S and E/W distinctions within a degree of the | |
168 | equator or prime merdian respectively. | |
169 | ||
170 | * Misc. fixes to deal with fallout from the Python 2 & 3 merge. | |
171 | Fixes #156, #157, #158, #159, #160. | |
172 | ||
173 | * Running with python optimization on caused issues when stripped docstrings | |
174 | were referenced. Fixes #154 | |
175 | ||
176 | * `dns.zone.from_text()` erroneously required the zone to be provided. | |
177 | Fixes #153 | |
178 | ||
179 | ### New since 1.12.0: | |
180 | ||
181 | * Dnspython now uses a single source for Python 2 and Python 3, eliminating the | |
182 | painful merging between the Python 2 and Python 3 branches. Thank you so much | |
183 | to Arthur Gautier for taking on this challenge and making it work! It was a | |
184 | big job! | |
185 | ||
186 | * Support for Python older than 2.6 dropped. | |
187 | ||
188 | * Support for Python older than 3.3 dropped. | |
189 | ||
190 | * Zone origin can be specified as a string. | |
191 | ||
192 | * A rich string representation for all DNSExceptions. | |
193 | ||
194 | * setuptools has replaced distutils | |
195 | ||
196 | * Added support for CAA, CDS, CDNSKEY, EUI48, EUI64, and URI RR types. | |
197 | ||
198 | * Names now support the pickle protocol. | |
199 | ||
200 | * Ports can be specified per-nameserver in the stub resolver. | |
201 | ||
202 | ### Bugs fixed since 1.12.0: | |
203 | ||
204 | * A number of Unicode name bugs have been fixed. | |
205 | ||
206 | * `resolv.conf` processing now rejects lines with too few tokens. | |
207 | ||
208 | * NameDicts now keep the max-depth value correct, and update properly. | |
209 | ||
210 | ### New since 1.11.1: | |
211 | ||
212 | * Added `dns.zone.to_text()`. | |
213 | ||
214 | * Added support for "options rotate" in `/etc/resolv.conf`. | |
215 | ||
216 | * `dns.rdtypes.ANY.DNSKEY` now has helpers functions to convert between the | |
217 | numeric form of the flags and a set of human-friendly strings | |
218 | ||
219 | * The reverse name of an IPv6 mapped IPv4 address is now in the IPv4 reverse | |
220 | namespace. | |
221 | ||
222 | * The test system can now run the tests without requiring dnspython to be | |
223 | installed. | |
224 | ||
225 | * Preliminary Elliptic Curve DNSSEC Validation (requires ecdsa module) | |
226 | ||
227 | ### Bugs fixed since 1.11.1: | |
228 | ||
229 | * dnspython raised an exception when reading a masterfile starting with leading | |
230 | whitespace | |
231 | ||
232 | * dnspython was affected by a python slicing API bug present on 64-bit windows. | |
233 | ||
234 | * Unicode escaping was applied at the wrong time. | |
235 | ||
236 | * RRSIG `to_text()` did not respect the relativize setting. | |
237 | ||
238 | * APL RRs with zero rdlength were rejected. | |
239 | ||
240 | * The tokenizer could put back an unescaped token. | |
241 | ||
242 | * Making a response to a message signed with TSIG was broken. | |
243 | ||
244 | * The IXFR state machine didn't handle long IXFR diffs. | |
245 | ||
246 | ### New since 1.11.0: | |
247 | ||
248 | * Nothing | |
249 | ||
250 | ### Bugs fixed since 1.11.0: | |
251 | ||
252 | * `dns.resolver.Resolver` erroneously referred to `retry_servfail` | |
253 | instead of `self.retry_servfail`. | |
254 | ||
255 | * `dns.tsigkeyring.to_text()` would fail trying to convert the keyname to text. | |
256 | ||
257 | * Multi-message TSIGs were broken for algorithms other than HMAC-MD5 because we | |
258 | weren't passing the right digest module to the HMAC code. | |
259 | ||
260 | * `dns.dnssec._find_candidate_keys()` tried to extract the key from the wrong | |
261 | variable name. | |
262 | ||
263 | * $GENERATE tests were not backward compatible with python 2.4. | |
264 | ||
265 | ### New since 1.10.0: | |
266 | ||
267 | * $GENERATE support | |
268 | ||
269 | * TLSA RR support | |
270 | ||
271 | * Added set_flags() method to dns.resolver.Resolver | |
272 | ||
273 | ### Bugs fixed since 1.10.0: | |
274 | ||
275 | * Names with offsets >= 2^14 are no longer added to the compression table. | |
276 | ||
277 | * The "::" syntax is not used to shorten a single 16-bit section of the text | |
278 | form an IPv6 address. | |
279 | ||
280 | * Caches are now locked. | |
281 | ||
282 | * YXDOMAIN is raised if seen by the resolver. | |
283 | ||
284 | * Empty rdatasets are not printed. | |
285 | ||
286 | * DNSKEY key tags are no longer assumed to be unique. | |
287 | ||
288 | ### New since 1.9.4: | |
289 | ||
290 | * Added dns.resolver.LRUCache. In this cache implementation, the cache size is | |
291 | limited to a user-specified number of nodes, and when adding a new node to a | |
292 | full cache the least-recently used node is removed. If you're crawling the web | |
293 | or otherwise doing lots of resolutions and you are using a cache, switching | |
294 | to the LRUCache is recommended. | |
295 | ||
296 | * `dns.resolver.query()` will try TCP if a UDP response is truncated. | |
297 | ||
298 | * The python socket module's DNS methods can be now be overridden with | |
299 | implementations that use dnspython's resolver. | |
300 | ||
301 | * Old DNSSEC types KEY, NXT, and SIG have been removed. | |
302 | ||
303 | * Whitespace is allowed in SSHFP fingerprints. | |
304 | ||
305 | * Origin checking in `dns.zone.from_xfr()` can be disabled. | |
306 | ||
307 | * Trailing junk checking can be disabled. | |
308 | ||
309 | * A source port can be specified when creating a resolver query. | |
310 | ||
311 | * All EDNS values may now be specified to `dns.message.make_query()`. | |
312 | ||
313 | ### Bugs fixed since 1.9.4: | |
314 | ||
315 | * IPv4 and IPv6 address processing is now stricter. | |
316 | ||
317 | * Bounds checking of slices in rdata wire processing is now more strict, and | |
318 | bounds errors (e.g. we got less data than was expected) now raise | |
319 | `dns.exception.FormError` rather than `IndexError`. | |
320 | ||
321 | * Specifying a source port without specifying source used to have no effect, but | |
322 | now uses the wildcard address and the specified port. | |
323 | ||
324 | ### New since 1.9.3: | |
325 | ||
326 | * Nothing. | |
327 | ||
328 | ### Bugs fixed since 1.9.3: | |
329 | ||
330 | * The rdata `_wire_cmp()` routine now handles relative names. | |
331 | ||
332 | * The SIG RR implementation was missing `import struct`. | |
333 | ||
334 | ### New since 1.9.2: | |
335 | ||
336 | * A boolean parameter, `raise_on_no_answer`, has been added to the `query()` | |
337 | methods. In no-error, no-data situations, this parameter determines whether | |
338 | `NoAnswer` should be raised or not. If True, `NoAnswer` is raised. If False, | |
339 | then an `Answer()` object with a None rrset will be returned. | |
340 | ||
341 | * Resolver `Answer()` objects now have a canonical_name field. | |
342 | ||
343 | * Rdata now has a `__hash__` method. | |
344 | ||
345 | ### Bugs fixed since 1.9.2: | |
346 | ||
347 | * Dnspython was erroneously doing case-insensitive comparisons of the names in | |
348 | NSEC and RRSIG RRs. | |
349 | ||
350 | * We now use `is` and not `==` when testing what section an RR is in. | |
351 | ||
352 | * The resolver now disallows metaqueries. | |
353 | ||
354 | ### New since 1.9.1: | |
355 | ||
356 | * Nothing. | |
357 | ||
358 | ### Bugs fixed since 1.9.1: | |
359 | ||
360 | * The `dns.dnssec` module didn't work at all due to missing imports that escaped | |
361 | detection in testing because the test suite also did the imports. The third | |
362 | time is the charm! | |
363 | ||
364 | ### New since 1.9.0: | |
365 | ||
366 | * Nothing. | |
367 | ||
368 | ### Bugs fixed since 1.9.0: | |
369 | ||
370 | * The `dns.dnssec` module didn't work with DSA due to namespace contamination | |
371 | from a "from"-style import. | |
372 | ||
373 | ### New since 1.8.0: | |
374 | ||
375 | * dnspython now uses `poll()` instead of `select()` when available. | |
376 | ||
377 | * Basic DNSSEC validation can be done using `dns.dnsec.validate()` and | |
378 | `dns.dnssec.validate_rrsig()` if you have PyCrypto 2.3 or later installed. | |
379 | Complete secure resolution is not yet available. | |
380 | ||
381 | * Added `key_id()` to the DNSSEC module, which computes the DNSSEC key id of a | |
382 | DNSKEY rdata. | |
383 | ||
384 | * Added `make_ds()` to the DNSSEC module, which returns the DS RR for a given | |
385 | DNSKEY rdata. | |
386 | ||
387 | * dnspython now raises an exception if HMAC-SHA284 or HMAC-SHA512 are used with | |
388 | a Python older than 2.5.2. (Older Pythons do not compute the correct value.) | |
389 | ||
390 | * Symbolic constants are now available for TSIG algorithm names. | |
391 | ||
392 | ### Bugs fixed since 1.8.0 | |
393 | ||
394 | * `dns.resolver.zone_for_name()` didn't handle a query response with a CNAME or | |
395 | DNAME correctly in some cases. | |
396 | ||
397 | * When specifying rdata types and classes as text, Unicode strings may now be | |
398 | used. | |
399 | ||
400 | * Hashlib compatibility issues have been fixed. | |
401 | ||
402 | * `dns.message` now imports `dns.edns`. | |
403 | ||
404 | * The TSIG algorithm value was passed incorrectly to `use_tsig()` in some cases. | |
405 | ||
406 | ### New since 1.7.1: | |
407 | ||
408 | * Support for hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384 and hmac-sha512 | |
409 | has been contributed by Kevin Chen. | |
410 | ||
411 | * The tokenizer's tokens are now Token objects instead of (type, value) tuples. | |
412 | ||
413 | ### Bugs fixed since 1.7.1: | |
414 | ||
415 | * Escapes in masterfiles now work correctly. Previously they were only working | |
416 | correctly when the text involved was part of a domain name. | |
417 | ||
418 | * When constructing a DDNS update, if the `present()` method was used with a | |
419 | single rdata, a zero TTL was not added. | |
420 | ||
421 | * The entropy pool needed locking to be thread safe. | |
422 | ||
423 | * The entropy pool's reading of `/dev/random` could cause dnspython to block. | |
424 | ||
425 | * The entropy pool did buffered reads, potentially consuming more randomness | |
426 | than we needed. | |
427 | ||
428 | * The entropy pool did not seed with high quality randomness on Windows. | |
429 | ||
430 | * SRV records were compared incorrectly. | |
431 | ||
432 | * In the e164 query function, the resolver parameter was not used. | |
433 | ||
434 | ### New since 1.7.0: | |
435 | ||
436 | * Nothing | |
437 | ||
438 | ### Bugs fixed since 1.7.0: | |
439 | ||
440 | * The 1.7.0 kitting process inadvertently omitted the code for the DLV RR. | |
441 | ||
442 | * Negative DDNS prerequisites are now handled correctly. | |
443 | ||
444 | ### New since 1.6.0: | |
445 | ||
446 | * Rdatas now have a `to_digestable()` method, which returns the DNSSEC canonical | |
447 | form of the rdata, suitable for use in signature computations. | |
448 | ||
449 | * The NSEC3, NSEC3PARAM, DLV, and HIP RR types are now supported. | |
450 | ||
451 | * An entropy module has been added and is used to randomize query ids. | |
452 | ||
453 | * EDNS0 options are now supported. | |
454 | ||
455 | * UDP IXFR is now supported. | |
456 | ||
457 | * The wire format parser now has a `one_rr_per_rrset` mode, which suppresses the | |
458 | usual coalescing of all RRs of a given type into a single RRset. | |
459 | ||
460 | * Various helpful DNSSEC-related constants are now defined. | |
461 | ||
462 | * The resolver's `query()` method now has an optional `source` parameter, | |
463 | allowing the source IP address to be specified. | |
464 | ||
465 | ### Bugs fixed since 1.6.0: | |
466 | ||
467 | * On Windows, the resolver set the domain incorrectly. | |
468 | ||
469 | * DS RR parsing only allowed one Base64 chunk. | |
470 | ||
471 | * TSIG validation didn't always use absolute names. | |
472 | ||
473 | * `NSEC.to_text()` only printed the last window. | |
474 | ||
475 | * We did not canonicalize IPv6 addresses before comparing them; we | |
476 | would thus treat equivalent but different textual forms, e.g. | |
477 | "1:00::1" and "1::1" as being non-equivalent. | |
478 | ||
479 | * If the peer set a TSIG error, we didn't raise an exception. | |
480 | ||
481 | * Some EDNS bugs in the message code have been fixed (see the ChangeLog | |
482 | for details). | |
483 | ||
484 | ### New since 1.5.0: | |
485 | ||
486 | * Added dns.inet.is_multicast(). | |
487 | ||
488 | ### Bugs fixed since 1.5.0: | |
489 | ||
490 | * If `select()` raises an exception due to EINTR, we should just `select()` | |
491 | again. | |
492 | ||
493 | * If the queried address is a multicast address, then don't check that the | |
494 | address of the response is the same as the address queried. | |
495 | ||
496 | * NAPTR comparisons didn't compare the preference field due to a typo. | |
497 | ||
498 | * Testing of whether a Windows NIC is enabled now works on Vista thanks to code | |
499 | contributed by Paul Marks. | |
500 | ||
501 | ### New since 1.4.0: | |
502 | ||
503 | * Answer objects now support more of the python sequence protocol, forwarding | |
504 | the requests to the answer rrset. E.g. `for a in answer` is equivalent to | |
505 | `for a in answer.rrset`, `answer[i]` is equivalent to `answer.rrset[i]`, and | |
506 | `answer[i:j]` is equivalent to `answer.rrset[i:j]`. | |
507 | ||
508 | * Making requests using EDNS, including indicating DNSSEC awareness, | |
509 | is now easier. For example, you can now say: | |
510 | `q = dns.message.make_query('www.dnspython.org', 'MX', want_dnssec=True)` | |
511 | ||
512 | * `dns.query.xfr()` can now be used for IXFR. | |
513 | ||
514 | * Support has been added for the DHCID, IPSECKEY, and SPF RR types. | |
515 | ||
516 | * UDP messages from unexpected sources can now be ignored by setting | |
517 | `ignore_unexpected` to True when calling `dns.query.udp`. | |
518 | ||
519 | ### Bugs fixed since 1.4.0: | |
520 | ||
521 | * If `/etc/resolv.conf` didn't exist, we raised an exception instead of simply | |
522 | using the default resolver configuration. | |
523 | ||
524 | * In `dns.resolver.Resolver._config_win32_fromkey()`, we were passing the wrong | |
525 | variable to `self._config_win32_search()`. | |
526 | ||
527 | ### New since 1.3.5: | |
528 | ||
529 | * You can now convert E.164 numbers to/from their ENUM name forms: | |
530 | ```python | |
531 | >>> import dns.e164 | |
532 | >>> n = dns.e164.from_e164("+1 555 1212") | |
533 | >>> n | |
534 | <DNS name 2.1.2.1.5.5.5.1.e164.arpa.> | |
535 | >>> dns.e164.to_e164(n) | |
536 | '+15551212' | |
537 | ``` | |
538 | ||
539 | * You can now convert IPv4 and IPv6 address to/from their corresponding DNS | |
540 | reverse map names: | |
541 | ```python | |
542 | >>> import dns.reversename | |
543 | >>> n = dns.reversename.from_address("127.0.0.1") | |
544 | >>> n | |
545 | <DNS name 1.0.0.127.in-addr.arpa.> | |
546 | >>> dns.reversename.to_address(n) | |
547 | '127.0.0.1' | |
548 | ``` | |
549 | ||
550 | * You can now convert between Unicode strings and their IDN ACE form: | |
551 | ```python | |
552 | >>> n = dns.name.from_text(u'les-\u00e9l\u00e8ves.example.') | |
553 | >>> n | |
554 | <DNS name xn--les-lves-50ai.example.> | |
555 | >>> n.to_unicode() | |
556 | u'les-\xe9l\xe8ves.example.' | |
557 | ``` | |
558 | ||
559 | * The origin parameter to `dns.zone.from_text()` and `dns.zone.to_text()` is now | |
560 | optional. If not specified, the origin will be taken from the first $ORIGIN | |
561 | statement in the master file. | |
562 | ||
563 | * Sanity checking of a zone can be disabled; this is useful when working with | |
564 | files which are zone fragments. | |
565 | ||
566 | ### Bugs fixed since 1.3.5: | |
567 | ||
568 | * The correct delimiter was not used when retrieving the list of nameservers | |
569 | from the registry in certain versions of windows. | |
570 | ||
571 | * The floating-point version of latitude and longitude in LOC RRs | |
572 | (`float_latitude` and `float_longitude`) had incorrect signs for south | |
573 | latitudes and west longitudes. | |
574 | ||
575 | * BIND 8 TTL syntax is now accepted in all TTL-like places (i.e. SOA fields | |
576 | refresh, retry, expire, and minimum; SIG/RRSIG field original_ttl). | |
577 | ||
578 | * TTLs are now bounds checked when their text form is parsed, and their values | |
579 | must be in the closed interval `[0, 2^31 - 1]`. | |
580 | ||
581 | ### New since 1.3.4: | |
582 | ||
583 | * In the resolver, if time goes backward a little bit, ignore it. | |
584 | ||
585 | * `zone_for_name()` has been added to the resolver module. It returns the zone | |
586 | which is authoritative for the specified name, which is handy for dynamic | |
587 | update. E.g. | |
588 | ||
589 | import dns.resolver | |
590 | print dns.resolver.zone_for_name('www.dnspython.org') | |
591 | ||
592 | will output `"dnspython.org."` and | |
593 | `print dns.resolver.zone_for_name('a.b.c.d.e.f.example.')` | |
594 | will output `"."`. | |
595 | ||
596 | * The default resolver can be fetched with the `get_default_resolver()` method. | |
597 | ||
598 | * You can now get the parent (immediate superdomain) of a name by using the | |
599 | `parent()` method. | |
600 | ||
601 | * `Zone.iterate_rdatasets()` and `Zone.iterate_rdatas()` now have a default | |
602 | rdtype of `dns.rdatatype.ANY` like the documentation says. | |
603 | ||
604 | * A Dynamic DNS example, ddns.py, has been added. | |
605 | ||
606 | ### New since 1.3.3: | |
607 | ||
608 | * The source address and port may now be specified when calling | |
609 | `dns.query.{udp,tcp,xfr}`. | |
610 | ||
611 | * The resolver now does exponential backoff each time it runs through all of the | |
612 | nameservers. | |
613 | ||
614 | * Rcodes which indicate a nameserver is likely to be a "permanent failure" for a | |
615 | query cause the nameserver to be removed from the mix for that query. | |
616 | ||
617 | ### New since 1.3.2: | |
618 | ||
619 | * `dns.message.Message.find_rrset()` now uses an index, vastly improving the | |
620 | `from_wire()` performance of large messages such as zone transfers. | |
621 | ||
622 | * Added `dns.message.make_response()`, which creates a skeletal response for the | |
623 | specified query. | |
624 | ||
625 | * Added `opcode()` and `set_opcode()` convenience methods to the | |
626 | `dns.message.Message` class. Added the `request_payload` attribute to the | |
627 | Message class. | |
628 | ||
629 | * The `file` parameter of `dns.name.Name.to_wire()` is now optional; if omitted, | |
630 | the wire form will be returned as the value of the function. | |
631 | ||
632 | * `dns.zone.from_xfr()` in relativization mode incorrectly set `zone.origin` to | |
633 | the empty name. | |
634 | ||
635 | * The masterfile parser incorrectly rejected TXT records where a value was not | |
636 | quoted. | |
637 | ||
638 | ### New since 1.3.1: | |
639 | ||
640 | * The NSEC format doesn't allow specifying types by number, so we shouldn't | |
641 | either. (Using the unknown type format is still OK though.) | |
642 | ||
643 | * The resolver wasn't catching `dns.exception.Timeout`, so a timeout erroneously | |
644 | caused the whole resolution to fail instead of just going on to the next | |
645 | server. | |
646 | ||
647 | * The renderer module didn't import random, causing an exception to be raised if | |
648 | a query id wasn't provided when a Renderer was created. | |
649 | ||
650 | * The conversion of LOC milliseconds values from text to binary was incorrect if | |
651 | the length of the milliseconds string was not 3. | |
652 | ||
653 | ### New since 1.3.0: | |
654 | ||
655 | * Added support for the SSHFP type. | |
656 | ||
657 | ### New since 1.2.0: | |
658 | ||
659 | * Added support for new DNSSEC types RRSIG, NSEC, and DNSKEY. | |
660 | ||
661 | * This release fixes all known bugs. | |
662 | ||
663 | * See the ChangeLog file for more detailed information on changes since the | |
664 | prior release. |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
1 | 1 | import decimal |
2 | 2 | from decimal import Context |
3 | 3 | |
4 | if sys.version_info > (3,): | |
4 | PY3 = sys.version_info[0] == 3 | |
5 | PY2 = sys.version_info[0] == 2 | |
6 | ||
7 | ||
8 | if PY3: | |
5 | 9 | long = int |
6 | 10 | xrange = range |
7 | 11 | else: |
9 | 13 | xrange = xrange # pylint: disable=xrange-builtin |
10 | 14 | |
11 | 15 | # unicode / binary types |
12 | if sys.version_info > (3,): | |
16 | if PY3: | |
13 | 17 | text_type = str |
14 | 18 | binary_type = bytes |
15 | 19 | string_types = (str,) |
18 | 22 | return x.decode() |
19 | 23 | def maybe_encode(x): |
20 | 24 | return x.encode() |
25 | def maybe_chr(x): | |
26 | return x | |
27 | def maybe_ord(x): | |
28 | return x | |
21 | 29 | else: |
22 | 30 | text_type = unicode # pylint: disable=unicode-builtin, undefined-variable |
23 | 31 | binary_type = str |
29 | 37 | return x |
30 | 38 | def maybe_encode(x): |
31 | 39 | return x |
40 | def maybe_chr(x): | |
41 | return chr(x) | |
42 | def maybe_ord(x): | |
43 | return ord(x) | |
32 | 44 | |
33 | 45 | |
34 | 46 | def round_py2_compat(what): |
0 | # Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
19 | 21 | import time |
20 | 22 | |
21 | 23 | import dns.exception |
22 | import dns.hash | |
23 | 24 | import dns.name |
24 | 25 | import dns.node |
25 | 26 | import dns.rdataset |
30 | 31 | |
31 | 32 | |
32 | 33 | class UnsupportedAlgorithm(dns.exception.DNSException): |
33 | ||
34 | 34 | """The DNSSEC algorithm is not supported.""" |
35 | 35 | |
36 | 36 | |
37 | 37 | class ValidationFailure(dns.exception.DNSException): |
38 | ||
39 | 38 | """The DNSSEC signature is invalid.""" |
40 | 39 | |
40 | ||
41 | #: RSAMD5 | |
41 | 42 | RSAMD5 = 1 |
43 | #: DH | |
42 | 44 | DH = 2 |
45 | #: DSA | |
43 | 46 | DSA = 3 |
47 | #: ECC | |
44 | 48 | ECC = 4 |
49 | #: RSASHA1 | |
45 | 50 | RSASHA1 = 5 |
51 | #: DSANSEC3SHA1 | |
46 | 52 | DSANSEC3SHA1 = 6 |
53 | #: RSASHA1NSEC3SHA1 | |
47 | 54 | RSASHA1NSEC3SHA1 = 7 |
55 | #: RSASHA256 | |
48 | 56 | RSASHA256 = 8 |
57 | #: RSASHA512 | |
49 | 58 | RSASHA512 = 10 |
59 | #: ECDSAP256SHA256 | |
50 | 60 | ECDSAP256SHA256 = 13 |
61 | #: ECDSAP384SHA384 | |
51 | 62 | ECDSAP384SHA384 = 14 |
63 | #: INDIRECT | |
52 | 64 | INDIRECT = 252 |
65 | #: PRIVATEDNS | |
53 | 66 | PRIVATEDNS = 253 |
67 | #: PRIVATEOID | |
54 | 68 | PRIVATEOID = 254 |
55 | 69 | |
56 | 70 | _algorithm_by_text = { |
74 | 88 | # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that |
75 | 89 | # would cause the mapping not to be true inverse. |
76 | 90 | |
77 | _algorithm_by_value = dict((y, x) for x, y in _algorithm_by_text.items()) | |
91 | _algorithm_by_value = {y: x for x, y in _algorithm_by_text.items()} | |
78 | 92 | |
79 | 93 | |
80 | 94 | def algorithm_from_text(text): |
81 | """Convert text into a DNSSEC algorithm value | |
82 | @rtype: int""" | |
95 | """Convert text into a DNSSEC algorithm value. | |
96 | ||
97 | Returns an ``int``. | |
98 | """ | |
83 | 99 | |
84 | 100 | value = _algorithm_by_text.get(text.upper()) |
85 | 101 | if value is None: |
89 | 105 | |
90 | 106 | def algorithm_to_text(value): |
91 | 107 | """Convert a DNSSEC algorithm value to text |
92 | @rtype: string""" | |
108 | ||
109 | Returns a ``str``. | |
110 | """ | |
93 | 111 | |
94 | 112 | text = _algorithm_by_value.get(value) |
95 | 113 | if text is None: |
104 | 122 | |
105 | 123 | |
106 | 124 | def key_id(key, origin=None): |
125 | """Return the key id (a 16-bit number) for the specified key. | |
126 | ||
127 | Note the *origin* parameter of this function is historical and | |
128 | is not needed. | |
129 | ||
130 | Returns an ``int`` between 0 and 65535. | |
131 | """ | |
132 | ||
107 | 133 | rdata = _to_rdata(key, origin) |
108 | 134 | rdata = bytearray(rdata) |
109 | 135 | if key.algorithm == RSAMD5: |
120 | 146 | |
121 | 147 | |
122 | 148 | def make_ds(name, key, algorithm, origin=None): |
149 | """Create a DS record for a DNSSEC key. | |
150 | ||
151 | *name* is the owner name of the DS record. | |
152 | ||
153 | *key* is a ``dns.rdtypes.ANY.DNSKEY``. | |
154 | ||
155 | *algorithm* is a string describing which hash algorithm to use. The | |
156 | currently supported hashes are "SHA1" and "SHA256". Case does not | |
157 | matter for these strings. | |
158 | ||
159 | *origin* is a ``dns.name.Name`` and will be used as the origin | |
160 | if *key* is a relative name. | |
161 | ||
162 | Returns a ``dns.rdtypes.ANY.DS``. | |
163 | """ | |
164 | ||
123 | 165 | if algorithm.upper() == 'SHA1': |
124 | 166 | dsalg = 1 |
125 | hash = dns.hash.hashes['SHA1']() | |
167 | hash = SHA1.new() | |
126 | 168 | elif algorithm.upper() == 'SHA256': |
127 | 169 | dsalg = 2 |
128 | hash = dns.hash.hashes['SHA256']() | |
170 | hash = SHA256.new() | |
129 | 171 | else: |
130 | 172 | raise UnsupportedAlgorithm('unsupported algorithm "%s"' % algorithm) |
131 | 173 | |
197 | 239 | |
198 | 240 | def _make_hash(algorithm): |
199 | 241 | if _is_md5(algorithm): |
200 | return dns.hash.hashes['MD5']() | |
242 | return MD5.new() | |
201 | 243 | if _is_sha1(algorithm): |
202 | return dns.hash.hashes['SHA1']() | |
244 | return SHA1.new() | |
203 | 245 | if _is_sha256(algorithm): |
204 | return dns.hash.hashes['SHA256']() | |
246 | return SHA256.new() | |
205 | 247 | if _is_sha384(algorithm): |
206 | return dns.hash.hashes['SHA384']() | |
248 | return SHA384.new() | |
207 | 249 | if _is_sha512(algorithm): |
208 | return dns.hash.hashes['SHA512']() | |
250 | return SHA512.new() | |
209 | 251 | raise ValidationFailure('unknown hash for algorithm %u' % algorithm) |
210 | 252 | |
211 | 253 | |
231 | 273 | def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None): |
232 | 274 | """Validate an RRset against a single signature rdata |
233 | 275 | |
234 | The owner name of the rrsig is assumed to be the same as the owner name | |
235 | of the rrset. | |
236 | ||
237 | @param rrset: The RRset to validate | |
238 | @type rrset: dns.rrset.RRset or (dns.name.Name, dns.rdataset.Rdataset) | |
239 | tuple | |
240 | @param rrsig: The signature rdata | |
241 | @type rrsig: dns.rrset.Rdata | |
242 | @param keys: The key dictionary. | |
243 | @type keys: a dictionary keyed by dns.name.Name with node or rdataset | |
244 | values | |
245 | @param origin: The origin to use for relative names | |
246 | @type origin: dns.name.Name or None | |
247 | @param now: The time to use when validating the signatures. The default | |
248 | is the current time. | |
249 | @type now: int | |
276 | The owner name of *rrsig* is assumed to be the same as the owner name | |
277 | of *rrset*. | |
278 | ||
279 | *rrset* is the RRset to validate. It can be a ``dns.rrset.RRset`` or | |
280 | a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple. | |
281 | ||
282 | *rrsig* is a ``dns.rdata.Rdata``, the signature to validate. | |
283 | ||
284 | *keys* is the key dictionary, used to find the DNSKEY associated with | |
285 | a given name. The dictionary is keyed by a ``dns.name.Name``, and has | |
286 | ``dns.node.Node`` or ``dns.rdataset.Rdataset`` values. | |
287 | ||
288 | *origin* is a ``dns.name.Name``, the origin to use for relative names. | |
289 | ||
290 | *now* is an ``int``, the time to use when validating the signatures, | |
291 | in seconds since the UNIX epoch. The default is the current time. | |
250 | 292 | """ |
251 | 293 | |
252 | 294 | if isinstance(origin, string_types): |
253 | 295 | origin = dns.name.from_text(origin, dns.name.root) |
254 | 296 | |
255 | for candidate_key in _find_candidate_keys(keys, rrsig): | |
256 | if not candidate_key: | |
257 | raise ValidationFailure('unknown key') | |
258 | ||
297 | candidate_keys = _find_candidate_keys(keys, rrsig) | |
298 | if candidate_keys is None: | |
299 | raise ValidationFailure('unknown key') | |
300 | ||
301 | for candidate_key in candidate_keys: | |
259 | 302 | # For convenience, allow the rrset to be specified as a (name, |
260 | 303 | # rdataset) tuple as well as a proper rrset |
261 | 304 | if isinstance(rrset, tuple): |
283 | 326 | keyptr = keyptr[2:] |
284 | 327 | rsa_e = keyptr[0:bytes_] |
285 | 328 | rsa_n = keyptr[bytes_:] |
286 | keylen = len(rsa_n) * 8 | |
287 | pubkey = Crypto.PublicKey.RSA.construct( | |
288 | (Crypto.Util.number.bytes_to_long(rsa_n), | |
289 | Crypto.Util.number.bytes_to_long(rsa_e))) | |
290 | sig = (Crypto.Util.number.bytes_to_long(rrsig.signature),) | |
329 | try: | |
330 | pubkey = CryptoRSA.construct( | |
331 | (number.bytes_to_long(rsa_n), | |
332 | number.bytes_to_long(rsa_e))) | |
333 | except ValueError: | |
334 | raise ValidationFailure('invalid public key') | |
335 | sig = rrsig.signature | |
291 | 336 | elif _is_dsa(rrsig.algorithm): |
292 | 337 | keyptr = candidate_key.key |
293 | 338 | (t,) = struct.unpack('!B', keyptr[0:1]) |
300 | 345 | dsa_g = keyptr[0:octets] |
301 | 346 | keyptr = keyptr[octets:] |
302 | 347 | dsa_y = keyptr[0:octets] |
303 | pubkey = Crypto.PublicKey.DSA.construct( | |
304 | (Crypto.Util.number.bytes_to_long(dsa_y), | |
305 | Crypto.Util.number.bytes_to_long(dsa_g), | |
306 | Crypto.Util.number.bytes_to_long(dsa_p), | |
307 | Crypto.Util.number.bytes_to_long(dsa_q))) | |
308 | (dsa_r, dsa_s) = struct.unpack('!20s20s', rrsig.signature[1:]) | |
309 | sig = (Crypto.Util.number.bytes_to_long(dsa_r), | |
310 | Crypto.Util.number.bytes_to_long(dsa_s)) | |
348 | pubkey = CryptoDSA.construct( | |
349 | (number.bytes_to_long(dsa_y), | |
350 | number.bytes_to_long(dsa_g), | |
351 | number.bytes_to_long(dsa_p), | |
352 | number.bytes_to_long(dsa_q))) | |
353 | sig = rrsig.signature[1:] | |
311 | 354 | elif _is_ecdsa(rrsig.algorithm): |
355 | # use ecdsa for NIST-384p -- not currently supported by pycryptodome | |
356 | ||
357 | keyptr = candidate_key.key | |
358 | ||
312 | 359 | if rrsig.algorithm == ECDSAP256SHA256: |
313 | 360 | curve = ecdsa.curves.NIST256p |
314 | 361 | key_len = 32 |
315 | 362 | elif rrsig.algorithm == ECDSAP384SHA384: |
316 | 363 | curve = ecdsa.curves.NIST384p |
317 | 364 | key_len = 48 |
318 | else: | |
319 | # shouldn't happen | |
320 | raise ValidationFailure('unknown ECDSA curve') | |
321 | keyptr = candidate_key.key | |
322 | x = Crypto.Util.number.bytes_to_long(keyptr[0:key_len]) | |
323 | y = Crypto.Util.number.bytes_to_long(keyptr[key_len:key_len * 2]) | |
324 | assert ecdsa.ecdsa.point_is_valid(curve.generator, x, y) | |
365 | ||
366 | x = number.bytes_to_long(keyptr[0:key_len]) | |
367 | y = number.bytes_to_long(keyptr[key_len:key_len * 2]) | |
368 | if not ecdsa.ecdsa.point_is_valid(curve.generator, x, y): | |
369 | raise ValidationFailure('invalid ECDSA key') | |
325 | 370 | point = ecdsa.ellipticcurve.Point(curve.curve, x, y, curve.order) |
326 | 371 | verifying_key = ecdsa.keys.VerifyingKey.from_public_point(point, |
327 | 372 | curve) |
328 | 373 | pubkey = ECKeyWrapper(verifying_key, key_len) |
329 | 374 | r = rrsig.signature[:key_len] |
330 | 375 | s = rrsig.signature[key_len:] |
331 | sig = ecdsa.ecdsa.Signature(Crypto.Util.number.bytes_to_long(r), | |
332 | Crypto.Util.number.bytes_to_long(s)) | |
376 | sig = ecdsa.ecdsa.Signature(number.bytes_to_long(r), | |
377 | number.bytes_to_long(s)) | |
378 | ||
333 | 379 | else: |
334 | 380 | raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm) |
335 | 381 | |
351 | 397 | hash.update(rrlen) |
352 | 398 | hash.update(rrdata) |
353 | 399 | |
354 | digest = hash.digest() | |
355 | ||
356 | if _is_rsa(rrsig.algorithm): | |
357 | # PKCS1 algorithm identifier goop | |
358 | digest = _make_algorithm_id(rrsig.algorithm) + digest | |
359 | padlen = keylen // 8 - len(digest) - 3 | |
360 | digest = struct.pack('!%dB' % (2 + padlen + 1), | |
361 | *([0, 1] + [0xFF] * padlen + [0])) + digest | |
362 | elif _is_dsa(rrsig.algorithm) or _is_ecdsa(rrsig.algorithm): | |
363 | pass | |
364 | else: | |
365 | # Raise here for code clarity; this won't actually ever happen | |
366 | # since if the algorithm is really unknown we'd already have | |
367 | # raised an exception above | |
368 | raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm) | |
369 | ||
370 | if pubkey.verify(digest, sig): | |
400 | try: | |
401 | if _is_rsa(rrsig.algorithm): | |
402 | verifier = pkcs1_15.new(pubkey) | |
403 | # will raise ValueError if verify fails: | |
404 | verifier.verify(hash, sig) | |
405 | elif _is_dsa(rrsig.algorithm): | |
406 | verifier = DSS.new(pubkey, 'fips-186-3') | |
407 | verifier.verify(hash, sig) | |
408 | elif _is_ecdsa(rrsig.algorithm): | |
409 | digest = hash.digest() | |
410 | if not pubkey.verify(digest, sig): | |
411 | raise ValueError | |
412 | else: | |
413 | # Raise here for code clarity; this won't actually ever happen | |
414 | # since if the algorithm is really unknown we'd already have | |
415 | # raised an exception above | |
416 | raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm) | |
417 | # If we got here, we successfully verified so we can return without error | |
371 | 418 | return |
419 | except ValueError: | |
420 | # this happens on an individual validation failure | |
421 | continue | |
422 | # nothing verified -- raise failure: | |
372 | 423 | raise ValidationFailure('verify failure') |
373 | 424 | |
374 | 425 | |
375 | 426 | def _validate(rrset, rrsigset, keys, origin=None, now=None): |
376 | """Validate an RRset | |
377 | ||
378 | @param rrset: The RRset to validate | |
379 | @type rrset: dns.rrset.RRset or (dns.name.Name, dns.rdataset.Rdataset) | |
380 | tuple | |
381 | @param rrsigset: The signature RRset | |
382 | @type rrsigset: dns.rrset.RRset or (dns.name.Name, dns.rdataset.Rdataset) | |
383 | tuple | |
384 | @param keys: The key dictionary. | |
385 | @type keys: a dictionary keyed by dns.name.Name with node or rdataset | |
386 | values | |
387 | @param origin: The origin to use for relative names | |
388 | @type origin: dns.name.Name or None | |
389 | @param now: The time to use when validating the signatures. The default | |
390 | is the current time. | |
391 | @type now: int | |
427 | """Validate an RRset. | |
428 | ||
429 | *rrset* is the RRset to validate. It can be a ``dns.rrset.RRset`` or | |
430 | a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple. | |
431 | ||
432 | *rrsigset* is the signature RRset to be validated. It can be a | |
433 | ``dns.rrset.RRset`` or a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple. | |
434 | ||
435 | *keys* is the key dictionary, used to find the DNSKEY associated with | |
436 | a given name. The dictionary is keyed by a ``dns.name.Name``, and has | |
437 | ``dns.node.Node`` or ``dns.rdataset.Rdataset`` values. | |
438 | ||
439 | *origin* is a ``dns.name.Name``, the origin to use for relative names. | |
440 | ||
441 | *now* is an ``int``, the time to use when validating the signatures, | |
442 | in seconds since the UNIX epoch. The default is the current time. | |
392 | 443 | """ |
393 | 444 | |
394 | 445 | if isinstance(origin, string_types): |
407 | 458 | rrsigrdataset = rrsigset |
408 | 459 | |
409 | 460 | rrname = rrname.choose_relativity(origin) |
410 | rrsigname = rrname.choose_relativity(origin) | |
461 | rrsigname = rrsigname.choose_relativity(origin) | |
411 | 462 | if rrname != rrsigname: |
412 | 463 | raise ValidationFailure("owner names do not match") |
413 | 464 | |
421 | 472 | |
422 | 473 | |
423 | 474 | def _need_pycrypto(*args, **kwargs): |
424 | raise NotImplementedError("DNSSEC validation requires pycrypto") | |
475 | raise NotImplementedError("DNSSEC validation requires pycryptodome/pycryptodomex") | |
476 | ||
425 | 477 | |
426 | 478 | try: |
427 | import Crypto.PublicKey.RSA | |
428 | import Crypto.PublicKey.DSA | |
429 | import Crypto.Util.number | |
430 | validate = _validate | |
431 | validate_rrsig = _validate_rrsig | |
432 | _have_pycrypto = True | |
479 | try: | |
480 | # test we're using pycryptodome, not pycrypto (which misses SHA1 for example) | |
481 | from Crypto.Hash import MD5, SHA1, SHA256, SHA384, SHA512 | |
482 | from Crypto.PublicKey import RSA as CryptoRSA, DSA as CryptoDSA | |
483 | from Crypto.Signature import pkcs1_15, DSS | |
484 | from Crypto.Util import number | |
485 | except ImportError: | |
486 | from Cryptodome.Hash import MD5, SHA1, SHA256, SHA384, SHA512 | |
487 | from Cryptodome.PublicKey import RSA as CryptoRSA, DSA as CryptoDSA | |
488 | from Cryptodome.Signature import pkcs1_15, DSS | |
489 | from Cryptodome.Util import number | |
433 | 490 | except ImportError: |
434 | 491 | validate = _need_pycrypto |
435 | 492 | validate_rrsig = _need_pycrypto |
436 | 493 | _have_pycrypto = False |
437 | ||
438 | try: | |
439 | import ecdsa | |
440 | import ecdsa.ecdsa | |
441 | import ecdsa.ellipticcurve | |
442 | import ecdsa.keys | |
443 | _have_ecdsa = True | |
444 | ||
445 | class ECKeyWrapper(object): | |
446 | ||
447 | def __init__(self, key, key_len): | |
448 | self.key = key | |
449 | self.key_len = key_len | |
450 | ||
451 | def verify(self, digest, sig): | |
452 | diglong = Crypto.Util.number.bytes_to_long(digest) | |
453 | return self.key.pubkey.verifies(diglong, sig) | |
454 | ||
455 | except ImportError: | |
456 | 494 | _have_ecdsa = False |
495 | else: | |
496 | validate = _validate | |
497 | validate_rrsig = _validate_rrsig | |
498 | _have_pycrypto = True | |
499 | ||
500 | try: | |
501 | import ecdsa | |
502 | import ecdsa.ecdsa | |
503 | import ecdsa.ellipticcurve | |
504 | import ecdsa.keys | |
505 | except ImportError: | |
506 | _have_ecdsa = False | |
507 | else: | |
508 | _have_ecdsa = True | |
509 | ||
510 | class ECKeyWrapper(object): | |
511 | ||
512 | def __init__(self, key, key_len): | |
513 | self.key = key | |
514 | self.key_len = key_len | |
515 | ||
516 | def verify(self, digest, sig): | |
517 | diglong = number.bytes_to_long(digest) | |
518 | return self.key.pubkey.verifies(diglong, sig) |
0 | from typing import Union, Dict, Tuple, Optional | |
1 | from . import rdataset, rrset, exception, name, rdtypes, rdata, node | |
2 | import dns.rdtypes.ANY.DS as DS | |
3 | import dns.rdtypes.ANY.DNSKEY as DNSKEY | |
4 | ||
5 | _have_ecdsa : bool | |
6 | _have_pycrypto : bool | |
7 | ||
8 | def validate_rrsig(rrset : Union[Tuple[name.Name, rdataset.Rdataset], rrset.RRset], rrsig : rdata.Rdata, keys : Dict[name.Name, Union[node.Node, rdataset.Rdataset]], origin : Optional[name.Name] = None, now : Optional[int] = None) -> None: | |
9 | ... | |
10 | ||
11 | def validate(rrset: Union[Tuple[name.Name, rdataset.Rdataset], rrset.RRset], rrsigset : Union[Tuple[name.Name, rdataset.Rdataset], rrset.RRset], keys : Dict[name.Name, Union[node.Node, rdataset.Rdataset]], origin=None, now=None) -> None: | |
12 | ... | |
13 | ||
14 | class ValidationFailure(exception.DNSException): | |
15 | ... | |
16 | ||
17 | def make_ds(name : name.Name, key : DNSKEY.DNSKEY, algorithm : str, origin : Optional[name.Name] = None) -> DS.DS: | |
18 | ... |
0 | # Copyright (C) 2006, 2007, 2009, 2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2006-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | """DNS E.164 helpers | |
16 | ||
17 | @var public_enum_domain: The DNS public ENUM domain, e164.arpa. | |
18 | @type public_enum_domain: dns.name.Name object | |
19 | """ | |
20 | ||
17 | """DNS E.164 helpers.""" | |
21 | 18 | |
22 | 19 | import dns.exception |
23 | 20 | import dns.name |
24 | 21 | import dns.resolver |
25 | from ._compat import string_types | |
22 | from ._compat import string_types, maybe_decode | |
26 | 23 | |
24 | #: The public E.164 domain. | |
27 | 25 | public_enum_domain = dns.name.from_text('e164.arpa.') |
28 | 26 | |
29 | 27 | |
30 | 28 | def from_e164(text, origin=public_enum_domain): |
31 | 29 | """Convert an E.164 number in textual form into a Name object whose |
32 | 30 | value is the ENUM domain name for that number. |
33 | @param text: an E.164 number in textual form. | |
34 | @type text: str | |
35 | @param origin: The domain in which the number should be constructed. | |
36 | The default is e164.arpa. | |
37 | @type origin: dns.name.Name object or None | |
38 | @rtype: dns.name.Name object | |
31 | ||
32 | Non-digits in the text are ignored, i.e. "16505551212", | |
33 | "+1.650.555.1212" and "1 (650) 555-1212" are all the same. | |
34 | ||
35 | *text*, a ``text``, is an E.164 number in textual form. | |
36 | ||
37 | *origin*, a ``dns.name.Name``, the domain in which the number | |
38 | should be constructed. The default is ``e164.arpa.``. | |
39 | ||
40 | Returns a ``dns.name.Name``. | |
39 | 41 | """ |
42 | ||
40 | 43 | parts = [d for d in text if d.isdigit()] |
41 | 44 | parts.reverse() |
42 | 45 | return dns.name.from_text('.'.join(parts), origin=origin) |
44 | 47 | |
45 | 48 | def to_e164(name, origin=public_enum_domain, want_plus_prefix=True): |
46 | 49 | """Convert an ENUM domain name into an E.164 number. |
47 | @param name: the ENUM domain name. | |
48 | @type name: dns.name.Name object. | |
49 | @param origin: A domain containing the ENUM domain name. The | |
50 | name is relativized to this domain before being converted to text. | |
51 | @type origin: dns.name.Name object or None | |
52 | @param want_plus_prefix: if True, add a '+' to the beginning of the | |
53 | returned number. | |
54 | @rtype: str | |
50 | ||
51 | Note that dnspython does not have any information about preferred | |
52 | number formats within national numbering plans, so all numbers are | |
53 | emitted as a simple string of digits, prefixed by a '+' (unless | |
54 | *want_plus_prefix* is ``False``). | |
55 | ||
56 | *name* is a ``dns.name.Name``, the ENUM domain name. | |
57 | ||
58 | *origin* is a ``dns.name.Name``, a domain containing the ENUM | |
59 | domain name. The name is relativized to this domain before being | |
60 | converted to text. If ``None``, no relativization is done. | |
61 | ||
62 | *want_plus_prefix* is a ``bool``. If True, add a '+' to the beginning of | |
63 | the returned number. | |
64 | ||
65 | Returns a ``text``. | |
66 | ||
55 | 67 | """ |
56 | 68 | if origin is not None: |
57 | 69 | name = name.relativize(origin) |
62 | 74 | text = b''.join(dlabels) |
63 | 75 | if want_plus_prefix: |
64 | 76 | text = b'+' + text |
65 | return text | |
77 | return maybe_decode(text) | |
66 | 78 | |
67 | 79 | |
68 | 80 | def query(number, domains, resolver=None): |
69 | 81 | """Look for NAPTR RRs for the specified number in the specified domains. |
70 | 82 | |
71 | 83 | e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.']) |
84 | ||
85 | *number*, a ``text`` is the number to look for. | |
86 | ||
87 | *domains* is an iterable containing ``dns.name.Name`` values. | |
88 | ||
89 | *resolver*, a ``dns.resolver.Resolver``, is the resolver to use. If | |
90 | ``None``, the default resolver is used. | |
72 | 91 | """ |
92 | ||
73 | 93 | if resolver is None: |
74 | 94 | resolver = dns.resolver.get_default_resolver() |
75 | 95 | e_nx = dns.resolver.NXDOMAIN() |
0 | from typing import Optional, Iterable | |
1 | from . import name, resolver | |
2 | def from_e164(text : str, origin=name.Name(".")) -> name.Name: | |
3 | ... | |
4 | ||
5 | def to_e164(name : name.Name, origin : Optional[name.Name] = None, want_plus_prefix=True) -> str: | |
6 | ... | |
7 | ||
8 | def query(number : str, domains : Iterable[str], resolver : Optional[resolver.Resolver] = None) -> resolver.Answer: | |
9 | ... |
0 | # Copyright (C) 2009, 2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2009-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
14 | 16 | |
15 | 17 | """EDNS Options""" |
16 | 18 | |
19 | from __future__ import absolute_import | |
20 | ||
21 | import math | |
22 | import struct | |
23 | ||
24 | import dns.inet | |
25 | ||
26 | #: NSID | |
17 | 27 | NSID = 3 |
18 | ||
28 | #: DAU | |
29 | DAU = 5 | |
30 | #: DHU | |
31 | DHU = 6 | |
32 | #: N3U | |
33 | N3U = 7 | |
34 | #: ECS (client-subnet) | |
35 | ECS = 8 | |
36 | #: EXPIRE | |
37 | EXPIRE = 9 | |
38 | #: COOKIE | |
39 | COOKIE = 10 | |
40 | #: KEEPALIVE | |
41 | KEEPALIVE = 11 | |
42 | #: PADDING | |
43 | PADDING = 12 | |
44 | #: CHAIN | |
45 | CHAIN = 13 | |
19 | 46 | |
20 | 47 | class Option(object): |
21 | 48 | |
22 | """Base class for all EDNS option types. | |
23 | """ | |
49 | """Base class for all EDNS option types.""" | |
24 | 50 | |
25 | 51 | def __init__(self, otype): |
26 | 52 | """Initialize an option. |
27 | @param otype: The rdata type | |
28 | @type otype: int | |
53 | ||
54 | *otype*, an ``int``, is the option type. | |
29 | 55 | """ |
30 | 56 | self.otype = otype |
31 | 57 | |
36 | 62 | |
37 | 63 | @classmethod |
38 | 64 | def from_wire(cls, otype, wire, current, olen): |
39 | """Build an EDNS option object from wire format | |
40 | ||
41 | @param otype: The option type | |
42 | @type otype: int | |
43 | @param wire: The wire-format message | |
44 | @type wire: string | |
45 | @param current: The offset in wire of the beginning of the rdata. | |
46 | @type current: int | |
47 | @param olen: The length of the wire-format option data | |
48 | @type olen: int | |
49 | @rtype: dns.edns.Option instance""" | |
65 | """Build an EDNS option object from wire format. | |
66 | ||
67 | *otype*, an ``int``, is the option type. | |
68 | ||
69 | *wire*, a ``binary``, is the wire-format message. | |
70 | ||
71 | *current*, an ``int``, is the offset in *wire* of the beginning | |
72 | of the rdata. | |
73 | ||
74 | *olen*, an ``int``, is the length of the wire-format option data | |
75 | ||
76 | Returns a ``dns.edns.Option``. | |
77 | """ | |
78 | ||
50 | 79 | raise NotImplementedError |
51 | 80 | |
52 | 81 | def _cmp(self, other): |
53 | 82 | """Compare an EDNS option with another option of the same type. |
54 | Return < 0 if self < other, 0 if self == other, | |
55 | and > 0 if self > other. | |
83 | ||
84 | Returns < 0 if < *other*, 0 if == *other*, and > 0 if > *other*. | |
56 | 85 | """ |
57 | 86 | raise NotImplementedError |
58 | 87 | |
97 | 126 | |
98 | 127 | class GenericOption(Option): |
99 | 128 | |
100 | """Generate Rdata Class | |
129 | """Generic Option Class | |
101 | 130 | |
102 | 131 | This class is used for EDNS option types for which we have no better |
103 | 132 | implementation. |
109 | 138 | |
110 | 139 | def to_wire(self, file): |
111 | 140 | file.write(self.data) |
141 | ||
142 | def to_text(self): | |
143 | return "Generic %d" % self.otype | |
112 | 144 | |
113 | 145 | @classmethod |
114 | 146 | def from_wire(cls, otype, wire, current, olen): |
121 | 153 | return 1 |
122 | 154 | return -1 |
123 | 155 | |
156 | ||
157 | class ECSOption(Option): | |
158 | """EDNS Client Subnet (ECS, RFC7871)""" | |
159 | ||
160 | def __init__(self, address, srclen=None, scopelen=0): | |
161 | """*address*, a ``text``, is the client address information. | |
162 | ||
163 | *srclen*, an ``int``, the source prefix length, which is the | |
164 | leftmost number of bits of the address to be used for the | |
165 | lookup. The default is 24 for IPv4 and 56 for IPv6. | |
166 | ||
167 | *scopelen*, an ``int``, the scope prefix length. This value | |
168 | must be 0 in queries, and should be set in responses. | |
169 | """ | |
170 | ||
171 | super(ECSOption, self).__init__(ECS) | |
172 | af = dns.inet.af_for_address(address) | |
173 | ||
174 | if af == dns.inet.AF_INET6: | |
175 | self.family = 2 | |
176 | if srclen is None: | |
177 | srclen = 56 | |
178 | elif af == dns.inet.AF_INET: | |
179 | self.family = 1 | |
180 | if srclen is None: | |
181 | srclen = 24 | |
182 | else: | |
183 | raise ValueError('Bad ip family') | |
184 | ||
185 | self.address = address | |
186 | self.srclen = srclen | |
187 | self.scopelen = scopelen | |
188 | ||
189 | addrdata = dns.inet.inet_pton(af, address) | |
190 | nbytes = int(math.ceil(srclen/8.0)) | |
191 | ||
192 | # Truncate to srclen and pad to the end of the last octet needed | |
193 | # See RFC section 6 | |
194 | self.addrdata = addrdata[:nbytes] | |
195 | nbits = srclen % 8 | |
196 | if nbits != 0: | |
197 | last = struct.pack('B', ord(self.addrdata[-1:]) & (0xff << nbits)) | |
198 | self.addrdata = self.addrdata[:-1] + last | |
199 | ||
200 | def to_text(self): | |
201 | return "ECS {}/{} scope/{}".format(self.address, self.srclen, | |
202 | self.scopelen) | |
203 | ||
204 | def to_wire(self, file): | |
205 | file.write(struct.pack('!H', self.family)) | |
206 | file.write(struct.pack('!BB', self.srclen, self.scopelen)) | |
207 | file.write(self.addrdata) | |
208 | ||
209 | @classmethod | |
210 | def from_wire(cls, otype, wire, cur, olen): | |
211 | family, src, scope = struct.unpack('!HBB', wire[cur:cur+4]) | |
212 | cur += 4 | |
213 | ||
214 | addrlen = int(math.ceil(src/8.0)) | |
215 | ||
216 | if family == 1: | |
217 | af = dns.inet.AF_INET | |
218 | pad = 4 - addrlen | |
219 | elif family == 2: | |
220 | af = dns.inet.AF_INET6 | |
221 | pad = 16 - addrlen | |
222 | else: | |
223 | raise ValueError('unsupported family') | |
224 | ||
225 | addr = dns.inet.inet_ntop(af, wire[cur:cur+addrlen] + b'\x00' * pad) | |
226 | return cls(addr, src, scope) | |
227 | ||
228 | def _cmp(self, other): | |
229 | if self.addrdata == other.addrdata: | |
230 | return 0 | |
231 | if self.addrdata > other.addrdata: | |
232 | return 1 | |
233 | return -1 | |
234 | ||
124 | 235 | _type_to_class = { |
236 | ECS: ECSOption | |
125 | 237 | } |
126 | 238 | |
127 | ||
128 | 239 | def get_option_class(otype): |
240 | """Return the class for the specified option type. | |
241 | ||
242 | The GenericOption class is used if a more specific class is not | |
243 | known. | |
244 | """ | |
245 | ||
129 | 246 | cls = _type_to_class.get(otype) |
130 | 247 | if cls is None: |
131 | 248 | cls = GenericOption |
133 | 250 | |
134 | 251 | |
135 | 252 | def option_from_wire(otype, wire, current, olen): |
136 | """Build an EDNS option object from wire format | |
137 | ||
138 | @param otype: The option type | |
139 | @type otype: int | |
140 | @param wire: The wire-format message | |
141 | @type wire: string | |
142 | @param current: The offset in wire of the beginning of the rdata. | |
143 | @type current: int | |
144 | @param olen: The length of the wire-format option data | |
145 | @type olen: int | |
146 | @rtype: dns.edns.Option instance""" | |
253 | """Build an EDNS option object from wire format. | |
254 | ||
255 | *otype*, an ``int``, is the option type. | |
256 | ||
257 | *wire*, a ``binary``, is the wire-format message. | |
258 | ||
259 | *current*, an ``int``, is the offset in *wire* of the beginning | |
260 | of the rdata. | |
261 | ||
262 | *olen*, an ``int``, is the length of the wire-format option data | |
263 | ||
264 | Returns an instance of a subclass of ``dns.edns.Option``. | |
265 | """ | |
147 | 266 | |
148 | 267 | cls = get_option_class(otype) |
149 | 268 | return cls.from_wire(otype, wire, current, olen) |
0 | # Copyright (C) 2009, 2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2009-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
23 | 25 | |
24 | 26 | |
25 | 27 | class EntropyPool(object): |
28 | ||
29 | # This is an entropy pool for Python implementations that do not | |
30 | # have a working SystemRandom. I'm not sure there are any, but | |
31 | # leaving this code doesn't hurt anything as the library code | |
32 | # is used if present. | |
26 | 33 | |
27 | 34 | def __init__(self, seed=None): |
28 | 35 | self.pool_index = 0 |
0 | from typing import Optional | |
1 | from random import SystemRandom | |
2 | ||
3 | system_random : Optional[SystemRandom] | |
4 | ||
5 | def random_16() -> int: | |
6 | pass | |
7 | ||
8 | def between(first: int, last: int) -> int: | |
9 | pass |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | """Common DNS Exceptions.""" | |
17 | """Common DNS Exceptions. | |
16 | 18 | |
19 | Dnspython modules may also define their own exceptions, which will | |
20 | always be subclasses of ``DNSException``. | |
21 | """ | |
17 | 22 | |
18 | 23 | class DNSException(Exception): |
19 | ||
20 | 24 | """Abstract base class shared by all dnspython exceptions. |
21 | 25 | |
22 | 26 | It supports two basic modes of operation: |
23 | 27 | |
24 | a) Old/compatible mode is used if __init__ was called with | |
25 | empty **kwargs. | |
26 | In compatible mode all *args are passed to standard Python Exception class | |
27 | as before and all *args are printed by standard __str__ implementation. | |
28 | Class variable msg (or doc string if msg is None) is returned from str() | |
29 | if *args is empty. | |
28 | a) Old/compatible mode is used if ``__init__`` was called with | |
29 | empty *kwargs*. In compatible mode all *args* are passed | |
30 | to the standard Python Exception class as before and all *args* are | |
31 | printed by the standard ``__str__`` implementation. Class variable | |
32 | ``msg`` (or doc string if ``msg`` is ``None``) is returned from ``str()`` | |
33 | if *args* is empty. | |
30 | 34 | |
31 | b) New/parametrized mode is used if __init__ was called with | |
32 | non-empty **kwargs. | |
33 | In the new mode *args has to be empty and all kwargs has to exactly match | |
34 | set in class variable self.supp_kwargs. All kwargs are stored inside | |
35 | self.kwargs and used in new __str__ implementation to construct | |
36 | formatted message based on self.fmt string. | |
35 | b) New/parametrized mode is used if ``__init__`` was called with | |
36 | non-empty *kwargs*. | |
37 | In the new mode *args* must be empty and all kwargs must match | |
38 | those set in class variable ``supp_kwargs``. All kwargs are stored inside | |
39 | ``self.kwargs`` and used in a new ``__str__`` implementation to construct | |
40 | a formatted message based on the ``fmt`` class variable, a ``string``. | |
37 | 41 | |
38 | In the simplest case it is enough to override supp_kwargs and fmt | |
39 | class variables to get nice parametrized messages. | |
42 | In the simplest case it is enough to override the ``supp_kwargs`` | |
43 | and ``fmt`` class variables to get nice parametrized messages. | |
40 | 44 | """ |
45 | ||
41 | 46 | msg = None # non-parametrized message |
42 | 47 | supp_kwargs = set() # accepted parameters for _fmt_kwargs (sanity check) |
43 | 48 | fmt = None # message parametrized with results from _fmt_kwargs |
101 | 106 | |
102 | 107 | |
103 | 108 | class FormError(DNSException): |
104 | ||
105 | 109 | """DNS message is malformed.""" |
106 | 110 | |
107 | 111 | |
108 | 112 | class SyntaxError(DNSException): |
109 | ||
110 | 113 | """Text input is malformed.""" |
111 | 114 | |
112 | 115 | |
113 | 116 | class UnexpectedEnd(SyntaxError): |
114 | ||
115 | 117 | """Text input ended unexpectedly.""" |
116 | 118 | |
117 | 119 | |
118 | 120 | class TooBig(DNSException): |
119 | ||
120 | 121 | """The DNS message is too big.""" |
121 | 122 | |
122 | 123 | |
123 | 124 | class Timeout(DNSException): |
124 | ||
125 | 125 | """The DNS operation timed out.""" |
126 | supp_kwargs = set(['timeout']) | |
126 | supp_kwargs = {'timeout'} | |
127 | 127 | fmt = "The DNS operation timed out after {timeout} seconds" |
0 | from typing import Set, Optional, Dict | |
1 | ||
2 | class DNSException(Exception): | |
3 | supp_kwargs : Set[str] | |
4 | kwargs : Optional[Dict] | |
5 | ||
6 | class SyntaxError(DNSException): ... | |
7 | class FormError(DNSException): ... | |
8 | class Timeout(DNSException): ... |
0 | # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2001-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
16 | 18 | |
17 | 19 | # Standard DNS flags |
18 | 20 | |
21 | #: Query Response | |
19 | 22 | QR = 0x8000 |
23 | #: Authoritative Answer | |
20 | 24 | AA = 0x0400 |
25 | #: Truncated Response | |
21 | 26 | TC = 0x0200 |
27 | #: Recursion Desired | |
22 | 28 | RD = 0x0100 |
29 | #: Recursion Available | |
23 | 30 | RA = 0x0080 |
31 | #: Authentic Data | |
24 | 32 | AD = 0x0020 |
33 | #: Checking Disabled | |
25 | 34 | CD = 0x0010 |
26 | 35 | |
27 | 36 | # EDNS flags |
28 | 37 | |
38 | #: DNSSEC answer OK | |
29 | 39 | DO = 0x8000 |
30 | 40 | |
31 | 41 | _by_text = { |
47 | 57 | # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that |
48 | 58 | # would cause the mappings not to be true inverses. |
49 | 59 | |
50 | _by_value = dict((y, x) for x, y in _by_text.items()) | |
60 | _by_value = {y: x for x, y in _by_text.items()} | |
51 | 61 | |
52 | _edns_by_value = dict((y, x) for x, y in _edns_by_text.items()) | |
62 | _edns_by_value = {y: x for x, y in _edns_by_text.items()} | |
53 | 63 | |
54 | 64 | |
55 | 65 | def _order_flags(table): |
82 | 92 | def from_text(text): |
83 | 93 | """Convert a space-separated list of flag text values into a flags |
84 | 94 | value. |
85 | @rtype: int""" | |
95 | ||
96 | Returns an ``int`` | |
97 | """ | |
86 | 98 | |
87 | 99 | return _from_text(text, _by_text) |
88 | 100 | |
90 | 102 | def to_text(flags): |
91 | 103 | """Convert a flags value into a space-separated list of flag text |
92 | 104 | values. |
93 | @rtype: string""" | |
105 | ||
106 | Returns a ``text``. | |
107 | """ | |
94 | 108 | |
95 | 109 | return _to_text(flags, _by_value, _flags_order) |
96 | 110 | |
98 | 112 | def edns_from_text(text): |
99 | 113 | """Convert a space-separated list of EDNS flag text values into a EDNS |
100 | 114 | flags value. |
101 | @rtype: int""" | |
115 | ||
116 | Returns an ``int`` | |
117 | """ | |
102 | 118 | |
103 | 119 | return _from_text(text, _edns_by_text) |
104 | 120 | |
106 | 122 | def edns_to_text(flags): |
107 | 123 | """Convert an EDNS flags value into a space-separated list of EDNS flag |
108 | 124 | text values. |
109 | @rtype: string""" | |
125 | ||
126 | Returns a ``text``. | |
127 | """ | |
110 | 128 | |
111 | 129 | return _to_text(flags, _edns_by_value, _edns_flags_order) |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2012-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
16 | 18 | |
17 | 19 | import dns |
18 | 20 | |
19 | ||
20 | 21 | def from_text(text): |
21 | """Convert the text form of a range in a GENERATE statement to an | |
22 | """Convert the text form of a range in a ``$GENERATE`` statement to an | |
22 | 23 | integer. |
23 | 24 | |
24 | @param text: the textual range | |
25 | @type text: string | |
26 | @return: The start, stop and step values. | |
27 | @rtype: tuple | |
25 | *text*, a ``str``, the textual range in ``$GENERATE`` form. | |
26 | ||
27 | Returns a tuple of three ``int`` values ``(start, stop, step)``. | |
28 | 28 | """ |
29 | ||
29 | 30 | # TODO, figure out the bounds on start, stop and step. |
30 | ||
31 | 31 | step = 1 |
32 | 32 | cur = '' |
33 | 33 | state = 0 |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
15 | 17 | """Hashing backwards compatibility wrapper""" |
16 | 18 | |
17 | 19 | import hashlib |
20 | import warnings | |
18 | 21 | |
22 | warnings.warn( | |
23 | "dns.hash module will be removed in future versions. Please use hashlib instead.", | |
24 | DeprecationWarning) | |
19 | 25 | |
20 | 26 | hashes = {} |
21 | 27 | hashes['MD5'] = hashlib.md5 |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
19 | 21 | import dns.ipv4 |
20 | 22 | import dns.ipv6 |
21 | 23 | |
24 | from ._compat import maybe_ord | |
22 | 25 | |
23 | 26 | # We assume that AF_INET is always defined. |
24 | 27 | |
37 | 40 | def inet_pton(family, text): |
38 | 41 | """Convert the textual form of a network address into its binary form. |
39 | 42 | |
40 | @param family: the address family | |
41 | @type family: int | |
42 | @param text: the textual address | |
43 | @type text: string | |
44 | @raises NotImplementedError: the address family specified is not | |
43 | *family* is an ``int``, the address family. | |
44 | ||
45 | *text* is a ``text``, the textual address. | |
46 | ||
47 | Raises ``NotImplementedError`` if the address family specified is not | |
45 | 48 | implemented. |
46 | @rtype: string | |
49 | ||
50 | Returns a ``binary``. | |
47 | 51 | """ |
48 | 52 | |
49 | 53 | if family == AF_INET: |
57 | 61 | def inet_ntop(family, address): |
58 | 62 | """Convert the binary form of a network address into its textual form. |
59 | 63 | |
60 | @param family: the address family | |
61 | @type family: int | |
62 | @param address: the binary address | |
63 | @type address: string | |
64 | @raises NotImplementedError: the address family specified is not | |
64 | *family* is an ``int``, the address family. | |
65 | ||
66 | *address* is a ``binary``, the network address in binary form. | |
67 | ||
68 | Raises ``NotImplementedError`` if the address family specified is not | |
65 | 69 | implemented. |
66 | @rtype: string | |
70 | ||
71 | Returns a ``text``. | |
67 | 72 | """ |
73 | ||
68 | 74 | if family == AF_INET: |
69 | 75 | return dns.ipv4.inet_ntoa(address) |
70 | 76 | elif family == AF_INET6: |
76 | 82 | def af_for_address(text): |
77 | 83 | """Determine the address family of a textual-form network address. |
78 | 84 | |
79 | @param text: the textual address | |
80 | @type text: string | |
81 | @raises ValueError: the address family cannot be determined from the input. | |
82 | @rtype: int | |
85 | *text*, a ``text``, the textual address. | |
86 | ||
87 | Raises ``ValueError`` if the address family cannot be determined | |
88 | from the input. | |
89 | ||
90 | Returns an ``int``. | |
83 | 91 | """ |
92 | ||
84 | 93 | try: |
85 | 94 | dns.ipv4.inet_aton(text) |
86 | 95 | return AF_INET |
95 | 104 | def is_multicast(text): |
96 | 105 | """Is the textual-form network address a multicast address? |
97 | 106 | |
98 | @param text: the textual address | |
99 | @raises ValueError: the address family cannot be determined from the input. | |
100 | @rtype: bool | |
107 | *text*, a ``text``, the textual address. | |
108 | ||
109 | Raises ``ValueError`` if the address family cannot be determined | |
110 | from the input. | |
111 | ||
112 | Returns a ``bool``. | |
101 | 113 | """ |
114 | ||
102 | 115 | try: |
103 | first = ord(dns.ipv4.inet_aton(text)[0]) | |
116 | first = maybe_ord(dns.ipv4.inet_aton(text)[0]) | |
104 | 117 | return first >= 224 and first <= 239 |
105 | 118 | except Exception: |
106 | 119 | try: |
107 | first = ord(dns.ipv6.inet_aton(text)[0]) | |
120 | first = maybe_ord(dns.ipv6.inet_aton(text)[0]) | |
108 | 121 | return first == 255 |
109 | 122 | except Exception: |
110 | 123 | raise ValueError |
0 | from typing import Union | |
1 | from socket import AddressFamily | |
2 | ||
3 | AF_INET6 : Union[int, AddressFamily] |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
20 | 22 | from ._compat import binary_type |
21 | 23 | |
22 | 24 | def inet_ntoa(address): |
23 | """Convert an IPv4 address in network form to text form. | |
25 | """Convert an IPv4 address in binary form to text form. | |
24 | 26 | |
25 | @param address: The IPv4 address | |
26 | @type address: string | |
27 | @returns: string | |
27 | *address*, a ``binary``, the IPv4 address in binary form. | |
28 | ||
29 | Returns a ``text``. | |
28 | 30 | """ |
31 | ||
29 | 32 | if len(address) != 4: |
30 | 33 | raise dns.exception.SyntaxError |
31 | 34 | if not isinstance(address, bytearray): |
32 | 35 | address = bytearray(address) |
33 | return (u'%u.%u.%u.%u' % (address[0], address[1], | |
34 | address[2], address[3])).encode() | |
36 | return ('%u.%u.%u.%u' % (address[0], address[1], | |
37 | address[2], address[3])) | |
35 | 38 | |
36 | 39 | def inet_aton(text): |
37 | """Convert an IPv4 address in text form to network form. | |
40 | """Convert an IPv4 address in text form to binary form. | |
38 | 41 | |
39 | @param text: The IPv4 address | |
40 | @type text: string | |
41 | @returns: string | |
42 | *text*, a ``text``, the IPv4 address in textual form. | |
43 | ||
44 | Returns a ``binary``. | |
42 | 45 | """ |
46 | ||
43 | 47 | if not isinstance(text, binary_type): |
44 | 48 | text = text.encode() |
45 | 49 | parts = text.split(b'.') |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
21 | 23 | import dns.ipv4 |
22 | 24 | from ._compat import xrange, binary_type, maybe_decode |
23 | 25 | |
24 | _leading_zero = re.compile(b'0+([0-9a-f]+)') | |
26 | _leading_zero = re.compile(r'0+([0-9a-f]+)') | |
25 | 27 | |
26 | 28 | def inet_ntoa(address): |
27 | """Convert a network format IPv6 address into text. | |
29 | """Convert an IPv6 address in binary form to text form. | |
28 | 30 | |
29 | @param address: the binary address | |
30 | @type address: string | |
31 | @rtype: string | |
32 | @raises ValueError: the address isn't 16 bytes long | |
31 | *address*, a ``binary``, the IPv6 address in binary form. | |
32 | ||
33 | Raises ``ValueError`` if the address isn't 16 bytes long. | |
34 | Returns a ``text``. | |
33 | 35 | """ |
34 | 36 | |
35 | 37 | if len(address) != 16: |
39 | 41 | i = 0 |
40 | 42 | l = len(hex) |
41 | 43 | while i < l: |
42 | chunk = hex[i : i + 4] | |
44 | chunk = maybe_decode(hex[i : i + 4]) | |
43 | 45 | # strip leading zeros. we do this with an re instead of |
44 | 46 | # with lstrip() because lstrip() didn't support chars until |
45 | 47 | # python 2.2.2 |
56 | 58 | start = -1 |
57 | 59 | last_was_zero = False |
58 | 60 | for i in xrange(8): |
59 | if chunks[i] != b'0': | |
61 | if chunks[i] != '0': | |
60 | 62 | if last_was_zero: |
61 | 63 | end = i |
62 | 64 | current_len = end - start |
76 | 78 | if best_len > 1: |
77 | 79 | if best_start == 0 and \ |
78 | 80 | (best_len == 6 or |
79 | best_len == 5 and chunks[5] == b'ffff'): | |
81 | best_len == 5 and chunks[5] == 'ffff'): | |
80 | 82 | # We have an embedded IPv4 address |
81 | 83 | if best_len == 6: |
82 | prefix = b'::' | |
84 | prefix = '::' | |
83 | 85 | else: |
84 | prefix = b'::ffff:' | |
86 | prefix = '::ffff:' | |
85 | 87 | hex = prefix + dns.ipv4.inet_ntoa(address[12:]) |
86 | 88 | else: |
87 | hex = b':'.join(chunks[:best_start]) + b'::' + \ | |
88 | b':'.join(chunks[best_start + best_len:]) | |
89 | hex = ':'.join(chunks[:best_start]) + '::' + \ | |
90 | ':'.join(chunks[best_start + best_len:]) | |
89 | 91 | else: |
90 | hex = b':'.join(chunks) | |
91 | return maybe_decode(hex) | |
92 | hex = ':'.join(chunks) | |
93 | return hex | |
92 | 94 | |
93 | _v4_ending = re.compile(b'(.*):(\d+\.\d+\.\d+\.\d+)$') | |
94 | _colon_colon_start = re.compile(b'::.*') | |
95 | _colon_colon_end = re.compile(b'.*::$') | |
95 | _v4_ending = re.compile(br'(.*):(\d+\.\d+\.\d+\.\d+)$') | |
96 | _colon_colon_start = re.compile(br'::.*') | |
97 | _colon_colon_end = re.compile(br'.*::$') | |
96 | 98 | |
97 | 99 | def inet_aton(text): |
98 | """Convert a text format IPv6 address into network format. | |
100 | """Convert an IPv6 address in text form to binary form. | |
99 | 101 | |
100 | @param text: the textual address | |
101 | @type text: string | |
102 | @rtype: string | |
103 | @raises dns.exception.SyntaxError: the text was not properly formatted | |
102 | *text*, a ``text``, the IPv6 address in textual form. | |
103 | ||
104 | Returns a ``binary``. | |
104 | 105 | """ |
105 | 106 | |
106 | 107 | # |
117 | 118 | m = _v4_ending.match(text) |
118 | 119 | if not m is None: |
119 | 120 | b = bytearray(dns.ipv4.inet_aton(m.group(2))) |
120 | text = (u"%s:%02x%02x:%02x%02x" % (m.group(1).decode(), b[0], b[1], | |
121 | b[2], b[3])).encode() | |
121 | text = (u"{}:{:02x}{:02x}:{:02x}{:02x}".format(m.group(1).decode(), | |
122 | b[0], b[1], b[2], | |
123 | b[3])).encode() | |
122 | 124 | # |
123 | 125 | # Try to turn '::<whatever>' into ':<whatever>'; if no match try to |
124 | 126 | # turn '<whatever>::' into '<whatever>:' |
168 | 170 | _mapped_prefix = b'\x00' * 10 + b'\xff\xff' |
169 | 171 | |
170 | 172 | def is_mapped(address): |
173 | """Is the specified address a mapped IPv4 address? | |
174 | ||
175 | *address*, a ``binary`` is an IPv6 address in binary form. | |
176 | ||
177 | Returns a ``bool``. | |
178 | """ | |
179 | ||
171 | 180 | return address.startswith(_mapped_prefix) |
0 | # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2001-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
39 | 41 | |
40 | 42 | |
41 | 43 | class ShortHeader(dns.exception.FormError): |
42 | ||
43 | 44 | """The DNS packet passed to from_wire() is too short.""" |
44 | 45 | |
45 | 46 | |
46 | 47 | class TrailingJunk(dns.exception.FormError): |
47 | ||
48 | 48 | """The DNS packet passed to from_wire() has extra junk at the end of it.""" |
49 | 49 | |
50 | 50 | |
51 | 51 | class UnknownHeaderField(dns.exception.DNSException): |
52 | ||
53 | 52 | """The header field name was not recognized when converting from text |
54 | 53 | into a message.""" |
55 | 54 | |
56 | 55 | |
57 | 56 | class BadEDNS(dns.exception.FormError): |
58 | ||
59 | """OPT record occurred somewhere other than the start of | |
57 | """An OPT record occurred somewhere other than the start of | |
60 | 58 | the additional data section.""" |
61 | 59 | |
62 | 60 | |
63 | 61 | class BadTSIG(dns.exception.FormError): |
64 | ||
65 | 62 | """A TSIG record occurred somewhere other than the end of |
66 | 63 | the additional data section.""" |
67 | 64 | |
68 | 65 | |
69 | 66 | class UnknownTSIGKey(dns.exception.DNSException): |
70 | ||
71 | 67 | """A TSIG with an unknown key was received.""" |
72 | 68 | |
73 | 69 | |
70 | #: The question section number | |
71 | QUESTION = 0 | |
72 | ||
73 | #: The answer section number | |
74 | ANSWER = 1 | |
75 | ||
76 | #: The authority section number | |
77 | AUTHORITY = 2 | |
78 | ||
79 | #: The additional section number | |
80 | ADDITIONAL = 3 | |
81 | ||
74 | 82 | class Message(object): |
75 | ||
76 | """A DNS message. | |
77 | ||
78 | @ivar id: The query id; the default is a randomly chosen id. | |
79 | @type id: int | |
80 | @ivar flags: The DNS flags of the message. @see: RFC 1035 for an | |
81 | explanation of these flags. | |
82 | @type flags: int | |
83 | @ivar question: The question section. | |
84 | @type question: list of dns.rrset.RRset objects | |
85 | @ivar answer: The answer section. | |
86 | @type answer: list of dns.rrset.RRset objects | |
87 | @ivar authority: The authority section. | |
88 | @type authority: list of dns.rrset.RRset objects | |
89 | @ivar additional: The additional data section. | |
90 | @type additional: list of dns.rrset.RRset objects | |
91 | @ivar edns: The EDNS level to use. The default is -1, no Edns. | |
92 | @type edns: int | |
93 | @ivar ednsflags: The EDNS flags | |
94 | @type ednsflags: long | |
95 | @ivar payload: The EDNS payload size. The default is 0. | |
96 | @type payload: int | |
97 | @ivar options: The EDNS options | |
98 | @type options: list of dns.edns.Option objects | |
99 | @ivar request_payload: The associated request's EDNS payload size. | |
100 | @type request_payload: int | |
101 | @ivar keyring: The TSIG keyring to use. The default is None. | |
102 | @type keyring: dict | |
103 | @ivar keyname: The TSIG keyname to use. The default is None. | |
104 | @type keyname: dns.name.Name object | |
105 | @ivar keyalgorithm: The TSIG algorithm to use; defaults to | |
106 | dns.tsig.default_algorithm. Constants for TSIG algorithms are defined | |
107 | in dns.tsig, and the currently implemented algorithms are | |
108 | HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and | |
109 | HMAC_SHA512. | |
110 | @type keyalgorithm: string | |
111 | @ivar request_mac: The TSIG MAC of the request message associated with | |
112 | this message; used when validating TSIG signatures. @see: RFC 2845 for | |
113 | more information on TSIG fields. | |
114 | @type request_mac: string | |
115 | @ivar fudge: TSIG time fudge; default is 300 seconds. | |
116 | @type fudge: int | |
117 | @ivar original_id: TSIG original id; defaults to the message's id | |
118 | @type original_id: int | |
119 | @ivar tsig_error: TSIG error code; default is 0. | |
120 | @type tsig_error: int | |
121 | @ivar other_data: TSIG other data. | |
122 | @type other_data: string | |
123 | @ivar mac: The TSIG MAC for this message. | |
124 | @type mac: string | |
125 | @ivar xfr: Is the message being used to contain the results of a DNS | |
126 | zone transfer? The default is False. | |
127 | @type xfr: bool | |
128 | @ivar origin: The origin of the zone in messages which are used for | |
129 | zone transfers or for DNS dynamic updates. The default is None. | |
130 | @type origin: dns.name.Name object | |
131 | @ivar tsig_ctx: The TSIG signature context associated with this | |
132 | message. The default is None. | |
133 | @type tsig_ctx: hmac.HMAC object | |
134 | @ivar had_tsig: Did the message decoded from wire format have a TSIG | |
135 | signature? | |
136 | @type had_tsig: bool | |
137 | @ivar multi: Is this message part of a multi-message sequence? The | |
138 | default is false. This variable is used when validating TSIG signatures | |
139 | on messages which are part of a zone transfer. | |
140 | @type multi: bool | |
141 | @ivar first: Is this message standalone, or the first of a multi | |
142 | message sequence? This variable is used when validating TSIG signatures | |
143 | on messages which are part of a zone transfer. | |
144 | @type first: bool | |
145 | @ivar index: An index of rrsets in the message. The index key is | |
146 | (section, name, rdclass, rdtype, covers, deleting). Indexing can be | |
147 | disabled by setting the index to None. | |
148 | @type index: dict | |
149 | """ | |
83 | """A DNS message.""" | |
150 | 84 | |
151 | 85 | def __init__(self, id=None): |
152 | 86 | if id is None: |
166 | 100 | self.keyring = None |
167 | 101 | self.keyname = None |
168 | 102 | self.keyalgorithm = dns.tsig.default_algorithm |
169 | self.request_mac = '' | |
170 | self.other_data = '' | |
103 | self.request_mac = b'' | |
104 | self.other_data = b'' | |
171 | 105 | self.tsig_error = 0 |
172 | 106 | self.fudge = 300 |
173 | 107 | self.original_id = self.id |
174 | self.mac = '' | |
108 | self.mac = b'' | |
175 | 109 | self.xfr = False |
176 | 110 | self.origin = None |
177 | 111 | self.tsig_ctx = None |
189 | 123 | def to_text(self, origin=None, relativize=True, **kw): |
190 | 124 | """Convert the message to text. |
191 | 125 | |
192 | The I{origin}, I{relativize}, and any other keyword | |
193 | arguments are passed to the rrset to_wire() method. | |
194 | ||
195 | @rtype: string | |
126 | The *origin*, *relativize*, and any other keyword | |
127 | arguments are passed to the RRset ``to_wire()`` method. | |
128 | ||
129 | Returns a ``text``. | |
196 | 130 | """ |
197 | 131 | |
198 | 132 | s = StringIO() |
208 | 142 | s.write(u'eflags %s\n' % |
209 | 143 | dns.flags.edns_to_text(self.ednsflags)) |
210 | 144 | s.write(u'payload %d\n' % self.payload) |
145 | for opt in self.options: | |
146 | s.write(u'option %s\n' % opt.to_text()) | |
211 | 147 | is_update = dns.opcode.is_update(self.flags) |
212 | 148 | if is_update: |
213 | 149 | s.write(u';ZONE\n') |
244 | 180 | def __eq__(self, other): |
245 | 181 | """Two messages are equal if they have the same content in the |
246 | 182 | header, question, answer, and authority sections. |
247 | @rtype: bool""" | |
183 | ||
184 | Returns a ``bool``. | |
185 | """ | |
186 | ||
248 | 187 | if not isinstance(other, Message): |
249 | 188 | return False |
250 | 189 | if self.id != other.id: |
272 | 211 | return True |
273 | 212 | |
274 | 213 | def __ne__(self, other): |
275 | """Are two messages not equal? | |
276 | @rtype: bool""" | |
277 | 214 | return not self.__eq__(other) |
278 | 215 | |
279 | 216 | def is_response(self, other): |
280 | """Is other a response to self? | |
281 | @rtype: bool""" | |
217 | """Is this message a response to *other*? | |
218 | ||
219 | Returns a ``bool``. | |
220 | """ | |
221 | ||
282 | 222 | if other.flags & dns.flags.QR == 0 or \ |
283 | 223 | self.id != other.id or \ |
284 | 224 | dns.opcode.from_flags(self.flags) != \ |
298 | 238 | return True |
299 | 239 | |
300 | 240 | def section_number(self, section): |
241 | """Return the "section number" of the specified section for use | |
242 | in indexing. The question section is 0, the answer section is 1, | |
243 | the authority section is 2, and the additional section is 3. | |
244 | ||
245 | *section* is one of the section attributes of this message. | |
246 | ||
247 | Raises ``ValueError`` if the section isn't known. | |
248 | ||
249 | Returns an ``int``. | |
250 | """ | |
251 | ||
301 | 252 | if section is self.question: |
302 | return 0 | |
253 | return QUESTION | |
303 | 254 | elif section is self.answer: |
304 | return 1 | |
255 | return ANSWER | |
305 | 256 | elif section is self.authority: |
306 | return 2 | |
257 | return AUTHORITY | |
307 | 258 | elif section is self.additional: |
308 | return 3 | |
259 | return ADDITIONAL | |
260 | else: | |
261 | raise ValueError('unknown section') | |
262 | ||
263 | def section_from_number(self, number): | |
264 | """Return the "section number" of the specified section for use | |
265 | in indexing. The question section is 0, the answer section is 1, | |
266 | the authority section is 2, and the additional section is 3. | |
267 | ||
268 | *section* is one of the section attributes of this message. | |
269 | ||
270 | Raises ``ValueError`` if the section isn't known. | |
271 | ||
272 | Returns an ``int``. | |
273 | """ | |
274 | ||
275 | if number == QUESTION: | |
276 | return self.question | |
277 | elif number == ANSWER: | |
278 | return self.answer | |
279 | elif number == AUTHORITY: | |
280 | return self.authority | |
281 | elif number == ADDITIONAL: | |
282 | return self.additional | |
309 | 283 | else: |
310 | 284 | raise ValueError('unknown section') |
311 | 285 | |
314 | 288 | force_unique=False): |
315 | 289 | """Find the RRset with the given attributes in the specified section. |
316 | 290 | |
317 | @param section: the section of the message to look in, e.g. | |
318 | self.answer. | |
319 | @type section: list of dns.rrset.RRset objects | |
320 | @param name: the name of the RRset | |
321 | @type name: dns.name.Name object | |
322 | @param rdclass: the class of the RRset | |
323 | @type rdclass: int | |
324 | @param rdtype: the type of the RRset | |
325 | @type rdtype: int | |
326 | @param covers: the covers value of the RRset | |
327 | @type covers: int | |
328 | @param deleting: the deleting value of the RRset | |
329 | @type deleting: int | |
330 | @param create: If True, create the RRset if it is not found. | |
331 | The created RRset is appended to I{section}. | |
332 | @type create: bool | |
333 | @param force_unique: If True and create is also True, create a | |
334 | new RRset regardless of whether a matching RRset exists already. | |
335 | @type force_unique: bool | |
336 | @raises KeyError: the RRset was not found and create was False | |
337 | @rtype: dns.rrset.RRset object""" | |
338 | ||
339 | key = (self.section_number(section), | |
340 | name, rdclass, rdtype, covers, deleting) | |
291 | *section*, an ``int`` section number, or one of the section | |
292 | attributes of this message. This specifies the | |
293 | the section of the message to search. For example:: | |
294 | ||
295 | my_message.find_rrset(my_message.answer, name, rdclass, rdtype) | |
296 | my_message.find_rrset(dns.message.ANSWER, name, rdclass, rdtype) | |
297 | ||
298 | *name*, a ``dns.name.Name``, the name of the RRset. | |
299 | ||
300 | *rdclass*, an ``int``, the class of the RRset. | |
301 | ||
302 | *rdtype*, an ``int``, the type of the RRset. | |
303 | ||
304 | *covers*, an ``int`` or ``None``, the covers value of the RRset. | |
305 | The default is ``None``. | |
306 | ||
307 | *deleting*, an ``int`` or ``None``, the deleting value of the RRset. | |
308 | The default is ``None``. | |
309 | ||
310 | *create*, a ``bool``. If ``True``, create the RRset if it is not found. | |
311 | The created RRset is appended to *section*. | |
312 | ||
313 | *force_unique*, a ``bool``. If ``True`` and *create* is also ``True``, | |
314 | create a new RRset regardless of whether a matching RRset exists | |
315 | already. The default is ``False``. This is useful when creating | |
316 | DDNS Update messages, as order matters for them. | |
317 | ||
318 | Raises ``KeyError`` if the RRset was not found and create was | |
319 | ``False``. | |
320 | ||
321 | Returns a ``dns.rrset.RRset object``. | |
322 | """ | |
323 | ||
324 | if isinstance(section, int): | |
325 | section_number = section | |
326 | section = self.section_from_number(section_number) | |
327 | else: | |
328 | section_number = self.section_number(section) | |
329 | key = (section_number, name, rdclass, rdtype, covers, deleting) | |
341 | 330 | if not force_unique: |
342 | 331 | if self.index is not None: |
343 | 332 | rrset = self.index.get(key) |
362 | 351 | |
363 | 352 | If the RRset is not found, None is returned. |
364 | 353 | |
365 | @param section: the section of the message to look in, e.g. | |
366 | self.answer. | |
367 | @type section: list of dns.rrset.RRset objects | |
368 | @param name: the name of the RRset | |
369 | @type name: dns.name.Name object | |
370 | @param rdclass: the class of the RRset | |
371 | @type rdclass: int | |
372 | @param rdtype: the type of the RRset | |
373 | @type rdtype: int | |
374 | @param covers: the covers value of the RRset | |
375 | @type covers: int | |
376 | @param deleting: the deleting value of the RRset | |
377 | @type deleting: int | |
378 | @param create: If True, create the RRset if it is not found. | |
379 | The created RRset is appended to I{section}. | |
380 | @type create: bool | |
381 | @param force_unique: If True and create is also True, create a | |
382 | new RRset regardless of whether a matching RRset exists already. | |
383 | @type force_unique: bool | |
384 | @rtype: dns.rrset.RRset object or None""" | |
354 | *section*, an ``int`` section number, or one of the section | |
355 | attributes of this message. This specifies the | |
356 | the section of the message to search. For example:: | |
357 | ||
358 | my_message.get_rrset(my_message.answer, name, rdclass, rdtype) | |
359 | my_message.get_rrset(dns.message.ANSWER, name, rdclass, rdtype) | |
360 | ||
361 | *name*, a ``dns.name.Name``, the name of the RRset. | |
362 | ||
363 | *rdclass*, an ``int``, the class of the RRset. | |
364 | ||
365 | *rdtype*, an ``int``, the type of the RRset. | |
366 | ||
367 | *covers*, an ``int`` or ``None``, the covers value of the RRset. | |
368 | The default is ``None``. | |
369 | ||
370 | *deleting*, an ``int`` or ``None``, the deleting value of the RRset. | |
371 | The default is ``None``. | |
372 | ||
373 | *create*, a ``bool``. If ``True``, create the RRset if it is not found. | |
374 | The created RRset is appended to *section*. | |
375 | ||
376 | *force_unique*, a ``bool``. If ``True`` and *create* is also ``True``, | |
377 | create a new RRset regardless of whether a matching RRset exists | |
378 | already. The default is ``False``. This is useful when creating | |
379 | DDNS Update messages, as order matters for them. | |
380 | ||
381 | Returns a ``dns.rrset.RRset object`` or ``None``. | |
382 | """ | |
385 | 383 | |
386 | 384 | try: |
387 | 385 | rrset = self.find_rrset(section, name, rdclass, rdtype, covers, |
394 | 392 | """Return a string containing the message in DNS compressed wire |
395 | 393 | format. |
396 | 394 | |
397 | Additional keyword arguments are passed to the rrset to_wire() | |
395 | Additional keyword arguments are passed to the RRset ``to_wire()`` | |
398 | 396 | method. |
399 | 397 | |
400 | @param origin: The origin to be appended to any relative names. | |
401 | @type origin: dns.name.Name object | |
402 | @param max_size: The maximum size of the wire format output; default | |
403 | is 0, which means 'the message's request payload, if nonzero, or | |
404 | 65536'. | |
405 | @type max_size: int | |
406 | @raises dns.exception.TooBig: max_size was exceeded | |
407 | @rtype: string | |
398 | *origin*, a ``dns.name.Name`` or ``None``, the origin to be appended | |
399 | to any relative names. | |
400 | ||
401 | *max_size*, an ``int``, the maximum size of the wire format | |
402 | output; default is 0, which means "the message's request | |
403 | payload, if nonzero, or 65535". | |
404 | ||
405 | Raises ``dns.exception.TooBig`` if *max_size* was exceeded. | |
406 | ||
407 | Returns a ``binary``. | |
408 | 408 | """ |
409 | 409 | |
410 | 410 | if max_size == 0: |
437 | 437 | return r.get_wire() |
438 | 438 | |
439 | 439 | def use_tsig(self, keyring, keyname=None, fudge=300, |
440 | original_id=None, tsig_error=0, other_data='', | |
440 | original_id=None, tsig_error=0, other_data=b'', | |
441 | 441 | algorithm=dns.tsig.default_algorithm): |
442 | 442 | """When sending, a TSIG signature using the specified keyring |
443 | 443 | and keyname should be added. |
444 | 444 | |
445 | @param keyring: The TSIG keyring to use; defaults to None. | |
446 | @type keyring: dict | |
447 | @param keyname: The name of the TSIG key to use; defaults to None. | |
448 | The key must be defined in the keyring. If a keyring is specified | |
449 | but a keyname is not, then the key used will be the first key in the | |
450 | keyring. Note that the order of keys in a dictionary is not defined, | |
451 | so applications should supply a keyname when a keyring is used, unless | |
452 | they know the keyring contains only one key. | |
453 | @type keyname: dns.name.Name or string | |
454 | @param fudge: TSIG time fudge; default is 300 seconds. | |
455 | @type fudge: int | |
456 | @param original_id: TSIG original id; defaults to the message's id | |
457 | @type original_id: int | |
458 | @param tsig_error: TSIG error code; default is 0. | |
459 | @type tsig_error: int | |
460 | @param other_data: TSIG other data. | |
461 | @type other_data: string | |
462 | @param algorithm: The TSIG algorithm to use; defaults to | |
463 | dns.tsig.default_algorithm | |
445 | See the documentation of the Message class for a complete | |
446 | description of the keyring dictionary. | |
447 | ||
448 | *keyring*, a ``dict``, the TSIG keyring to use. If a | |
449 | *keyring* is specified but a *keyname* is not, then the key | |
450 | used will be the first key in the *keyring*. Note that the | |
451 | order of keys in a dictionary is not defined, so applications | |
452 | should supply a keyname when a keyring is used, unless they | |
453 | know the keyring contains only one key. | |
454 | ||
455 | *keyname*, a ``dns.name.Name`` or ``None``, the name of the TSIG key | |
456 | to use; defaults to ``None``. The key must be defined in the keyring. | |
457 | ||
458 | *fudge*, an ``int``, the TSIG time fudge. | |
459 | ||
460 | *original_id*, an ``int``, the TSIG original id. If ``None``, | |
461 | the message's id is used. | |
462 | ||
463 | *tsig_error*, an ``int``, the TSIG error code. | |
464 | ||
465 | *other_data*, a ``binary``, the TSIG other data. | |
466 | ||
467 | *algorithm*, a ``dns.name.Name``, the TSIG algorithm to use. | |
464 | 468 | """ |
465 | 469 | |
466 | 470 | self.keyring = keyring |
482 | 486 | def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None, |
483 | 487 | options=None): |
484 | 488 | """Configure EDNS behavior. |
485 | @param edns: The EDNS level to use. Specifying None, False, or -1 | |
486 | means 'do not use EDNS', and in this case the other parameters are | |
487 | ignored. Specifying True is equivalent to specifying 0, i.e. 'use | |
488 | EDNS0'. | |
489 | @type edns: int or bool or None | |
490 | @param ednsflags: EDNS flag values. | |
491 | @type ednsflags: int | |
492 | @param payload: The EDNS sender's payload field, which is the maximum | |
493 | size of UDP datagram the sender can handle. | |
494 | @type payload: int | |
495 | @param request_payload: The EDNS payload size to use when sending | |
496 | this message. If not specified, defaults to the value of payload. | |
497 | @type request_payload: int or None | |
498 | @param options: The EDNS options | |
499 | @type options: None or list of dns.edns.Option objects | |
500 | @see: RFC 2671 | |
501 | """ | |
489 | ||
490 | *edns*, an ``int``, is the EDNS level to use. Specifying | |
491 | ``None``, ``False``, or ``-1`` means "do not use EDNS", and in this case | |
492 | the other parameters are ignored. Specifying ``True`` is | |
493 | equivalent to specifying 0, i.e. "use EDNS0". | |
494 | ||
495 | *ednsflags*, an ``int``, the EDNS flag values. | |
496 | ||
497 | *payload*, an ``int``, is the EDNS sender's payload field, which is the | |
498 | maximum size of UDP datagram the sender can handle. I.e. how big | |
499 | a response to this message can be. | |
500 | ||
501 | *request_payload*, an ``int``, is the EDNS payload size to use when | |
502 | sending this message. If not specified, defaults to the value of | |
503 | *payload*. | |
504 | ||
505 | *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS | |
506 | options. | |
507 | """ | |
508 | ||
502 | 509 | if edns is None or edns is False: |
503 | 510 | edns = -1 |
504 | 511 | if edns is True: |
524 | 531 | |
525 | 532 | def want_dnssec(self, wanted=True): |
526 | 533 | """Enable or disable 'DNSSEC desired' flag in requests. |
527 | @param wanted: Is DNSSEC desired? If True, EDNS is enabled if | |
528 | required, and then the DO bit is set. If False, the DO bit is | |
529 | cleared if EDNS is enabled. | |
530 | @type wanted: bool | |
531 | """ | |
534 | ||
535 | *wanted*, a ``bool``. If ``True``, then DNSSEC data is | |
536 | desired in the response, EDNS is enabled if required, and then | |
537 | the DO bit is set. If ``False``, the DO bit is cleared if | |
538 | EDNS is enabled. | |
539 | """ | |
540 | ||
532 | 541 | if wanted: |
533 | 542 | if self.edns < 0: |
534 | 543 | self.use_edns() |
538 | 547 | |
539 | 548 | def rcode(self): |
540 | 549 | """Return the rcode. |
541 | @rtype: int | |
550 | ||
551 | Returns an ``int``. | |
542 | 552 | """ |
543 | 553 | return dns.rcode.from_flags(self.flags, self.ednsflags) |
544 | 554 | |
545 | 555 | def set_rcode(self, rcode): |
546 | 556 | """Set the rcode. |
547 | @param rcode: the rcode | |
548 | @type rcode: int | |
557 | ||
558 | *rcode*, an ``int``, is the rcode to set. | |
549 | 559 | """ |
550 | 560 | (value, evalue) = dns.rcode.to_flags(rcode) |
551 | 561 | self.flags &= 0xFFF0 |
557 | 567 | |
558 | 568 | def opcode(self): |
559 | 569 | """Return the opcode. |
560 | @rtype: int | |
570 | ||
571 | Returns an ``int``. | |
561 | 572 | """ |
562 | 573 | return dns.opcode.from_flags(self.flags) |
563 | 574 | |
564 | 575 | def set_opcode(self, opcode): |
565 | 576 | """Set the opcode. |
566 | @param opcode: the opcode | |
567 | @type opcode: int | |
577 | ||
578 | *opcode*, an ``int``, is the opcode to set. | |
568 | 579 | """ |
569 | 580 | self.flags &= 0x87FF |
570 | 581 | self.flags |= dns.opcode.to_flags(opcode) |
574 | 585 | |
575 | 586 | """Wire format reader. |
576 | 587 | |
577 | @ivar wire: the wire-format message. | |
578 | @type wire: string | |
579 | @ivar message: The message object being built | |
580 | @type message: dns.message.Message object | |
581 | @ivar current: When building a message object from wire format, this | |
588 | wire: a binary, is the wire-format message. | |
589 | message: The message object being built | |
590 | current: When building a message object from wire format, this | |
582 | 591 | variable contains the offset from the beginning of wire of the next octet |
583 | 592 | to be read. |
584 | @type current: int | |
585 | @ivar updating: Is the message a dynamic update? | |
586 | @type updating: bool | |
587 | @ivar one_rr_per_rrset: Put each RR into its own RRset? | |
588 | @type one_rr_per_rrset: bool | |
589 | @ivar ignore_trailing: Ignore trailing junk at end of request? | |
590 | @type ignore_trailing: bool | |
591 | @ivar zone_rdclass: The class of the zone in messages which are | |
593 | updating: Is the message a dynamic update? | |
594 | one_rr_per_rrset: Put each RR into its own RRset? | |
595 | ignore_trailing: Ignore trailing junk at end of request? | |
596 | zone_rdclass: The class of the zone in messages which are | |
592 | 597 | DNS dynamic updates. |
593 | @type zone_rdclass: int | |
594 | 598 | """ |
595 | 599 | |
596 | 600 | def __init__(self, wire, message, question_only=False, |
605 | 609 | self.ignore_trailing = ignore_trailing |
606 | 610 | |
607 | 611 | def _get_question(self, qcount): |
608 | """Read the next I{qcount} records from the wire data and add them to | |
612 | """Read the next *qcount* records from the wire data and add them to | |
609 | 613 | the question section. |
610 | @param qcount: the number of questions in the message | |
611 | @type qcount: int""" | |
614 | """ | |
612 | 615 | |
613 | 616 | if self.updating and qcount > 1: |
614 | 617 | raise dns.exception.FormError |
631 | 634 | def _get_section(self, section, count): |
632 | 635 | """Read the next I{count} records from the wire data and add them to |
633 | 636 | the specified section. |
634 | @param section: the section of the message to which to add records | |
635 | @type section: list of dns.rrset.RRset objects | |
636 | @param count: the number of records to read | |
637 | @type count: int""" | |
637 | ||
638 | section: the section of the message to which to add records | |
639 | count: the number of records to read | |
640 | """ | |
638 | 641 | |
639 | 642 | if self.updating or self.one_rr_per_rrset: |
640 | 643 | force_unique = True |
752 | 755 | self.message.tsig_ctx.update(self.wire) |
753 | 756 | |
754 | 757 | |
755 | def from_wire(wire, keyring=None, request_mac='', xfr=False, origin=None, | |
758 | def from_wire(wire, keyring=None, request_mac=b'', xfr=False, origin=None, | |
756 | 759 | tsig_ctx=None, multi=False, first=True, |
757 | 760 | question_only=False, one_rr_per_rrset=False, |
758 | 761 | ignore_trailing=False): |
759 | 762 | """Convert a DNS wire format message into a message |
760 | 763 | object. |
761 | 764 | |
762 | @param keyring: The keyring to use if the message is signed. | |
763 | @type keyring: dict | |
764 | @param request_mac: If the message is a response to a TSIG-signed request, | |
765 | I{request_mac} should be set to the MAC of that request. | |
766 | @type request_mac: string | |
767 | @param xfr: Is this message part of a zone transfer? | |
768 | @type xfr: bool | |
769 | @param origin: If the message is part of a zone transfer, I{origin} | |
770 | should be the origin name of the zone. | |
771 | @type origin: dns.name.Name object | |
772 | @param tsig_ctx: The ongoing TSIG context, used when validating zone | |
773 | transfers. | |
774 | @type tsig_ctx: hmac.HMAC object | |
775 | @param multi: Is this message part of a multiple message sequence? | |
776 | @type multi: bool | |
777 | @param first: Is this message standalone, or the first of a multi | |
778 | message sequence? | |
779 | @type first: bool | |
780 | @param question_only: Read only up to the end of the question section? | |
781 | @type question_only: bool | |
782 | @param one_rr_per_rrset: Put each RR into its own RRset | |
783 | @type one_rr_per_rrset: bool | |
784 | @param ignore_trailing: Ignore trailing junk at end of request? | |
785 | @type ignore_trailing: bool | |
786 | @raises ShortHeader: The message is less than 12 octets long. | |
787 | @raises TrailingJunk: There were octets in the message past the end | |
788 | of the proper DNS message. | |
789 | @raises BadEDNS: An OPT record was in the wrong section, or occurred more | |
790 | than once. | |
791 | @raises BadTSIG: A TSIG record was not the last record of the additional | |
792 | data section. | |
793 | @rtype: dns.message.Message object""" | |
765 | *keyring*, a ``dict``, the keyring to use if the message is signed. | |
766 | ||
767 | *request_mac*, a ``binary``. If the message is a response to a | |
768 | TSIG-signed request, *request_mac* should be set to the MAC of | |
769 | that request. | |
770 | ||
771 | *xfr*, a ``bool``, should be set to ``True`` if this message is part of | |
772 | a zone transfer. | |
773 | ||
774 | *origin*, a ``dns.name.Name`` or ``None``. If the message is part | |
775 | of a zone transfer, *origin* should be the origin name of the | |
776 | zone. | |
777 | ||
778 | *tsig_ctx*, a ``hmac.HMAC`` objext, the ongoing TSIG context, used | |
779 | when validating zone transfers. | |
780 | ||
781 | *multi*, a ``bool``, should be set to ``True`` if this message | |
782 | part of a multiple message sequence. | |
783 | ||
784 | *first*, a ``bool``, should be set to ``True`` if this message is | |
785 | stand-alone, or the first message in a multi-message sequence. | |
786 | ||
787 | *question_only*, a ``bool``. If ``True``, read only up to | |
788 | the end of the question section. | |
789 | ||
790 | *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its | |
791 | own RRset. | |
792 | ||
793 | *ignore_trailing*, a ``bool``. If ``True``, ignore trailing | |
794 | junk at end of the message. | |
795 | ||
796 | Raises ``dns.message.ShortHeader`` if the message is less than 12 octets | |
797 | long. | |
798 | ||
799 | Raises ``dns.messaage.TrailingJunk`` if there were octets in the message | |
800 | past the end of the proper DNS message, and *ignore_trailing* is ``False``. | |
801 | ||
802 | Raises ``dns.message.BadEDNS`` if an OPT record was in the | |
803 | wrong section, or occurred more than once. | |
804 | ||
805 | Raises ``dns.message.BadTSIG`` if a TSIG record was not the last | |
806 | record of the additional data section. | |
807 | ||
808 | Returns a ``dns.message.Message``. | |
809 | """ | |
794 | 810 | |
795 | 811 | m = Message(id=0) |
796 | 812 | m.keyring = keyring |
812 | 828 | |
813 | 829 | """Text format reader. |
814 | 830 | |
815 | @ivar tok: the tokenizer | |
816 | @type tok: dns.tokenizer.Tokenizer object | |
817 | @ivar message: The message object being built | |
818 | @type message: dns.message.Message object | |
819 | @ivar updating: Is the message a dynamic update? | |
820 | @type updating: bool | |
821 | @ivar zone_rdclass: The class of the zone in messages which are | |
831 | tok: the tokenizer. | |
832 | message: The message object being built. | |
833 | updating: Is the message a dynamic update? | |
834 | zone_rdclass: The class of the zone in messages which are | |
822 | 835 | DNS dynamic updates. |
823 | @type zone_rdclass: int | |
824 | @ivar last_name: The most recently read name when building a message object | |
825 | from text format. | |
826 | @type last_name: dns.name.Name object | |
836 | last_name: The most recently read name when building a message object. | |
827 | 837 | """ |
828 | 838 | |
829 | 839 | def __init__(self, text, message): |
996 | 1006 | def from_text(text): |
997 | 1007 | """Convert the text format message into a message object. |
998 | 1008 | |
999 | @param text: The text format message. | |
1000 | @type text: string | |
1001 | @raises UnknownHeaderField: | |
1002 | @raises dns.exception.SyntaxError: | |
1003 | @rtype: dns.message.Message object""" | |
1009 | *text*, a ``text``, the text format message. | |
1010 | ||
1011 | Raises ``dns.message.UnknownHeaderField`` if a header is unknown. | |
1012 | ||
1013 | Raises ``dns.exception.SyntaxError`` if the text is badly formed. | |
1014 | ||
1015 | Returns a ``dns.message.Message object`` | |
1016 | """ | |
1004 | 1017 | |
1005 | 1018 | # 'text' can also be a file, but we don't publish that fact |
1006 | 1019 | # since it's an implementation detail. The official file |
1017 | 1030 | def from_file(f): |
1018 | 1031 | """Read the next text format message from the specified file. |
1019 | 1032 | |
1020 | @param f: file or string. If I{f} is a string, it is treated | |
1021 | as the name of a file to open. | |
1022 | @raises UnknownHeaderField: | |
1023 | @raises dns.exception.SyntaxError: | |
1024 | @rtype: dns.message.Message object""" | |
1033 | *f*, a ``file`` or ``text``. If *f* is text, it is treated as the | |
1034 | pathname of a file to open. | |
1035 | ||
1036 | Raises ``dns.message.UnknownHeaderField`` if a header is unknown. | |
1037 | ||
1038 | Raises ``dns.exception.SyntaxError`` if the text is badly formed. | |
1039 | ||
1040 | Returns a ``dns.message.Message object`` | |
1041 | """ | |
1025 | 1042 | |
1026 | 1043 | str_type = string_types |
1027 | 1044 | opts = 'rU' |
1051 | 1068 | The query will have a randomly chosen query id, and its DNS flags |
1052 | 1069 | will be set to dns.flags.RD. |
1053 | 1070 | |
1054 | @param qname: The query name. | |
1055 | @type qname: dns.name.Name object or string | |
1056 | @param rdtype: The desired rdata type. | |
1057 | @type rdtype: int | |
1058 | @param rdclass: The desired rdata class; the default is class IN. | |
1059 | @type rdclass: int | |
1060 | @param use_edns: The EDNS level to use; the default is None (no EDNS). | |
1071 | qname, a ``dns.name.Name`` or ``text``, the query name. | |
1072 | ||
1073 | *rdtype*, an ``int`` or ``text``, the desired rdata type. | |
1074 | ||
1075 | *rdclass*, an ``int`` or ``text``, the desired rdata class; the default | |
1076 | is class IN. | |
1077 | ||
1078 | *use_edns*, an ``int``, ``bool`` or ``None``. The EDNS level to use; the | |
1079 | default is None (no EDNS). | |
1061 | 1080 | See the description of dns.message.Message.use_edns() for the possible |
1062 | 1081 | values for use_edns and their meanings. |
1063 | @type use_edns: int or bool or None | |
1064 | @param want_dnssec: Should the query indicate that DNSSEC is desired? | |
1065 | @type want_dnssec: bool | |
1066 | @param ednsflags: EDNS flag values. | |
1067 | @type ednsflags: int | |
1068 | @param payload: The EDNS sender's payload field, which is the maximum | |
1069 | size of UDP datagram the sender can handle. | |
1070 | @type payload: int | |
1071 | @param request_payload: The EDNS payload size to use when sending | |
1072 | this message. If not specified, defaults to the value of payload. | |
1073 | @type request_payload: int or None | |
1074 | @param options: The EDNS options | |
1075 | @type options: None or list of dns.edns.Option objects | |
1076 | @see: RFC 2671 | |
1077 | @rtype: dns.message.Message object""" | |
1082 | ||
1083 | *want_dnssec*, a ``bool``. If ``True``, DNSSEC data is desired. | |
1084 | ||
1085 | *ednsflags*, an ``int``, the EDNS flag values. | |
1086 | ||
1087 | *payload*, an ``int``, is the EDNS sender's payload field, which is the | |
1088 | maximum size of UDP datagram the sender can handle. I.e. how big | |
1089 | a response to this message can be. | |
1090 | ||
1091 | *request_payload*, an ``int``, is the EDNS payload size to use when | |
1092 | sending this message. If not specified, defaults to the value of | |
1093 | *payload*. | |
1094 | ||
1095 | *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS | |
1096 | options. | |
1097 | ||
1098 | Returns a ``dns.message.Message`` | |
1099 | """ | |
1078 | 1100 | |
1079 | 1101 | if isinstance(qname, string_types): |
1080 | 1102 | qname = dns.name.from_text(qname) |
1123 | 1145 | question section, so the query's question RRsets should not be |
1124 | 1146 | changed. |
1125 | 1147 | |
1126 | @param query: the query to respond to | |
1127 | @type query: dns.message.Message object | |
1128 | @param recursion_available: should RA be set in the response? | |
1129 | @type recursion_available: bool | |
1130 | @param our_payload: payload size to advertise in EDNS responses; default | |
1131 | is 8192. | |
1132 | @type our_payload: int | |
1133 | @param fudge: TSIG time fudge; default is 300 seconds. | |
1134 | @type fudge: int | |
1135 | @rtype: dns.message.Message object""" | |
1148 | *query*, a ``dns.message.Message``, the query to respond to. | |
1149 | ||
1150 | *recursion_available*, a ``bool``, should RA be set in the response? | |
1151 | ||
1152 | *our_payload*, an ``int``, the payload size to advertise in EDNS | |
1153 | responses. | |
1154 | ||
1155 | *fudge*, an ``int``, the TSIG time fudge. | |
1156 | ||
1157 | Returns a ``dns.message.Message`` object. | |
1158 | """ | |
1136 | 1159 | |
1137 | 1160 | if query.flags & dns.flags.QR: |
1138 | 1161 | raise dns.exception.FormError('specified query message is not a query') |
1145 | 1168 | if query.edns >= 0: |
1146 | 1169 | response.use_edns(0, 0, our_payload, query.payload) |
1147 | 1170 | if query.had_tsig: |
1148 | response.use_tsig(query.keyring, query.keyname, fudge, None, 0, '', | |
1171 | response.use_tsig(query.keyring, query.keyname, fudge, None, 0, b'', | |
1149 | 1172 | query.keyalgorithm) |
1150 | 1173 | response.request_mac = query.mac |
1151 | 1174 | return response |
0 | from typing import Optional, Dict, List, Tuple, Union | |
1 | from . import name, rrset, tsig, rdatatype, entropy, edns, rdataclass | |
2 | import hmac | |
3 | ||
4 | class Message: | |
5 | def to_wire(self, origin : Optional[name.Name]=None, max_size=0, **kw) -> bytes: | |
6 | ... | |
7 | def find_rrset(self, section : List[rrset.RRset], name : name.Name, rdclass : int, rdtype : int, | |
8 | covers=rdatatype.NONE, deleting : Optional[int]=None, create=False, | |
9 | force_unique=False) -> rrset.RRset: | |
10 | ... | |
11 | def __init__(self, id : Optional[int] =None) -> None: | |
12 | self.id : int | |
13 | self.flags = 0 | |
14 | self.question : List[rrset.RRset] = [] | |
15 | self.answer : List[rrset.RRset] = [] | |
16 | self.authority : List[rrset.RRset] = [] | |
17 | self.additional : List[rrset.RRset] = [] | |
18 | self.edns = -1 | |
19 | self.ednsflags = 0 | |
20 | self.payload = 0 | |
21 | self.options : List[edns.Option] = [] | |
22 | self.request_payload = 0 | |
23 | self.keyring = None | |
24 | self.keyname = None | |
25 | self.keyalgorithm = tsig.default_algorithm | |
26 | self.request_mac = b'' | |
27 | self.other_data = b'' | |
28 | self.tsig_error = 0 | |
29 | self.fudge = 300 | |
30 | self.original_id = self.id | |
31 | self.mac = b'' | |
32 | self.xfr = False | |
33 | self.origin = None | |
34 | self.tsig_ctx = None | |
35 | self.had_tsig = False | |
36 | self.multi = False | |
37 | self.first = True | |
38 | self.index : Dict[Tuple[rrset.RRset, name.Name, int, int, Union[int,str], int], rrset.RRset] = {} | |
39 | def from_text(a : str) -> Message: | |
40 | ... | |
41 | ||
42 | def from_wire(wire, keyring : Optional[Dict[name.Name,bytes]] = None, request_mac = b'', xfr=False, origin=None, | |
43 | tsig_ctx : Optional[hmac.HMAC] = None, multi=False, first=True, | |
44 | question_only=False, one_rr_per_rrset=False, | |
45 | ignore_trailing=False) -> Message: | |
46 | ... | |
47 | def make_response(query : Message, recursion_available=False, our_payload=8192, | |
48 | fudge=300) -> Message: | |
49 | ... | |
50 | ||
51 | def make_query(qname : Union[name.Name,str], rdtype : Union[str,int], rdclass : Union[int,str] =rdataclass.IN, use_edns : Optional[bool] = None, | |
52 | want_dnssec=False, ednsflags : Optional[int] = None, payload : Optional[int] = None, | |
53 | request_payload : Optional[int] = None, options : Optional[List[edns.Option]] = None) -> Message: | |
54 | ... |
0 | # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2001-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | 17 | """DNS Names. |
16 | ||
17 | @var root: The DNS root name. | |
18 | @type root: dns.name.Name object | |
19 | @var empty: The empty DNS name. | |
20 | @type empty: dns.name.Name object | |
21 | 18 | """ |
22 | 19 | |
23 | 20 | from io import BytesIO |
37 | 34 | from ._compat import long, binary_type, text_type, unichr, maybe_decode |
38 | 35 | |
39 | 36 | try: |
40 | maxint = sys.maxint | |
37 | maxint = sys.maxint # pylint: disable=sys-max-int | |
41 | 38 | except AttributeError: |
42 | 39 | maxint = (1 << (8 * struct.calcsize("P"))) // 2 - 1 |
43 | 40 | |
41 | ||
42 | # fullcompare() result values | |
43 | ||
44 | #: The compared names have no relationship to each other. | |
44 | 45 | NAMERELN_NONE = 0 |
46 | #: the first name is a superdomain of the second. | |
45 | 47 | NAMERELN_SUPERDOMAIN = 1 |
48 | #: The first name is a subdomain of the second. | |
46 | 49 | NAMERELN_SUBDOMAIN = 2 |
50 | #: The compared names are equal. | |
47 | 51 | NAMERELN_EQUAL = 3 |
52 | #: The compared names have a common ancestor. | |
48 | 53 | NAMERELN_COMMONANCESTOR = 4 |
49 | 54 | |
50 | 55 | |
51 | 56 | class EmptyLabel(dns.exception.SyntaxError): |
52 | ||
53 | 57 | """A DNS label is empty.""" |
54 | 58 | |
55 | 59 | |
56 | 60 | class BadEscape(dns.exception.SyntaxError): |
57 | ||
58 | 61 | """An escaped code in a text format of DNS name is invalid.""" |
59 | 62 | |
60 | 63 | |
61 | 64 | class BadPointer(dns.exception.FormError): |
62 | ||
63 | 65 | """A DNS compression pointer points forward instead of backward.""" |
64 | 66 | |
65 | 67 | |
66 | 68 | class BadLabelType(dns.exception.FormError): |
67 | ||
68 | 69 | """The label type in DNS name wire format is unknown.""" |
69 | 70 | |
70 | 71 | |
71 | 72 | class NeedAbsoluteNameOrOrigin(dns.exception.DNSException): |
72 | ||
73 | 73 | """An attempt was made to convert a non-absolute name to |
74 | 74 | wire when there was also a non-absolute (or missing) origin.""" |
75 | 75 | |
76 | 76 | |
77 | 77 | class NameTooLong(dns.exception.FormError): |
78 | ||
79 | 78 | """A DNS name is > 255 octets long.""" |
80 | 79 | |
81 | 80 | |
82 | 81 | class LabelTooLong(dns.exception.SyntaxError): |
83 | ||
84 | 82 | """A DNS label is > 63 octets long.""" |
85 | 83 | |
86 | 84 | |
87 | 85 | class AbsoluteConcatenation(dns.exception.DNSException): |
88 | ||
89 | 86 | """An attempt was made to append anything other than the |
90 | 87 | empty name to an absolute DNS name.""" |
91 | 88 | |
92 | 89 | |
93 | 90 | class NoParent(dns.exception.DNSException): |
94 | ||
95 | 91 | """An attempt was made to get the parent of the root name |
96 | 92 | or the empty name.""" |
97 | 93 | |
98 | 94 | class NoIDNA2008(dns.exception.DNSException): |
99 | ||
100 | 95 | """IDNA 2008 processing was requested but the idna module is not |
101 | 96 | available.""" |
102 | 97 | |
103 | 98 | |
104 | 99 | class IDNAException(dns.exception.DNSException): |
105 | ||
106 | 100 | """IDNA processing raised an exception.""" |
107 | 101 | |
108 | supp_kwargs = set(['idna_exception']) | |
102 | supp_kwargs = {'idna_exception'} | |
109 | 103 | fmt = "IDNA processing exception: {idna_exception}" |
110 | 104 | |
105 | ||
111 | 106 | class IDNACodec(object): |
112 | ||
113 | 107 | """Abstract base class for IDNA encoder/decoders.""" |
114 | 108 | |
115 | 109 | def __init__(self): |
130 | 124 | label = maybe_decode(label) |
131 | 125 | return _escapify(label, True) |
132 | 126 | |
127 | ||
133 | 128 | class IDNA2003Codec(IDNACodec): |
134 | ||
135 | 129 | """IDNA 2003 encoder/decoder.""" |
136 | 130 | |
137 | 131 | def __init__(self, strict_decode=False): |
138 | 132 | """Initialize the IDNA 2003 encoder/decoder. |
139 | @param strict_decode: If True, then IDNA2003 checking is done when | |
140 | decoding. This can cause failures if the name was encoded with | |
141 | IDNA2008. The default is False. | |
142 | @type strict_decode: bool | |
143 | """ | |
133 | ||
134 | *strict_decode* is a ``bool``. If `True`, then IDNA2003 checking | |
135 | is done when decoding. This can cause failures if the name | |
136 | was encoded with IDNA2008. The default is `False`. | |
137 | """ | |
138 | ||
144 | 139 | super(IDNA2003Codec, self).__init__() |
145 | 140 | self.strict_decode = strict_decode |
146 | 141 | |
147 | 142 | def encode(self, label): |
143 | """Encode *label*.""" | |
144 | ||
148 | 145 | if label == '': |
149 | 146 | return b'' |
150 | 147 | try: |
153 | 150 | raise LabelTooLong |
154 | 151 | |
155 | 152 | def decode(self, label): |
153 | """Decode *label*.""" | |
156 | 154 | if not self.strict_decode: |
157 | 155 | return super(IDNA2003Codec, self).decode(label) |
158 | 156 | if label == b'': |
162 | 160 | except Exception as e: |
163 | 161 | raise IDNAException(idna_exception=e) |
164 | 162 | |
163 | ||
165 | 164 | class IDNA2008Codec(IDNACodec): |
166 | ||
167 | """IDNA 2008 encoder/decoder.""" | |
165 | """IDNA 2008 encoder/decoder. | |
166 | ||
167 | *uts_46* is a ``bool``. If True, apply Unicode IDNA | |
168 | compatibility processing as described in Unicode Technical | |
169 | Standard #46 (http://unicode.org/reports/tr46/). | |
170 | If False, do not apply the mapping. The default is False. | |
171 | ||
172 | *transitional* is a ``bool``: If True, use the | |
173 | "transitional" mode described in Unicode Technical Standard | |
174 | #46. The default is False. | |
175 | ||
176 | *allow_pure_ascii* is a ``bool``. If True, then a label which | |
177 | consists of only ASCII characters is allowed. This is less | |
178 | strict than regular IDNA 2008, but is also necessary for mixed | |
179 | names, e.g. a name with starting with "_sip._tcp." and ending | |
180 | in an IDN suffix which would otherwise be disallowed. The | |
181 | default is False. | |
182 | ||
183 | *strict_decode* is a ``bool``: If True, then IDNA2008 checking | |
184 | is done when decoding. This can cause failures if the name | |
185 | was encoded with IDNA2003. The default is False. | |
186 | """ | |
168 | 187 | |
169 | 188 | def __init__(self, uts_46=False, transitional=False, |
170 | 189 | allow_pure_ascii=False, strict_decode=False): |
171 | """Initialize the IDNA 2008 encoder/decoder. | |
172 | @param uts_46: If True, apply Unicode IDNA compatibility processing | |
173 | as described in Unicode Technical Standard #46 | |
174 | (U{http://unicode.org/reports/tr46/}). This parameter is only | |
175 | meaningful if IDNA 2008 is in use. If False, do not apply | |
176 | the mapping. The default is False | |
177 | @type uts_46: bool | |
178 | @param transitional: If True, use the "transitional" mode described | |
179 | in Unicode Technical Standard #46. This parameter is only | |
180 | meaningful if IDNA 2008 is in use. The default is False. | |
181 | @type transitional: bool | |
182 | @param allow_pure_ascii: If True, then a label which | |
183 | consists of only ASCII characters is allowed. This is less strict | |
184 | than regular IDNA 2008, but is also necessary for mixed names, | |
185 | e.g. a name with starting with "_sip._tcp." and ending in an IDN | |
186 | suffixm which would otherwise be disallowed. The default is False | |
187 | @type allow_pure_ascii: bool | |
188 | @param strict_decode: If True, then IDNA2008 checking is done when | |
189 | decoding. This can cause failures if the name was encoded with | |
190 | IDNA2003. The default is False. | |
191 | @type strict_decode: bool | |
192 | """ | |
190 | """Initialize the IDNA 2008 encoder/decoder.""" | |
193 | 191 | super(IDNA2008Codec, self).__init__() |
194 | 192 | self.uts_46 = uts_46 |
195 | 193 | self.transitional = transitional |
276 | 274 | def _validate_labels(labels): |
277 | 275 | """Check for empty labels in the middle of a label sequence, |
278 | 276 | labels that are too long, and for too many labels. |
279 | @raises NameTooLong: the name as a whole is too long | |
280 | @raises EmptyLabel: a label is empty (i.e. the root label) and appears | |
281 | in a position other than the end of the label sequence""" | |
277 | ||
278 | Raises ``dns.name.NameTooLong`` if the name as a whole is too long. | |
279 | ||
280 | Raises ``dns.name.EmptyLabel`` if a label is empty (i.e. the root | |
281 | label) and appears in a position other than the end of the label | |
282 | sequence | |
283 | ||
284 | """ | |
282 | 285 | |
283 | 286 | l = len(labels) |
284 | 287 | total = 0 |
298 | 301 | raise EmptyLabel |
299 | 302 | |
300 | 303 | |
301 | def _ensure_bytes(label): | |
304 | def _maybe_convert_to_binary(label): | |
305 | """If label is ``text``, convert it to ``binary``. If it is already | |
306 | ``binary`` just return it. | |
307 | ||
308 | """ | |
309 | ||
302 | 310 | if isinstance(label, binary_type): |
303 | 311 | return label |
304 | 312 | if isinstance(label, text_type): |
310 | 318 | |
311 | 319 | """A DNS name. |
312 | 320 | |
313 | The dns.name.Name class represents a DNS name as a tuple of labels. | |
314 | Instances of the class are immutable. | |
315 | ||
316 | @ivar labels: The tuple of labels in the name. Each label is a string of | |
317 | up to 63 octets.""" | |
321 | The dns.name.Name class represents a DNS name as a tuple of | |
322 | labels. Each label is a `binary` in DNS wire format. Instances | |
323 | of the class are immutable. | |
324 | """ | |
318 | 325 | |
319 | 326 | __slots__ = ['labels'] |
320 | 327 | |
321 | 328 | def __init__(self, labels): |
322 | """Initialize a domain name from a list of labels. | |
323 | @param labels: the labels | |
324 | @type labels: any iterable whose values are strings | |
325 | """ | |
326 | labels = [_ensure_bytes(x) for x in labels] | |
329 | """*labels* is any iterable whose values are ``text`` or ``binary``. | |
330 | """ | |
331 | ||
332 | labels = [_maybe_convert_to_binary(x) for x in labels] | |
327 | 333 | super(Name, self).__setattr__('labels', tuple(labels)) |
328 | 334 | _validate_labels(self.labels) |
329 | 335 | |
330 | 336 | def __setattr__(self, name, value): |
337 | # Names are immutable | |
331 | 338 | raise TypeError("object doesn't support attribute assignment") |
332 | 339 | |
333 | 340 | def __copy__(self): |
337 | 344 | return Name(copy.deepcopy(self.labels, memo)) |
338 | 345 | |
339 | 346 | def __getstate__(self): |
347 | # Names can be pickled | |
340 | 348 | return {'labels': self.labels} |
341 | 349 | |
342 | 350 | def __setstate__(self, state): |
345 | 353 | |
346 | 354 | def is_absolute(self): |
347 | 355 | """Is the most significant label of this name the root label? |
348 | @rtype: bool | |
356 | ||
357 | Returns a ``bool``. | |
349 | 358 | """ |
350 | 359 | |
351 | 360 | return len(self.labels) > 0 and self.labels[-1] == b'' |
352 | 361 | |
353 | 362 | def is_wild(self): |
354 | 363 | """Is this name wild? (I.e. Is the least significant label '*'?) |
355 | @rtype: bool | |
364 | ||
365 | Returns a ``bool``. | |
356 | 366 | """ |
357 | 367 | |
358 | 368 | return len(self.labels) > 0 and self.labels[0] == b'*' |
359 | 369 | |
360 | 370 | def __hash__(self): |
361 | 371 | """Return a case-insensitive hash of the name. |
362 | @rtype: int | |
372 | ||
373 | Returns an ``int``. | |
363 | 374 | """ |
364 | 375 | |
365 | 376 | h = long(0) |
369 | 380 | return int(h % maxint) |
370 | 381 | |
371 | 382 | def fullcompare(self, other): |
372 | """Compare two names, returning a 3-tuple (relation, order, nlabels). | |
373 | ||
374 | I{relation} describes the relation ship between the names, | |
375 | and is one of: dns.name.NAMERELN_NONE, | |
376 | dns.name.NAMERELN_SUPERDOMAIN, dns.name.NAMERELN_SUBDOMAIN, | |
377 | dns.name.NAMERELN_EQUAL, or dns.name.NAMERELN_COMMONANCESTOR | |
378 | ||
379 | I{order} is < 0 if self < other, > 0 if self > other, and == | |
380 | 0 if self == other. A relative name is always less than an | |
383 | """Compare two names, returning a 3-tuple | |
384 | ``(relation, order, nlabels)``. | |
385 | ||
386 | *relation* describes the relation ship between the names, | |
387 | and is one of: ``dns.name.NAMERELN_NONE``, | |
388 | ``dns.name.NAMERELN_SUPERDOMAIN``, ``dns.name.NAMERELN_SUBDOMAIN``, | |
389 | ``dns.name.NAMERELN_EQUAL``, or ``dns.name.NAMERELN_COMMONANCESTOR``. | |
390 | ||
391 | *order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and == | |
392 | 0 if *self* == *other*. A relative name is always less than an | |
381 | 393 | absolute name. If both names have the same relativity, then |
382 | 394 | the DNSSEC order relation is used to order them. |
383 | 395 | |
384 | I{nlabels} is the number of significant labels that the two names | |
396 | *nlabels* is the number of significant labels that the two names | |
385 | 397 | have in common. |
398 | ||
399 | Here are some examples. Names ending in "." are absolute names, | |
400 | those not ending in "." are relative names. | |
401 | ||
402 | ============= ============= =========== ===== ======= | |
403 | self other relation order nlabels | |
404 | ============= ============= =========== ===== ======= | |
405 | www.example. www.example. equal 0 3 | |
406 | www.example. example. subdomain > 0 2 | |
407 | example. www.example. superdomain < 0 2 | |
408 | example1.com. example2.com. common anc. < 0 2 | |
409 | example1 example2. none < 0 0 | |
410 | example1. example2 none > 0 0 | |
411 | ============= ============= =========== ===== ======= | |
386 | 412 | """ |
387 | 413 | |
388 | 414 | sabs = self.is_absolute() |
432 | 458 | def is_subdomain(self, other): |
433 | 459 | """Is self a subdomain of other? |
434 | 460 | |
435 | The notion of subdomain includes equality. | |
436 | @rtype: bool | |
461 | Note that the notion of subdomain includes equality, e.g. | |
462 | "dnpython.org" is a subdomain of itself. | |
463 | ||
464 | Returns a ``bool``. | |
437 | 465 | """ |
438 | 466 | |
439 | 467 | (nr, o, nl) = self.fullcompare(other) |
444 | 472 | def is_superdomain(self, other): |
445 | 473 | """Is self a superdomain of other? |
446 | 474 | |
447 | The notion of subdomain includes equality. | |
448 | @rtype: bool | |
475 | Note that the notion of superdomain includes equality, e.g. | |
476 | "dnpython.org" is a superdomain of itself. | |
477 | ||
478 | Returns a ``bool``. | |
449 | 479 | """ |
450 | 480 | |
451 | 481 | (nr, o, nl) = self.fullcompare(other) |
456 | 486 | def canonicalize(self): |
457 | 487 | """Return a name which is equal to the current name, but is in |
458 | 488 | DNSSEC canonical form. |
459 | @rtype: dns.name.Name object | |
460 | 489 | """ |
461 | 490 | |
462 | 491 | return Name([x.lower() for x in self.labels]) |
504 | 533 | return self.to_text(False) |
505 | 534 | |
506 | 535 | def to_text(self, omit_final_dot=False): |
507 | """Convert name to text format. | |
508 | @param omit_final_dot: If True, don't emit the final dot (denoting the | |
509 | root label) for absolute names. The default is False. | |
510 | @rtype: string | |
536 | """Convert name to DNS text format. | |
537 | ||
538 | *omit_final_dot* is a ``bool``. If True, don't emit the final | |
539 | dot (denoting the root label) for absolute names. The default | |
540 | is False. | |
541 | ||
542 | Returns a ``text``. | |
511 | 543 | """ |
512 | 544 | |
513 | 545 | if len(self.labels) == 0: |
526 | 558 | |
527 | 559 | IDN ACE labels are converted to Unicode. |
528 | 560 | |
529 | @param omit_final_dot: If True, don't emit the final dot (denoting the | |
530 | root label) for absolute names. The default is False. | |
531 | @type omit_final_dot: bool | |
532 | @param idna_codec: IDNA encoder/decoder. If None, the | |
533 | IDNA_2003_Practical encoder/decoder is used. The IDNA_2003_Practical | |
534 | decoder does not impose any policy, it just decodes punycode, so if | |
535 | you don't want checking for compliance, you can use this decoder for | |
536 | IDNA2008 as well. | |
537 | @type idna_codec: dns.name.IDNA | |
538 | @rtype: string | |
561 | *omit_final_dot* is a ``bool``. If True, don't emit the final | |
562 | dot (denoting the root label) for absolute names. The default | |
563 | is False. | |
564 | *idna_codec* specifies the IDNA encoder/decoder. If None, the | |
565 | dns.name.IDNA_2003_Practical encoder/decoder is used. | |
566 | The IDNA_2003_Practical decoder does | |
567 | not impose any policy, it just decodes punycode, so if you | |
568 | don't want checking for compliance, you can use this decoder | |
569 | for IDNA2008 as well. | |
570 | ||
571 | Returns a ``text``. | |
539 | 572 | """ |
540 | 573 | |
541 | 574 | if len(self.labels) == 0: |
553 | 586 | def to_digestable(self, origin=None): |
554 | 587 | """Convert name to a format suitable for digesting in hashes. |
555 | 588 | |
556 | The name is canonicalized and converted to uncompressed wire format. | |
557 | ||
558 | @param origin: If the name is relative and origin is not None, then | |
559 | origin will be appended to it. | |
560 | @type origin: dns.name.Name object | |
561 | @raises NeedAbsoluteNameOrOrigin: All names in wire format are | |
562 | absolute. If self is a relative name, then an origin must be supplied; | |
563 | if it is missing, then this exception is raised | |
564 | @rtype: string | |
589 | The name is canonicalized and converted to uncompressed wire | |
590 | format. All names in wire format are absolute. If the name | |
591 | is a relative name, then an origin must be supplied. | |
592 | ||
593 | *origin* is a ``dns.name.Name`` or ``None``. If the name is | |
594 | relative and origin is not ``None``, then origin will be appended | |
595 | to the name. | |
596 | ||
597 | Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is | |
598 | relative and no origin was provided. | |
599 | ||
600 | Returns a ``binary``. | |
565 | 601 | """ |
566 | 602 | |
567 | 603 | if not self.is_absolute(): |
578 | 614 | def to_wire(self, file=None, compress=None, origin=None): |
579 | 615 | """Convert name to wire format, possibly compressing it. |
580 | 616 | |
581 | @param file: the file where the name is emitted (typically | |
582 | a BytesIO file). If None, a string containing the wire name | |
583 | will be returned. | |
584 | @type file: file or None | |
585 | @param compress: The compression table. If None (the default) names | |
586 | will not be compressed. | |
587 | @type compress: dict | |
588 | @param origin: If the name is relative and origin is not None, then | |
589 | origin will be appended to it. | |
590 | @type origin: dns.name.Name object | |
591 | @raises NeedAbsoluteNameOrOrigin: All names in wire format are | |
592 | absolute. If self is a relative name, then an origin must be supplied; | |
593 | if it is missing, then this exception is raised | |
617 | *file* is the file where the name is emitted (typically a | |
618 | BytesIO file). If ``None`` (the default), a ``binary`` | |
619 | containing the wire name will be returned. | |
620 | ||
621 | *compress*, a ``dict``, is the compression table to use. If | |
622 | ``None`` (the default), names will not be compressed. | |
623 | ||
624 | *origin* is a ``dns.name.Name`` or ``None``. If the name is | |
625 | relative and origin is not ``None``, then *origin* will be appended | |
626 | to it. | |
627 | ||
628 | Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is | |
629 | relative and no origin was provided. | |
630 | ||
631 | Returns a ``binary`` or ``None``. | |
594 | 632 | """ |
595 | 633 | |
596 | 634 | if file is None: |
633 | 671 | |
634 | 672 | def __len__(self): |
635 | 673 | """The length of the name (in labels). |
636 | @rtype: int | |
674 | ||
675 | Returns an ``int``. | |
637 | 676 | """ |
638 | 677 | |
639 | 678 | return len(self.labels) |
648 | 687 | return self.relativize(other) |
649 | 688 | |
650 | 689 | def split(self, depth): |
651 | """Split a name into a prefix and suffix at depth. | |
652 | ||
653 | @param depth: the number of labels in the suffix | |
654 | @type depth: int | |
655 | @raises ValueError: the depth was not >= 0 and <= the length of the | |
690 | """Split a name into a prefix and suffix names at the specified depth. | |
691 | ||
692 | *depth* is an ``int`` specifying the number of labels in the suffix | |
693 | ||
694 | Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the | |
656 | 695 | name. |
657 | @returns: the tuple (prefix, suffix) | |
658 | @rtype: tuple | |
696 | ||
697 | Returns the tuple ``(prefix, suffix)``. | |
659 | 698 | """ |
660 | 699 | |
661 | 700 | l = len(self.labels) |
670 | 709 | |
671 | 710 | def concatenate(self, other): |
672 | 711 | """Return a new name which is the concatenation of self and other. |
673 | @rtype: dns.name.Name object | |
674 | @raises AbsoluteConcatenation: self is absolute and other is | |
675 | not the empty name | |
712 | ||
713 | Raises ``dns.name.AbsoluteConcatenation`` if the name is | |
714 | absolute and *other* is not the empty name. | |
715 | ||
716 | Returns a ``dns.name.Name``. | |
676 | 717 | """ |
677 | 718 | |
678 | 719 | if self.is_absolute() and len(other) > 0: |
682 | 723 | return Name(labels) |
683 | 724 | |
684 | 725 | def relativize(self, origin): |
685 | """If self is a subdomain of origin, return a new name which is self | |
686 | relative to origin. Otherwise return self. | |
687 | @rtype: dns.name.Name object | |
726 | """If the name is a subdomain of *origin*, return a new name which is | |
727 | the name relative to origin. Otherwise return the name. | |
728 | ||
729 | For example, relativizing ``www.dnspython.org.`` to origin | |
730 | ``dnspython.org.`` returns the name ``www``. Relativizing ``example.`` | |
731 | to origin ``dnspython.org.`` returns ``example.``. | |
732 | ||
733 | Returns a ``dns.name.Name``. | |
688 | 734 | """ |
689 | 735 | |
690 | 736 | if origin is not None and self.is_subdomain(origin): |
693 | 739 | return self |
694 | 740 | |
695 | 741 | def derelativize(self, origin): |
696 | """If self is a relative name, return a new name which is the | |
697 | concatenation of self and origin. Otherwise return self. | |
698 | @rtype: dns.name.Name object | |
742 | """If the name is a relative name, return a new name which is the | |
743 | concatenation of the name and origin. Otherwise return the name. | |
744 | ||
745 | For example, derelativizing ``www`` to origin ``dnspython.org.`` | |
746 | returns the name ``www.dnspython.org.``. Derelativizing ``example.`` | |
747 | to origin ``dnspython.org.`` returns ``example.``. | |
748 | ||
749 | Returns a ``dns.name.Name``. | |
699 | 750 | """ |
700 | 751 | |
701 | 752 | if not self.is_absolute(): |
704 | 755 | return self |
705 | 756 | |
706 | 757 | def choose_relativity(self, origin=None, relativize=True): |
707 | """Return a name with the relativity desired by the caller. If | |
708 | origin is None, then self is returned. Otherwise, if | |
709 | relativize is true the name is relativized, and if relativize is | |
710 | false the name is derelativized. | |
711 | @rtype: dns.name.Name object | |
758 | """Return a name with the relativity desired by the caller. | |
759 | ||
760 | If *origin* is ``None``, then the name is returned. | |
761 | Otherwise, if *relativize* is ``True`` the name is | |
762 | relativized, and if *relativize* is ``False`` the name is | |
763 | derelativized. | |
764 | ||
765 | Returns a ``dns.name.Name``. | |
712 | 766 | """ |
713 | 767 | |
714 | 768 | if origin: |
721 | 775 | |
722 | 776 | def parent(self): |
723 | 777 | """Return the parent of the name. |
724 | @rtype: dns.name.Name object | |
725 | @raises NoParent: the name is either the root name or the empty name, | |
726 | and thus has no parent. | |
727 | """ | |
778 | ||
779 | For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``. | |
780 | ||
781 | Raises ``dns.name.NoParent`` if the name is either the root name or the | |
782 | empty name, and thus has no parent. | |
783 | ||
784 | Returns a ``dns.name.Name``. | |
785 | """ | |
786 | ||
728 | 787 | if self == root or self == empty: |
729 | 788 | raise NoParent |
730 | 789 | return Name(self.labels[1:]) |
731 | 790 | |
791 | #: The root name, '.' | |
732 | 792 | root = Name([b'']) |
793 | ||
794 | #: The empty name. | |
733 | 795 | empty = Name([]) |
734 | ||
735 | 796 | |
736 | 797 | def from_unicode(text, origin=root, idna_codec=None): |
737 | 798 | """Convert unicode text into a Name object. |
738 | 799 | |
739 | Labels are encoded in IDN ACE form. | |
740 | ||
741 | @param text: The text to convert into a name. | |
742 | @type text: Unicode string | |
743 | @param origin: The origin to append to non-absolute names. | |
744 | @type origin: dns.name.Name | |
745 | @param idna_codec: IDNA encoder/decoder. If None, the default IDNA 2003 | |
746 | encoder/decoder is used. | |
747 | @type idna_codec: dns.name.IDNA | |
748 | @rtype: dns.name.Name object | |
800 | Labels are encoded in IDN ACE form according to rules specified by | |
801 | the IDNA codec. | |
802 | ||
803 | *text*, a ``text``, is the text to convert into a name. | |
804 | ||
805 | *origin*, a ``dns.name.Name``, specifies the origin to | |
806 | append to non-absolute names. The default is the root name. | |
807 | ||
808 | *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA | |
809 | encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder | |
810 | is used. | |
811 | ||
812 | Returns a ``dns.name.Name``. | |
749 | 813 | """ |
750 | 814 | |
751 | 815 | if not isinstance(text, text_type): |
808 | 872 | def from_text(text, origin=root, idna_codec=None): |
809 | 873 | """Convert text into a Name object. |
810 | 874 | |
811 | @param text: The text to convert into a name. | |
812 | @type text: string | |
813 | @param origin: The origin to append to non-absolute names. | |
814 | @type origin: dns.name.Name | |
815 | @param idna_codec: IDNA encoder/decoder. If None, the default IDNA 2003 | |
816 | encoder/decoder is used. | |
817 | @type idna_codec: dns.name.IDNA | |
818 | @rtype: dns.name.Name object | |
875 | *text*, a ``text``, is the text to convert into a name. | |
876 | ||
877 | *origin*, a ``dns.name.Name``, specifies the origin to | |
878 | append to non-absolute names. The default is the root name. | |
879 | ||
880 | *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA | |
881 | encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder | |
882 | is used. | |
883 | ||
884 | Returns a ``dns.name.Name``. | |
819 | 885 | """ |
820 | 886 | |
821 | 887 | if isinstance(text, text_type): |
877 | 943 | |
878 | 944 | def from_wire(message, current): |
879 | 945 | """Convert possibly compressed wire format into a Name. |
880 | @param message: the entire DNS message | |
881 | @type message: string | |
882 | @param current: the offset of the beginning of the name from the start | |
883 | of the message | |
884 | @type current: int | |
885 | @raises dns.name.BadPointer: a compression pointer did not point backwards | |
886 | in the message | |
887 | @raises dns.name.BadLabelType: an invalid label type was encountered. | |
888 | @returns: a tuple consisting of the name that was read and the number | |
889 | of bytes of the wire format message which were consumed reading it | |
890 | @rtype: (dns.name.Name object, int) tuple | |
946 | ||
947 | *message* is a ``binary`` containing an entire DNS message in DNS | |
948 | wire form. | |
949 | ||
950 | *current*, an ``int``, is the offset of the beginning of the name | |
951 | from the start of the message | |
952 | ||
953 | Raises ``dns.name.BadPointer`` if a compression pointer did not | |
954 | point backwards in the message. | |
955 | ||
956 | Raises ``dns.name.BadLabelType`` if an invalid label type was encountered. | |
957 | ||
958 | Returns a ``(dns.name.Name, int)`` tuple consisting of the name | |
959 | that was read and the number of bytes of the wire format message | |
960 | which were consumed reading it. | |
891 | 961 | """ |
892 | 962 | |
893 | 963 | if not isinstance(message, binary_type): |
0 | from typing import Optional, Union, Tuple, Iterable, List | |
1 | ||
2 | class Name: | |
3 | def is_subdomain(self, o : Name) -> bool: ... | |
4 | def is_superdomain(self, o : Name) -> bool: ... | |
5 | def __init__(self, labels : Iterable[Union[bytes,str]]) -> None: | |
6 | self.labels : List[bytes] | |
7 | def is_absolute(self) -> bool: ... | |
8 | def is_wild(self) -> bool: ... | |
9 | def fullcompare(self, other) -> Tuple[int,int,int]: ... | |
10 | def canonicalize(self) -> Name: ... | |
11 | def __lt__(self, other : Name): ... | |
12 | def __le__(self, other : Name): ... | |
13 | def __ge__(self, other : Name): ... | |
14 | def __gt__(self, other : Name): ... | |
15 | def to_text(self, omit_final_dot=False) -> str: ... | |
16 | def to_unicode(self, omit_final_dot=False, idna_codec=None) -> str: ... | |
17 | def to_digestable(self, origin=None) -> bytes: ... | |
18 | def to_wire(self, file=None, compress=None, origin=None) -> Optional[bytes]: ... | |
19 | def __add__(self, other : Name): ... | |
20 | def __sub__(self, other : Name): ... | |
21 | def split(self, depth) -> List[Tuple[str,str]]: ... | |
22 | def concatenate(self, other : Name) -> Name: ... | |
23 | def relativize(self, origin): ... | |
24 | def derelativize(self, origin): ... | |
25 | def choose_relativity(self, origin : Optional[Name] = None, relativize=True): ... | |
26 | def parent(self) -> Name: ... | |
27 | ||
28 | class IDNACodec: | |
29 | pass | |
30 | ||
31 | def from_text(text, origin : Optional[Name] = Name('.'), idna_codec : Optional[IDNACodec] = None) -> Name: | |
32 | ... | |
33 | ||
34 | empty : Name |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # Copyright (C) 2016 Coresec Systems AB |
2 | 4 | # |
3 | 5 | # Permission to use, copy, modify, and distribute this software and its |
30 | 32 | |
31 | 33 | |
32 | 34 | class NameDict(collections.MutableMapping): |
35 | """A dictionary whose keys are dns.name.Name objects. | |
33 | 36 | |
34 | """A dictionary whose keys are dns.name.Name objects. | |
35 | @ivar max_depth: the maximum depth of the keys that have ever been | |
36 | added to the dictionary. | |
37 | @type max_depth: int | |
38 | @ivar max_depth_items: the number of items of maximum depth | |
39 | @type max_depth_items: int | |
37 | In addition to being like a regular Python dictionary, this | |
38 | dictionary can also get the deepest match for a given key. | |
40 | 39 | """ |
41 | 40 | |
42 | 41 | __slots__ = ["max_depth", "max_depth_items", "__store"] |
43 | 42 | |
44 | 43 | def __init__(self, *args, **kwargs): |
44 | super(NameDict, self).__init__() | |
45 | 45 | self.__store = dict() |
46 | #: the maximum depth of the keys that have ever been added | |
46 | 47 | self.max_depth = 0 |
48 | #: the number of items of maximum depth | |
47 | 49 | self.max_depth_items = 0 |
48 | 50 | self.update(dict(*args, **kwargs)) |
49 | 51 | |
82 | 84 | return key in self.__store |
83 | 85 | |
84 | 86 | def get_deepest_match(self, name): |
85 | """Find the deepest match to I{name} in the dictionary. | |
87 | """Find the deepest match to *fname* in the dictionary. | |
86 | 88 | |
87 | 89 | The deepest match is the longest name in the dictionary which is |
88 | a superdomain of I{name}. | |
90 | a superdomain of *name*. Note that *superdomain* includes matching | |
91 | *name* itself. | |
89 | 92 | |
90 | @param name: the name | |
91 | @type name: dns.name.Name object | |
92 | @rtype: (key, value) tuple | |
93 | *name*, a ``dns.name.Name``, the name to find. | |
94 | ||
95 | Returns a ``(key, value)`` where *key* is the deepest | |
96 | ``dns.name.Name``, and *value* is the value associated with *key*. | |
93 | 97 | """ |
94 | 98 | |
95 | 99 | depth = len(name) |
0 | # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2001-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
23 | 25 | |
24 | 26 | class Node(object): |
25 | 27 | |
26 | """A DNS node. | |
27 | ||
28 | A node is a set of rdatasets | |
29 | ||
30 | @ivar rdatasets: the node's rdatasets | |
31 | @type rdatasets: list of dns.rdataset.Rdataset objects""" | |
28 | """A Node is a set of rdatasets.""" | |
32 | 29 | |
33 | 30 | __slots__ = ['rdatasets'] |
34 | 31 | |
35 | 32 | def __init__(self): |
36 | """Initialize a DNS node. | |
37 | """ | |
38 | ||
33 | #: the set of rdatsets, represented as a list. | |
39 | 34 | self.rdatasets = [] |
40 | 35 | |
41 | 36 | def to_text(self, name, **kw): |
43 | 38 | |
44 | 39 | Each rdataset at the node is printed. Any keyword arguments |
45 | 40 | to this method are passed on to the rdataset's to_text() method. |
46 | @param name: the owner name of the rdatasets | |
47 | @type name: dns.name.Name object | |
48 | @rtype: string | |
41 | ||
42 | *name*, a ``dns.name.Name`` or ``text``, the owner name of the rdatasets. | |
43 | ||
44 | Returns a ``text``. | |
49 | 45 | """ |
50 | 46 | |
51 | 47 | s = StringIO() |
59 | 55 | return '<DNS node ' + str(id(self)) + '>' |
60 | 56 | |
61 | 57 | def __eq__(self, other): |
62 | """Two nodes are equal if they have the same rdatasets. | |
63 | ||
64 | @rtype: bool | |
65 | """ | |
66 | 58 | # |
67 | 59 | # This is inefficient. Good thing we don't need to do it much. |
68 | 60 | # |
88 | 80 | """Find an rdataset matching the specified properties in the |
89 | 81 | current node. |
90 | 82 | |
91 | @param rdclass: The class of the rdataset | |
92 | @type rdclass: int | |
93 | @param rdtype: The type of the rdataset | |
94 | @type rdtype: int | |
95 | @param covers: The covered type. Usually this value is | |
83 | *rdclass*, an ``int``, the class of the rdataset. | |
84 | ||
85 | *rdtype*, an ``int``, the type of the rdataset. | |
86 | ||
87 | *covers*, an ``int``, the covered type. Usually this value is | |
96 | 88 | dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or |
97 | 89 | dns.rdatatype.RRSIG, then the covers value will be the rdata |
98 | 90 | type the SIG/RRSIG covers. The library treats the SIG and RRSIG |
100 | 92 | types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much |
101 | 93 | easier to work with than if RRSIGs covering different rdata |
102 | 94 | types were aggregated into a single RRSIG rdataset. |
103 | @type covers: int | |
104 | @param create: If True, create the rdataset if it is not found. | |
105 | @type create: bool | |
106 | @raises KeyError: An rdataset of the desired type and class does | |
107 | not exist and I{create} is not True. | |
108 | @rtype: dns.rdataset.Rdataset object | |
95 | ||
96 | *create*, a ``bool``. If True, create the rdataset if it is not found. | |
97 | ||
98 | Raises ``KeyError`` if an rdataset of the desired type and class does | |
99 | not exist and *create* is not ``True``. | |
100 | ||
101 | Returns a ``dns.rdataset.Rdataset``. | |
109 | 102 | """ |
110 | 103 | |
111 | 104 | for rds in self.rdatasets: |
123 | 116 | current node. |
124 | 117 | |
125 | 118 | None is returned if an rdataset of the specified type and |
126 | class does not exist and I{create} is not True. | |
119 | class does not exist and *create* is not ``True``. | |
127 | 120 | |
128 | @param rdclass: The class of the rdataset | |
129 | @type rdclass: int | |
130 | @param rdtype: The type of the rdataset | |
131 | @type rdtype: int | |
132 | @param covers: The covered type. | |
133 | @type covers: int | |
134 | @param create: If True, create the rdataset if it is not found. | |
135 | @type create: bool | |
136 | @rtype: dns.rdataset.Rdataset object or None | |
121 | *rdclass*, an ``int``, the class of the rdataset. | |
122 | ||
123 | *rdtype*, an ``int``, the type of the rdataset. | |
124 | ||
125 | *covers*, an ``int``, the covered type. Usually this value is | |
126 | dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or | |
127 | dns.rdatatype.RRSIG, then the covers value will be the rdata | |
128 | type the SIG/RRSIG covers. The library treats the SIG and RRSIG | |
129 | types as if they were a family of | |
130 | types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much | |
131 | easier to work with than if RRSIGs covering different rdata | |
132 | types were aggregated into a single RRSIG rdataset. | |
133 | ||
134 | *create*, a ``bool``. If True, create the rdataset if it is not found. | |
135 | ||
136 | Returns a ``dns.rdataset.Rdataset`` or ``None``. | |
137 | 137 | """ |
138 | 138 | |
139 | 139 | try: |
148 | 148 | |
149 | 149 | If a matching rdataset does not exist, it is not an error. |
150 | 150 | |
151 | @param rdclass: The class of the rdataset | |
152 | @type rdclass: int | |
153 | @param rdtype: The type of the rdataset | |
154 | @type rdtype: int | |
155 | @param covers: The covered type. | |
156 | @type covers: int | |
151 | *rdclass*, an ``int``, the class of the rdataset. | |
152 | ||
153 | *rdtype*, an ``int``, the type of the rdataset. | |
154 | ||
155 | *covers*, an ``int``, the covered type. | |
157 | 156 | """ |
158 | 157 | |
159 | 158 | rds = self.get_rdataset(rdclass, rdtype, covers) |
163 | 162 | def replace_rdataset(self, replacement): |
164 | 163 | """Replace an rdataset. |
165 | 164 | |
166 | It is not an error if there is no rdataset matching I{replacement}. | |
165 | It is not an error if there is no rdataset matching *replacement*. | |
167 | 166 | |
168 | Ownership of the I{replacement} object is transferred to the node; | |
169 | in other words, this method does not store a copy of I{replacement} | |
170 | at the node, it stores I{replacement} itself. | |
167 | Ownership of the *replacement* object is transferred to the node; | |
168 | in other words, this method does not store a copy of *replacement* | |
169 | at the node, it stores *replacement* itself. | |
170 | ||
171 | *replacement*, a ``dns.rdataset.Rdataset``. | |
172 | ||
173 | Raises ``ValueError`` if *replacement* is not a | |
174 | ``dns.rdataset.Rdataset``. | |
171 | 175 | """ |
172 | 176 | |
173 | 177 | if not isinstance(replacement, dns.rdataset.Rdataset): |
0 | from typing import List, Optional, Union | |
1 | from . import rdataset, rdatatype, name | |
2 | class Node: | |
3 | def __init__(self): | |
4 | self.rdatasets : List[rdataset.Rdataset] | |
5 | def to_text(self, name : Union[str,name.Name], **kw) -> str: | |
6 | ... | |
7 | def find_rdataset(self, rdclass : int, rdtype : int, covers=rdatatype.NONE, | |
8 | create=False) -> rdataset.Rdataset: | |
9 | ... | |
10 | def get_rdataset(self, rdclass : int, rdtype : int, covers=rdatatype.NONE, | |
11 | create=False) -> Optional[rdataset.Rdataset]: | |
12 | ... | |
13 | def delete_rdataset(self, rdclass : int, rdtype : int, covers=rdatatype.NONE): | |
14 | ... | |
15 | def replace_rdataset(self, replacement : rdataset.Rdataset) -> None: | |
16 | ... |
0 | # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2001-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
16 | 18 | |
17 | 19 | import dns.exception |
18 | 20 | |
21 | #: Query | |
19 | 22 | QUERY = 0 |
23 | #: Inverse Query (historical) | |
20 | 24 | IQUERY = 1 |
25 | #: Server Status (unspecified and unimplemented anywhere) | |
21 | 26 | STATUS = 2 |
27 | #: Notify | |
22 | 28 | NOTIFY = 4 |
29 | #: Dynamic Update | |
23 | 30 | UPDATE = 5 |
24 | 31 | |
25 | 32 | _by_text = { |
34 | 41 | # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that |
35 | 42 | # would cause the mapping not to be true inverse. |
36 | 43 | |
37 | _by_value = dict((y, x) for x, y in _by_text.items()) | |
44 | _by_value = {y: x for x, y in _by_text.items()} | |
38 | 45 | |
39 | 46 | |
40 | 47 | class UnknownOpcode(dns.exception.DNSException): |
41 | ||
42 | 48 | """An DNS opcode is unknown.""" |
43 | 49 | |
44 | 50 | |
45 | 51 | def from_text(text): |
46 | 52 | """Convert text into an opcode. |
47 | 53 | |
48 | @param text: the textual opcode | |
49 | @type text: string | |
50 | @raises UnknownOpcode: the opcode is unknown | |
51 | @rtype: int | |
54 | *text*, a ``text``, the textual opcode | |
55 | ||
56 | Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown. | |
57 | ||
58 | Returns an ``int``. | |
52 | 59 | """ |
53 | 60 | |
54 | 61 | if text.isdigit(): |
64 | 71 | def from_flags(flags): |
65 | 72 | """Extract an opcode from DNS message flags. |
66 | 73 | |
67 | @param flags: int | |
68 | @rtype: int | |
74 | *flags*, an ``int``, the DNS flags. | |
75 | ||
76 | Returns an ``int``. | |
69 | 77 | """ |
70 | 78 | |
71 | 79 | return (flags & 0x7800) >> 11 |
74 | 82 | def to_flags(value): |
75 | 83 | """Convert an opcode to a value suitable for ORing into DNS message |
76 | 84 | flags. |
77 | @rtype: int | |
85 | ||
86 | *value*, an ``int``, the DNS opcode value. | |
87 | ||
88 | Returns an ``int``. | |
78 | 89 | """ |
79 | 90 | |
80 | 91 | return (value << 11) & 0x7800 |
83 | 94 | def to_text(value): |
84 | 95 | """Convert an opcode to text. |
85 | 96 | |
86 | @param value: the opcdoe | |
87 | @type value: int | |
88 | @raises UnknownOpcode: the opcode is unknown | |
89 | @rtype: string | |
97 | *value*, an ``int`` the opcode value, | |
98 | ||
99 | Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown. | |
100 | ||
101 | Returns a ``text``. | |
90 | 102 | """ |
91 | 103 | |
92 | 104 | text = _by_value.get(value) |
96 | 108 | |
97 | 109 | |
98 | 110 | def is_update(flags): |
99 | """True if the opcode in flags is UPDATE. | |
111 | """Is the opcode in flags UPDATE? | |
100 | 112 | |
101 | @param flags: DNS flags | |
102 | @type flags: int | |
103 | @rtype: bool | |
113 | *flags*, an ``int``, the DNS message flags. | |
114 | ||
115 | Returns a ``bool``. | |
104 | 116 | """ |
105 | 117 | |
106 | 118 | return from_flags(flags) == UPDATE |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
27 | 29 | import dns.inet |
28 | 30 | import dns.name |
29 | 31 | import dns.message |
32 | import dns.rcode | |
30 | 33 | import dns.rdataclass |
31 | 34 | import dns.rdatatype |
32 | from ._compat import long, string_types | |
33 | ||
34 | if sys.version_info > (3,): | |
35 | from ._compat import long, string_types, PY3 | |
36 | ||
37 | if PY3: | |
35 | 38 | select_error = OSError |
36 | 39 | else: |
37 | 40 | select_error = select.error |
41 | 44 | socket_factory = socket.socket |
42 | 45 | |
43 | 46 | class UnexpectedSource(dns.exception.DNSException): |
44 | ||
45 | 47 | """A DNS query response came from an unexpected address or port.""" |
46 | 48 | |
47 | 49 | |
48 | 50 | class BadResponse(dns.exception.FormError): |
49 | ||
50 | 51 | """A DNS query response does not respond to the question asked.""" |
52 | ||
53 | ||
54 | class TransferError(dns.exception.DNSException): | |
55 | """A zone transfer response got a non-zero rcode.""" | |
56 | ||
57 | def __init__(self, rcode): | |
58 | message = 'Zone transfer error: %s' % dns.rcode.to_text(rcode) | |
59 | super(TransferError, self).__init__(message) | |
60 | self.rcode = rcode | |
51 | 61 | |
52 | 62 | |
53 | 63 | def _compute_expiration(timeout): |
56 | 66 | else: |
57 | 67 | return time.time() + timeout |
58 | 68 | |
69 | # This module can use either poll() or select() as the "polling backend". | |
70 | # | |
71 | # A backend function takes an fd, bools for readability, writablity, and | |
72 | # error detection, and a timeout. | |
59 | 73 | |
60 | 74 | def _poll_for(fd, readable, writable, error, timeout): |
61 | """Poll polling backend. | |
62 | @param fd: File descriptor | |
63 | @type fd: int | |
64 | @param readable: Whether to wait for readability | |
65 | @type readable: bool | |
66 | @param writable: Whether to wait for writability | |
67 | @type writable: bool | |
68 | @param timeout: Deadline timeout (expiration time, in seconds) | |
69 | @type timeout: float | |
70 | @return True on success, False on timeout | |
71 | """ | |
75 | """Poll polling backend.""" | |
76 | ||
72 | 77 | event_mask = 0 |
73 | 78 | if readable: |
74 | 79 | event_mask |= select.POLLIN |
89 | 94 | |
90 | 95 | |
91 | 96 | def _select_for(fd, readable, writable, error, timeout): |
92 | """Select polling backend. | |
93 | @param fd: File descriptor | |
94 | @type fd: int | |
95 | @param readable: Whether to wait for readability | |
96 | @type readable: bool | |
97 | @param writable: Whether to wait for writability | |
98 | @type writable: bool | |
99 | @param timeout: Deadline timeout (expiration time, in seconds) | |
100 | @type timeout: float | |
101 | @return True on success, False on timeout | |
102 | """ | |
97 | """Select polling backend.""" | |
98 | ||
103 | 99 | rset, wset, xset = [], [], [] |
104 | 100 | |
105 | 101 | if readable: |
118 | 114 | |
119 | 115 | |
120 | 116 | def _wait_for(fd, readable, writable, error, expiration): |
117 | # Use the selected polling backend to wait for any of the specified | |
118 | # events. An "expiration" absolute time is converted into a relative | |
119 | # timeout. | |
120 | ||
121 | 121 | done = False |
122 | 122 | while not done: |
123 | 123 | if expiration is None: |
136 | 136 | |
137 | 137 | |
138 | 138 | def _set_polling_backend(fn): |
139 | """ | |
140 | Internal API. Do not use. | |
141 | """ | |
139 | # Internal API. Do not use. | |
140 | ||
142 | 141 | global _polling_backend |
143 | 142 | |
144 | 143 | _polling_backend = fn |
164 | 163 | # Convert the first value of the tuple, which is a textual format |
165 | 164 | # address into binary form, so that we are not confused by different |
166 | 165 | # textual representations of the same address |
167 | n1 = dns.inet.inet_pton(af, a1[0]) | |
168 | n2 = dns.inet.inet_pton(af, a2[0]) | |
166 | try: | |
167 | n1 = dns.inet.inet_pton(af, a1[0]) | |
168 | n2 = dns.inet.inet_pton(af, a2[0]) | |
169 | except dns.exception.SyntaxError: | |
170 | return False | |
169 | 171 | return n1 == n2 and a1[1:] == a2[1:] |
170 | 172 | |
171 | 173 | |
192 | 194 | return (af, destination, source) |
193 | 195 | |
194 | 196 | |
197 | def send_udp(sock, what, destination, expiration=None): | |
198 | """Send a DNS message to the specified UDP socket. | |
199 | ||
200 | *sock*, a ``socket``. | |
201 | ||
202 | *what*, a ``binary`` or ``dns.message.Message``, the message to send. | |
203 | ||
204 | *destination*, a destination tuple appropriate for the address family | |
205 | of the socket, specifying where to send the query. | |
206 | ||
207 | *expiration*, a ``float`` or ``None``, the absolute time at which | |
208 | a timeout exception should be raised. If ``None``, no timeout will | |
209 | occur. | |
210 | ||
211 | Returns an ``(int, float)`` tuple of bytes sent and the sent time. | |
212 | """ | |
213 | ||
214 | if isinstance(what, dns.message.Message): | |
215 | what = what.to_wire() | |
216 | _wait_for_writable(sock, expiration) | |
217 | sent_time = time.time() | |
218 | n = sock.sendto(what, destination) | |
219 | return (n, sent_time) | |
220 | ||
221 | ||
222 | def receive_udp(sock, destination, expiration=None, | |
223 | ignore_unexpected=False, one_rr_per_rrset=False, | |
224 | keyring=None, request_mac=b'', ignore_trailing=False): | |
225 | """Read a DNS message from a UDP socket. | |
226 | ||
227 | *sock*, a ``socket``. | |
228 | ||
229 | *destination*, a destination tuple appropriate for the address family | |
230 | of the socket, specifying where the associated query was sent. | |
231 | ||
232 | *expiration*, a ``float`` or ``None``, the absolute time at which | |
233 | a timeout exception should be raised. If ``None``, no timeout will | |
234 | occur. | |
235 | ||
236 | *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from | |
237 | unexpected sources. | |
238 | ||
239 | *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own | |
240 | RRset. | |
241 | ||
242 | *keyring*, a ``dict``, the keyring to use for TSIG. | |
243 | ||
244 | *request_mac*, a ``binary``, the MAC of the request (for TSIG). | |
245 | ||
246 | *ignore_trailing*, a ``bool``. If ``True``, ignore trailing | |
247 | junk at end of the received message. | |
248 | ||
249 | Raises if the message is malformed, if network errors occur, of if | |
250 | there is a timeout. | |
251 | ||
252 | Returns a ``dns.message.Message`` object. | |
253 | """ | |
254 | ||
255 | wire = b'' | |
256 | while 1: | |
257 | _wait_for_readable(sock, expiration) | |
258 | (wire, from_address) = sock.recvfrom(65535) | |
259 | if _addresses_equal(sock.family, from_address, destination) or \ | |
260 | (dns.inet.is_multicast(destination[0]) and | |
261 | from_address[1:] == destination[1:]): | |
262 | break | |
263 | if not ignore_unexpected: | |
264 | raise UnexpectedSource('got a response from ' | |
265 | '%s instead of %s' % (from_address, | |
266 | destination)) | |
267 | received_time = time.time() | |
268 | r = dns.message.from_wire(wire, keyring=keyring, request_mac=request_mac, | |
269 | one_rr_per_rrset=one_rr_per_rrset, | |
270 | ignore_trailing=ignore_trailing) | |
271 | return (r, received_time) | |
272 | ||
195 | 273 | def udp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, |
196 | ignore_unexpected=False, one_rr_per_rrset=False): | |
274 | ignore_unexpected=False, one_rr_per_rrset=False, ignore_trailing=False): | |
197 | 275 | """Return the response obtained after sending a query via UDP. |
198 | 276 | |
199 | @param q: the query | |
200 | @type q: dns.message.Message | |
201 | @param where: where to send the message | |
202 | @type where: string containing an IPv4 or IPv6 address | |
203 | @param timeout: The number of seconds to wait before the query times out. | |
204 | If None, the default, wait forever. | |
205 | @type timeout: float | |
206 | @param port: The port to which to send the message. The default is 53. | |
207 | @type port: int | |
208 | @param af: the address family to use. The default is None, which | |
209 | causes the address family to use to be inferred from the form of where. | |
210 | If the inference attempt fails, AF_INET is used. | |
211 | @type af: int | |
212 | @rtype: dns.message.Message object | |
213 | @param source: source address. The default is the wildcard address. | |
214 | @type source: string | |
215 | @param source_port: The port from which to send the message. | |
277 | *q*, a ``dns.message.Message``, the query to send | |
278 | ||
279 | *where*, a ``text`` containing an IPv4 or IPv6 address, where | |
280 | to send the message. | |
281 | ||
282 | *timeout*, a ``float`` or ``None``, the number of seconds to wait before the | |
283 | query times out. If ``None``, the default, wait forever. | |
284 | ||
285 | *port*, an ``int``, the port send the message to. The default is 53. | |
286 | ||
287 | *af*, an ``int``, the address family to use. The default is ``None``, | |
288 | which causes the address family to use to be inferred from the form of | |
289 | *where*. If the inference attempt fails, AF_INET is used. This | |
290 | parameter is historical; you need never set it. | |
291 | ||
292 | *source*, a ``text`` containing an IPv4 or IPv6 address, specifying | |
293 | the source address. The default is the wildcard address. | |
294 | ||
295 | *source_port*, an ``int``, the port from which to send the message. | |
216 | 296 | The default is 0. |
217 | @type source_port: int | |
218 | @param ignore_unexpected: If True, ignore responses from unexpected | |
219 | sources. The default is False. | |
220 | @type ignore_unexpected: bool | |
221 | @param one_rr_per_rrset: Put each RR into its own RRset | |
222 | @type one_rr_per_rrset: bool | |
297 | ||
298 | *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from | |
299 | unexpected sources. | |
300 | ||
301 | *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own | |
302 | RRset. | |
303 | ||
304 | *ignore_trailing*, a ``bool``. If ``True``, ignore trailing | |
305 | junk at end of the received message. | |
306 | ||
307 | Returns a ``dns.message.Message``. | |
223 | 308 | """ |
224 | 309 | |
225 | 310 | wire = q.to_wire() |
226 | 311 | (af, destination, source) = _destination_and_source(af, where, port, |
227 | 312 | source, source_port) |
228 | 313 | s = socket_factory(af, socket.SOCK_DGRAM, 0) |
229 | begin_time = None | |
314 | received_time = None | |
315 | sent_time = None | |
230 | 316 | try: |
231 | 317 | expiration = _compute_expiration(timeout) |
232 | 318 | s.setblocking(0) |
233 | 319 | if source is not None: |
234 | 320 | s.bind(source) |
235 | _wait_for_writable(s, expiration) | |
236 | begin_time = time.time() | |
237 | s.sendto(wire, destination) | |
238 | while 1: | |
239 | _wait_for_readable(s, expiration) | |
240 | (wire, from_address) = s.recvfrom(65535) | |
241 | if _addresses_equal(af, from_address, destination) or \ | |
242 | (dns.inet.is_multicast(where) and | |
243 | from_address[1:] == destination[1:]): | |
244 | break | |
245 | if not ignore_unexpected: | |
246 | raise UnexpectedSource('got a response from ' | |
247 | '%s instead of %s' % (from_address, | |
248 | destination)) | |
321 | (_, sent_time) = send_udp(s, wire, destination, expiration) | |
322 | (r, received_time) = receive_udp(s, destination, expiration, | |
323 | ignore_unexpected, one_rr_per_rrset, | |
324 | q.keyring, q.mac, ignore_trailing) | |
249 | 325 | finally: |
250 | if begin_time is None: | |
326 | if sent_time is None or received_time is None: | |
251 | 327 | response_time = 0 |
252 | 328 | else: |
253 | response_time = time.time() - begin_time | |
329 | response_time = received_time - sent_time | |
254 | 330 | s.close() |
255 | r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, | |
256 | one_rr_per_rrset=one_rr_per_rrset) | |
257 | 331 | r.time = response_time |
258 | 332 | if not q.is_response(r): |
259 | 333 | raise BadResponse |
289 | 363 | current += sock.send(data[current:]) |
290 | 364 | |
291 | 365 | |
366 | def send_tcp(sock, what, expiration=None): | |
367 | """Send a DNS message to the specified TCP socket. | |
368 | ||
369 | *sock*, a ``socket``. | |
370 | ||
371 | *what*, a ``binary`` or ``dns.message.Message``, the message to send. | |
372 | ||
373 | *expiration*, a ``float`` or ``None``, the absolute time at which | |
374 | a timeout exception should be raised. If ``None``, no timeout will | |
375 | occur. | |
376 | ||
377 | Returns an ``(int, float)`` tuple of bytes sent and the sent time. | |
378 | """ | |
379 | ||
380 | if isinstance(what, dns.message.Message): | |
381 | what = what.to_wire() | |
382 | l = len(what) | |
383 | # copying the wire into tcpmsg is inefficient, but lets us | |
384 | # avoid writev() or doing a short write that would get pushed | |
385 | # onto the net | |
386 | tcpmsg = struct.pack("!H", l) + what | |
387 | _wait_for_writable(sock, expiration) | |
388 | sent_time = time.time() | |
389 | _net_write(sock, tcpmsg, expiration) | |
390 | return (len(tcpmsg), sent_time) | |
391 | ||
392 | def receive_tcp(sock, expiration=None, one_rr_per_rrset=False, | |
393 | keyring=None, request_mac=b'', ignore_trailing=False): | |
394 | """Read a DNS message from a TCP socket. | |
395 | ||
396 | *sock*, a ``socket``. | |
397 | ||
398 | *expiration*, a ``float`` or ``None``, the absolute time at which | |
399 | a timeout exception should be raised. If ``None``, no timeout will | |
400 | occur. | |
401 | ||
402 | *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own | |
403 | RRset. | |
404 | ||
405 | *keyring*, a ``dict``, the keyring to use for TSIG. | |
406 | ||
407 | *request_mac*, a ``binary``, the MAC of the request (for TSIG). | |
408 | ||
409 | *ignore_trailing*, a ``bool``. If ``True``, ignore trailing | |
410 | junk at end of the received message. | |
411 | ||
412 | Raises if the message is malformed, if network errors occur, of if | |
413 | there is a timeout. | |
414 | ||
415 | Returns a ``dns.message.Message`` object. | |
416 | """ | |
417 | ||
418 | ldata = _net_read(sock, 2, expiration) | |
419 | (l,) = struct.unpack("!H", ldata) | |
420 | wire = _net_read(sock, l, expiration) | |
421 | received_time = time.time() | |
422 | r = dns.message.from_wire(wire, keyring=keyring, request_mac=request_mac, | |
423 | one_rr_per_rrset=one_rr_per_rrset, | |
424 | ignore_trailing=ignore_trailing) | |
425 | return (r, received_time) | |
426 | ||
292 | 427 | def _connect(s, address): |
293 | 428 | try: |
294 | 429 | s.connect(address) |
304 | 439 | |
305 | 440 | |
306 | 441 | def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, |
307 | one_rr_per_rrset=False): | |
442 | one_rr_per_rrset=False, ignore_trailing=False): | |
308 | 443 | """Return the response obtained after sending a query via TCP. |
309 | 444 | |
310 | @param q: the query | |
311 | @type q: dns.message.Message object | |
312 | @param where: where to send the message | |
313 | @type where: string containing an IPv4 or IPv6 address | |
314 | @param timeout: The number of seconds to wait before the query times out. | |
315 | If None, the default, wait forever. | |
316 | @type timeout: float | |
317 | @param port: The port to which to send the message. The default is 53. | |
318 | @type port: int | |
319 | @param af: the address family to use. The default is None, which | |
320 | causes the address family to use to be inferred from the form of where. | |
321 | If the inference attempt fails, AF_INET is used. | |
322 | @type af: int | |
323 | @rtype: dns.message.Message object | |
324 | @param source: source address. The default is the wildcard address. | |
325 | @type source: string | |
326 | @param source_port: The port from which to send the message. | |
445 | *q*, a ``dns.message.Message``, the query to send | |
446 | ||
447 | *where*, a ``text`` containing an IPv4 or IPv6 address, where | |
448 | to send the message. | |
449 | ||
450 | *timeout*, a ``float`` or ``None``, the number of seconds to wait before the | |
451 | query times out. If ``None``, the default, wait forever. | |
452 | ||
453 | *port*, an ``int``, the port send the message to. The default is 53. | |
454 | ||
455 | *af*, an ``int``, the address family to use. The default is ``None``, | |
456 | which causes the address family to use to be inferred from the form of | |
457 | *where*. If the inference attempt fails, AF_INET is used. This | |
458 | parameter is historical; you need never set it. | |
459 | ||
460 | *source*, a ``text`` containing an IPv4 or IPv6 address, specifying | |
461 | the source address. The default is the wildcard address. | |
462 | ||
463 | *source_port*, an ``int``, the port from which to send the message. | |
327 | 464 | The default is 0. |
328 | @type source_port: int | |
329 | @param one_rr_per_rrset: Put each RR into its own RRset | |
330 | @type one_rr_per_rrset: bool | |
465 | ||
466 | *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own | |
467 | RRset. | |
468 | ||
469 | *ignore_trailing*, a ``bool``. If ``True``, ignore trailing | |
470 | junk at end of the received message. | |
471 | ||
472 | Returns a ``dns.message.Message``. | |
331 | 473 | """ |
332 | 474 | |
333 | 475 | wire = q.to_wire() |
335 | 477 | source, source_port) |
336 | 478 | s = socket_factory(af, socket.SOCK_STREAM, 0) |
337 | 479 | begin_time = None |
480 | received_time = None | |
338 | 481 | try: |
339 | 482 | expiration = _compute_expiration(timeout) |
340 | 483 | s.setblocking(0) |
342 | 485 | if source is not None: |
343 | 486 | s.bind(source) |
344 | 487 | _connect(s, destination) |
345 | ||
346 | l = len(wire) | |
347 | ||
348 | # copying the wire into tcpmsg is inefficient, but lets us | |
349 | # avoid writev() or doing a short write that would get pushed | |
350 | # onto the net | |
351 | tcpmsg = struct.pack("!H", l) + wire | |
352 | _net_write(s, tcpmsg, expiration) | |
353 | ldata = _net_read(s, 2, expiration) | |
354 | (l,) = struct.unpack("!H", ldata) | |
355 | wire = _net_read(s, l, expiration) | |
488 | send_tcp(s, wire, expiration) | |
489 | (r, received_time) = receive_tcp(s, expiration, one_rr_per_rrset, | |
490 | q.keyring, q.mac, ignore_trailing) | |
356 | 491 | finally: |
357 | if begin_time is None: | |
492 | if begin_time is None or received_time is None: | |
358 | 493 | response_time = 0 |
359 | 494 | else: |
360 | response_time = time.time() - begin_time | |
495 | response_time = received_time - begin_time | |
361 | 496 | s.close() |
362 | r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, | |
363 | one_rr_per_rrset=one_rr_per_rrset) | |
364 | 497 | r.time = response_time |
365 | 498 | if not q.is_response(r): |
366 | 499 | raise BadResponse |
373 | 506 | use_udp=False, keyalgorithm=dns.tsig.default_algorithm): |
374 | 507 | """Return a generator for the responses to a zone transfer. |
375 | 508 | |
376 | @param where: where to send the message | |
377 | @type where: string containing an IPv4 or IPv6 address | |
378 | @param zone: The name of the zone to transfer | |
379 | @type zone: dns.name.Name object or string | |
380 | @param rdtype: The type of zone transfer. The default is | |
381 | dns.rdatatype.AXFR. | |
382 | @type rdtype: int or string | |
383 | @param rdclass: The class of the zone transfer. The default is | |
384 | dns.rdataclass.IN. | |
385 | @type rdclass: int or string | |
386 | @param timeout: The number of seconds to wait for each response message. | |
387 | If None, the default, wait forever. | |
388 | @type timeout: float | |
389 | @param port: The port to which to send the message. The default is 53. | |
390 | @type port: int | |
391 | @param keyring: The TSIG keyring to use | |
392 | @type keyring: dict | |
393 | @param keyname: The name of the TSIG key to use | |
394 | @type keyname: dns.name.Name object or string | |
395 | @param relativize: If True, all names in the zone will be relativized to | |
396 | the zone origin. It is essential that the relativize setting matches | |
397 | the one specified to dns.zone.from_xfr(). | |
398 | @type relativize: bool | |
399 | @param af: the address family to use. The default is None, which | |
400 | causes the address family to use to be inferred from the form of where. | |
401 | If the inference attempt fails, AF_INET is used. | |
402 | @type af: int | |
403 | @param lifetime: The total number of seconds to spend doing the transfer. | |
404 | If None, the default, then there is no limit on the time the transfer may | |
405 | take. | |
406 | @type lifetime: float | |
407 | @rtype: generator of dns.message.Message objects. | |
408 | @param source: source address. The default is the wildcard address. | |
409 | @type source: string | |
410 | @param source_port: The port from which to send the message. | |
509 | *where*. If the inference attempt fails, AF_INET is used. This | |
510 | parameter is historical; you need never set it. | |
511 | ||
512 | *zone*, a ``dns.name.Name`` or ``text``, the name of the zone to transfer. | |
513 | ||
514 | *rdtype*, an ``int`` or ``text``, the type of zone transfer. The | |
515 | default is ``dns.rdatatype.AXFR``. ``dns.rdatatype.IXFR`` can be | |
516 | used to do an incremental transfer instead. | |
517 | ||
518 | *rdclass*, an ``int`` or ``text``, the class of the zone transfer. | |
519 | The default is ``dns.rdataclass.IN``. | |
520 | ||
521 | *timeout*, a ``float``, the number of seconds to wait for each | |
522 | response message. If None, the default, wait forever. | |
523 | ||
524 | *port*, an ``int``, the port send the message to. The default is 53. | |
525 | ||
526 | *keyring*, a ``dict``, the keyring to use for TSIG. | |
527 | ||
528 | *keyname*, a ``dns.name.Name`` or ``text``, the name of the TSIG | |
529 | key to use. | |
530 | ||
531 | *relativize*, a ``bool``. If ``True``, all names in the zone will be | |
532 | relativized to the zone origin. It is essential that the | |
533 | relativize setting matches the one specified to | |
534 | ``dns.zone.from_xfr()`` if using this generator to make a zone. | |
535 | ||
536 | *af*, an ``int``, the address family to use. The default is ``None``, | |
537 | which causes the address family to use to be inferred from the form of | |
538 | *where*. If the inference attempt fails, AF_INET is used. This | |
539 | parameter is historical; you need never set it. | |
540 | ||
541 | *lifetime*, a ``float``, the total number of seconds to spend | |
542 | doing the transfer. If ``None``, the default, then there is no | |
543 | limit on the time the transfer may take. | |
544 | ||
545 | *source*, a ``text`` containing an IPv4 or IPv6 address, specifying | |
546 | the source address. The default is the wildcard address. | |
547 | ||
548 | *source_port*, an ``int``, the port from which to send the message. | |
411 | 549 | The default is 0. |
412 | @type source_port: int | |
413 | @param serial: The SOA serial number to use as the base for an IXFR diff | |
414 | sequence (only meaningful if rdtype == dns.rdatatype.IXFR). | |
415 | @type serial: int | |
416 | @param use_udp: Use UDP (only meaningful for IXFR) | |
417 | @type use_udp: bool | |
418 | @param keyalgorithm: The TSIG algorithm to use; defaults to | |
419 | dns.tsig.default_algorithm | |
420 | @type keyalgorithm: string | |
550 | ||
551 | *serial*, an ``int``, the SOA serial number to use as the base for | |
552 | an IXFR diff sequence (only meaningful if *rdtype* is | |
553 | ``dns.rdatatype.IXFR``). | |
554 | ||
555 | *use_udp*, a ``bool``. If ``True``, use UDP (only meaningful for IXFR). | |
556 | ||
557 | *keyalgorithm*, a ``dns.name.Name`` or ``text``, the TSIG algorithm to use. | |
558 | ||
559 | Raises on errors, and so does the generator. | |
560 | ||
561 | Returns a generator of ``dns.message.Message`` objects. | |
421 | 562 | """ |
422 | 563 | |
423 | 564 | if isinstance(zone, string_types): |
480 | 621 | xfr=True, origin=origin, tsig_ctx=tsig_ctx, |
481 | 622 | multi=True, first=first, |
482 | 623 | one_rr_per_rrset=is_ixfr) |
624 | rcode = r.rcode() | |
625 | if rcode != dns.rcode.NOERROR: | |
626 | raise TransferError(rcode) | |
483 | 627 | tsig_ctx = r.tsig_ctx |
484 | 628 | first = False |
485 | 629 | answer_index = 0 |
0 | from typing import Optional, Union, Dict, Generator, Any | |
1 | from . import message, tsig, rdatatype, rdataclass, name, message | |
2 | def tcp(q : message.Message, where : str, timeout : float = None, port=53, af : Optional[int] = None, source : Optional[str] = None, source_port : int = 0, | |
3 | one_rr_per_rrset=False) -> message.Message: | |
4 | pass | |
5 | ||
6 | def xfr(where : None, zone : Union[name.Name,str], rdtype=rdatatype.AXFR, rdclass=rdataclass.IN, | |
7 | timeout : Optional[float] =None, port=53, keyring : Optional[Dict[name.Name, bytes]] =None, keyname : Union[str,name.Name]=None, relativize=True, | |
8 | af : Optional[int] =None, lifetime : Optional[float]=None, source : Optional[str] =None, source_port=0, serial=0, | |
9 | use_udp=False, keyalgorithm=tsig.default_algorithm) -> Generator[Any,Any,message.Message]: | |
10 | pass | |
11 | ||
12 | def udp(q : message.Message, where : str, timeout : Optional[float] = None, port=53, af : Optional[int] = None, source : Optional[str] = None, source_port=0, | |
13 | ignore_unexpected=False, one_rr_per_rrset=False) -> message.Message: | |
14 | ... |
0 | # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2001-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
17 | 19 | import dns.exception |
18 | 20 | from ._compat import long |
19 | 21 | |
20 | ||
22 | #: No error | |
21 | 23 | NOERROR = 0 |
24 | #: Form error | |
22 | 25 | FORMERR = 1 |
26 | #: Server failure | |
23 | 27 | SERVFAIL = 2 |
28 | #: Name does not exist ("Name Error" in RFC 1025 terminology). | |
24 | 29 | NXDOMAIN = 3 |
30 | #: Not implemented | |
25 | 31 | NOTIMP = 4 |
32 | #: Refused | |
26 | 33 | REFUSED = 5 |
34 | #: Name exists. | |
27 | 35 | YXDOMAIN = 6 |
36 | #: RRset exists. | |
28 | 37 | YXRRSET = 7 |
38 | #: RRset does not exist. | |
29 | 39 | NXRRSET = 8 |
40 | #: Not authoritative. | |
30 | 41 | NOTAUTH = 9 |
42 | #: Name not in zone. | |
31 | 43 | NOTZONE = 10 |
44 | #: Bad EDNS version. | |
32 | 45 | BADVERS = 16 |
33 | 46 | |
34 | 47 | _by_text = { |
50 | 63 | # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that |
51 | 64 | # would cause the mapping not to be a true inverse. |
52 | 65 | |
53 | _by_value = dict((y, x) for x, y in _by_text.items()) | |
66 | _by_value = {y: x for x, y in _by_text.items()} | |
54 | 67 | |
55 | 68 | |
56 | 69 | class UnknownRcode(dns.exception.DNSException): |
57 | ||
58 | 70 | """A DNS rcode is unknown.""" |
59 | 71 | |
60 | 72 | |
61 | 73 | def from_text(text): |
62 | 74 | """Convert text into an rcode. |
63 | 75 | |
64 | @param text: the textual rcode | |
65 | @type text: string | |
66 | @raises UnknownRcode: the rcode is unknown | |
67 | @rtype: int | |
76 | *text*, a ``text``, the textual rcode or an integer in textual form. | |
77 | ||
78 | Raises ``dns.rcode.UnknownRcode`` if the rcode mnemonic is unknown. | |
79 | ||
80 | Returns an ``int``. | |
68 | 81 | """ |
69 | 82 | |
70 | 83 | if text.isdigit(): |
80 | 93 | def from_flags(flags, ednsflags): |
81 | 94 | """Return the rcode value encoded by flags and ednsflags. |
82 | 95 | |
83 | @param flags: the DNS flags | |
84 | @type flags: int | |
85 | @param ednsflags: the EDNS flags | |
86 | @type ednsflags: int | |
87 | @raises ValueError: rcode is < 0 or > 4095 | |
88 | @rtype: int | |
96 | *flags*, an ``int``, the DNS flags field. | |
97 | ||
98 | *ednsflags*, an ``int``, the EDNS flags field. | |
99 | ||
100 | Raises ``ValueError`` if rcode is < 0 or > 4095 | |
101 | ||
102 | Returns an ``int``. | |
89 | 103 | """ |
90 | 104 | |
91 | 105 | value = (flags & 0x000f) | ((ednsflags >> 20) & 0xff0) |
97 | 111 | def to_flags(value): |
98 | 112 | """Return a (flags, ednsflags) tuple which encodes the rcode. |
99 | 113 | |
100 | @param value: the rcode | |
101 | @type value: int | |
102 | @raises ValueError: rcode is < 0 or > 4095 | |
103 | @rtype: (int, int) tuple | |
114 | *value*, an ``int``, the rcode. | |
115 | ||
116 | Raises ``ValueError`` if rcode is < 0 or > 4095. | |
117 | ||
118 | Returns an ``(int, int)`` tuple. | |
104 | 119 | """ |
105 | 120 | |
106 | 121 | if value < 0 or value > 4095: |
113 | 128 | def to_text(value): |
114 | 129 | """Convert rcode into text. |
115 | 130 | |
116 | @param value: the rcode | |
117 | @type value: int | |
118 | @rtype: string | |
131 | *value*, and ``int``, the rcode. | |
132 | ||
133 | Raises ``ValueError`` if rcode is < 0 or > 4095. | |
134 | ||
135 | Returns a ``text``. | |
119 | 136 | """ |
120 | 137 | |
138 | if value < 0 or value > 4095: | |
139 | raise ValueError('rcode must be >= 0 and <= 4095') | |
121 | 140 | text = _by_value.get(value) |
122 | 141 | if text is None: |
123 | 142 | text = str(value) |
0 | # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2001-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | """DNS rdata. | |
16 | ||
17 | @var _rdata_modules: A dictionary mapping a (rdclass, rdtype) tuple to | |
18 | the module which implements that type. | |
19 | @type _rdata_modules: dict | |
20 | @var _module_prefix: The prefix to use when forming modules names. The | |
21 | default is 'dns.rdtypes'. Changing this value will break the library. | |
22 | @type _module_prefix: string | |
23 | @var _hex_chunk: At most this many octets that will be represented in each | |
24 | chunk of hexstring that _hexify() produces before whitespace occurs. | |
25 | @type _hex_chunk: int""" | |
17 | """DNS rdata.""" | |
26 | 18 | |
27 | 19 | from io import BytesIO |
28 | 20 | import base64 |
36 | 28 | import dns.wiredata |
37 | 29 | from ._compat import xrange, string_types, text_type |
38 | 30 | |
31 | try: | |
32 | import threading as _threading | |
33 | except ImportError: | |
34 | import dummy_threading as _threading | |
35 | ||
39 | 36 | _hex_chunksize = 32 |
40 | 37 | |
41 | 38 | |
42 | 39 | def _hexify(data, chunksize=_hex_chunksize): |
43 | 40 | """Convert a binary string into its hex encoding, broken up into chunks |
44 | of I{chunksize} characters separated by a space. | |
45 | ||
46 | @param data: the binary string | |
47 | @type data: string | |
48 | @param chunksize: the chunk size. Default is L{dns.rdata._hex_chunksize} | |
49 | @rtype: string | |
41 | of chunksize characters separated by a space. | |
50 | 42 | """ |
51 | 43 | |
52 | 44 | line = binascii.hexlify(data) |
59 | 51 | |
60 | 52 | def _base64ify(data, chunksize=_base64_chunksize): |
61 | 53 | """Convert a binary string into its base64 encoding, broken up into chunks |
62 | of I{chunksize} characters separated by a space. | |
63 | ||
64 | @param data: the binary string | |
65 | @type data: string | |
66 | @param chunksize: the chunk size. Default is | |
67 | L{dns.rdata._base64_chunksize} | |
68 | @rtype: string | |
54 | of chunksize characters separated by a space. | |
69 | 55 | """ |
70 | 56 | |
71 | 57 | line = base64.b64encode(data) |
76 | 62 | __escaped = bytearray(b'"\\') |
77 | 63 | |
78 | 64 | def _escapify(qstring): |
79 | """Escape the characters in a quoted string which need it. | |
80 | ||
81 | @param qstring: the string | |
82 | @type qstring: string | |
83 | @returns: the escaped string | |
84 | @rtype: string | |
85 | """ | |
65 | """Escape the characters in a quoted string which need it.""" | |
86 | 66 | |
87 | 67 | if isinstance(qstring, text_type): |
88 | 68 | qstring = qstring.encode() |
103 | 83 | def _truncate_bitmap(what): |
104 | 84 | """Determine the index of greatest byte that isn't all zeros, and |
105 | 85 | return the bitmap that contains all the bytes less than that index. |
106 | ||
107 | @param what: a string of octets representing a bitmap. | |
108 | @type what: string | |
109 | @rtype: string | |
110 | 86 | """ |
111 | 87 | |
112 | 88 | for i in xrange(len(what) - 1, -1, -1): |
116 | 92 | |
117 | 93 | |
118 | 94 | class Rdata(object): |
119 | ||
120 | """Base class for all DNS rdata types. | |
121 | """ | |
95 | """Base class for all DNS rdata types.""" | |
122 | 96 | |
123 | 97 | __slots__ = ['rdclass', 'rdtype'] |
124 | 98 | |
125 | 99 | def __init__(self, rdclass, rdtype): |
126 | 100 | """Initialize an rdata. |
127 | @param rdclass: The rdata class | |
128 | @type rdclass: int | |
129 | @param rdtype: The rdata type | |
130 | @type rdtype: int | |
101 | ||
102 | *rdclass*, an ``int`` is the rdataclass of the Rdata. | |
103 | *rdtype*, an ``int`` is the rdatatype of the Rdata. | |
131 | 104 | """ |
132 | 105 | |
133 | 106 | self.rdclass = rdclass |
134 | 107 | self.rdtype = rdtype |
135 | 108 | |
136 | 109 | def covers(self): |
137 | """DNS SIG/RRSIG rdatas apply to a specific type; this type is | |
110 | """Return the type a Rdata covers. | |
111 | ||
112 | DNS SIG/RRSIG rdatas apply to a specific type; this type is | |
138 | 113 | returned by the covers() function. If the rdata type is not |
139 | 114 | SIG or RRSIG, dns.rdatatype.NONE is returned. This is useful when |
140 | 115 | creating rdatasets, allowing the rdataset to contain only RRSIGs |
141 | 116 | of a particular type, e.g. RRSIG(NS). |
142 | @rtype: int | |
117 | ||
118 | Returns an ``int``. | |
143 | 119 | """ |
144 | 120 | |
145 | 121 | return dns.rdatatype.NONE |
148 | 124 | """Return a 32-bit type value, the least significant 16 bits of |
149 | 125 | which are the ordinary DNS type, and the upper 16 bits of which are |
150 | 126 | the "covered" type, if any. |
151 | @rtype: int | |
127 | ||
128 | Returns an ``int``. | |
152 | 129 | """ |
153 | 130 | |
154 | 131 | return self.covers() << 16 | self.rdtype |
155 | 132 | |
156 | 133 | def to_text(self, origin=None, relativize=True, **kw): |
157 | 134 | """Convert an rdata to text format. |
158 | @rtype: string | |
159 | """ | |
135 | ||
136 | Returns a ``text``. | |
137 | """ | |
138 | ||
160 | 139 | raise NotImplementedError |
161 | 140 | |
162 | 141 | def to_wire(self, file, compress=None, origin=None): |
163 | 142 | """Convert an rdata to wire format. |
164 | @rtype: string | |
143 | ||
144 | Returns a ``binary``. | |
165 | 145 | """ |
166 | 146 | |
167 | 147 | raise NotImplementedError |
168 | 148 | |
169 | 149 | def to_digestable(self, origin=None): |
170 | 150 | """Convert rdata to a format suitable for digesting in hashes. This |
171 | is also the DNSSEC canonical form.""" | |
151 | is also the DNSSEC canonical form. | |
152 | ||
153 | Returns a ``binary``. | |
154 | """ | |
155 | ||
172 | 156 | f = BytesIO() |
173 | 157 | self.to_wire(f, None, origin) |
174 | 158 | return f.getvalue() |
175 | 159 | |
176 | 160 | def validate(self): |
177 | 161 | """Check that the current contents of the rdata's fields are |
178 | valid. If you change an rdata by assigning to its fields, | |
162 | valid. | |
163 | ||
164 | If you change an rdata by assigning to its fields, | |
179 | 165 | it is a good idea to call validate() when you are done making |
180 | 166 | changes. |
181 | """ | |
167 | ||
168 | Raises various exceptions if there are problems. | |
169 | ||
170 | Returns ``None``. | |
171 | """ | |
172 | ||
182 | 173 | dns.rdata.from_text(self.rdclass, self.rdtype, self.to_text()) |
183 | 174 | |
184 | 175 | def __repr__(self): |
196 | 187 | |
197 | 188 | def _cmp(self, other): |
198 | 189 | """Compare an rdata with another rdata of the same rdtype and |
199 | rdclass. Return < 0 if self < other in the DNSSEC ordering, | |
200 | 0 if self == other, and > 0 if self > other. | |
190 | rdclass. | |
191 | ||
192 | Return < 0 if self < other in the DNSSEC ordering, 0 if self | |
193 | == other, and > 0 if self > other. | |
194 | ||
201 | 195 | """ |
202 | 196 | our = self.to_digestable(dns.name.root) |
203 | 197 | their = other.to_digestable(dns.name.root) |
204 | 198 | if our == their: |
205 | 199 | return 0 |
206 | if our > their: | |
200 | elif our > their: | |
207 | 201 | return 1 |
208 | ||
209 | return -1 | |
202 | else: | |
203 | return -1 | |
210 | 204 | |
211 | 205 | def __eq__(self, other): |
212 | 206 | if not isinstance(other, Rdata): |
252 | 246 | |
253 | 247 | @classmethod |
254 | 248 | def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): |
255 | """Build an rdata object from text format. | |
256 | ||
257 | @param rdclass: The rdata class | |
258 | @type rdclass: int | |
259 | @param rdtype: The rdata type | |
260 | @type rdtype: int | |
261 | @param tok: The tokenizer | |
262 | @type tok: dns.tokenizer.Tokenizer | |
263 | @param origin: The origin to use for relative names | |
264 | @type origin: dns.name.Name | |
265 | @param relativize: should names be relativized? | |
266 | @type relativize: bool | |
267 | @rtype: dns.rdata.Rdata instance | |
268 | """ | |
269 | ||
270 | 249 | raise NotImplementedError |
271 | 250 | |
272 | 251 | @classmethod |
273 | 252 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): |
274 | """Build an rdata object from wire format | |
275 | ||
276 | @param rdclass: The rdata class | |
277 | @type rdclass: int | |
278 | @param rdtype: The rdata type | |
279 | @type rdtype: int | |
280 | @param wire: The wire-format message | |
281 | @type wire: string | |
282 | @param current: The offset in wire of the beginning of the rdata. | |
283 | @type current: int | |
284 | @param rdlen: The length of the wire-format rdata | |
285 | @type rdlen: int | |
286 | @param origin: The origin to use for relative names | |
287 | @type origin: dns.name.Name | |
288 | @rtype: dns.rdata.Rdata instance | |
289 | """ | |
290 | ||
291 | 253 | raise NotImplementedError |
292 | 254 | |
293 | 255 | def choose_relativity(self, origin=None, relativize=True): |
295 | 257 | relativization. |
296 | 258 | """ |
297 | 259 | |
298 | pass | |
299 | ||
300 | ||
301 | 260 | class GenericRdata(Rdata): |
302 | 261 | |
303 | """Generate Rdata Class | |
262 | """Generic Rdata Class | |
304 | 263 | |
305 | 264 | This class is used for rdata types for which we have no better |
306 | 265 | implementation. It implements the DNS "unknown RRs" scheme. |
318 | 277 | @classmethod |
319 | 278 | def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): |
320 | 279 | token = tok.get() |
321 | if not token.is_identifier() or token.value != '\#': | |
280 | if not token.is_identifier() or token.value != r'\#': | |
322 | 281 | raise dns.exception.SyntaxError( |
323 | 282 | r'generic rdata does not start with \#') |
324 | 283 | length = tok.get_int() |
344 | 303 | |
345 | 304 | _rdata_modules = {} |
346 | 305 | _module_prefix = 'dns.rdtypes' |
347 | ||
306 | _import_lock = _threading.Lock() | |
348 | 307 | |
349 | 308 | def get_rdata_class(rdclass, rdtype): |
350 | 309 | |
351 | 310 | def import_module(name): |
352 | mod = __import__(name) | |
353 | components = name.split('.') | |
354 | for comp in components[1:]: | |
355 | mod = getattr(mod, comp) | |
356 | return mod | |
311 | with _import_lock: | |
312 | mod = __import__(name) | |
313 | components = name.split('.') | |
314 | for comp in components[1:]: | |
315 | mod = getattr(mod, comp) | |
316 | return mod | |
357 | 317 | |
358 | 318 | mod = _rdata_modules.get((rdclass, rdtype)) |
359 | 319 | rdclass_text = dns.rdataclass.to_text(rdclass) |
391 | 351 | Once a class is chosen, its from_text() class method is called |
392 | 352 | with the parameters to this function. |
393 | 353 | |
394 | If I{tok} is a string, then a tokenizer is created and the string | |
354 | If *tok* is a ``text``, then a tokenizer is created and the string | |
395 | 355 | is used as its input. |
396 | 356 | |
397 | @param rdclass: The rdata class | |
398 | @type rdclass: int | |
399 | @param rdtype: The rdata type | |
400 | @type rdtype: int | |
401 | @param tok: The tokenizer or input text | |
402 | @type tok: dns.tokenizer.Tokenizer or string | |
403 | @param origin: The origin to use for relative names | |
404 | @type origin: dns.name.Name | |
405 | @param relativize: Should names be relativized? | |
406 | @type relativize: bool | |
407 | @rtype: dns.rdata.Rdata instance""" | |
357 | *rdclass*, an ``int``, the rdataclass. | |
358 | ||
359 | *rdtype*, an ``int``, the rdatatype. | |
360 | ||
361 | *tok*, a ``dns.tokenizer.Tokenizer`` or a ``text``. | |
362 | ||
363 | *origin*, a ``dns.name.Name`` (or ``None``), the | |
364 | origin to use for relative names. | |
365 | ||
366 | *relativize*, a ``bool``. If true, name will be relativized to | |
367 | the specified origin. | |
368 | ||
369 | Returns an instance of the chosen Rdata subclass. | |
370 | """ | |
408 | 371 | |
409 | 372 | if isinstance(tok, string_types): |
410 | 373 | tok = dns.tokenizer.Tokenizer(tok) |
438 | 401 | Once a class is chosen, its from_wire() class method is called |
439 | 402 | with the parameters to this function. |
440 | 403 | |
441 | @param rdclass: The rdata class | |
442 | @type rdclass: int | |
443 | @param rdtype: The rdata type | |
444 | @type rdtype: int | |
445 | @param wire: The wire-format message | |
446 | @type wire: string | |
447 | @param current: The offset in wire of the beginning of the rdata. | |
448 | @type current: int | |
449 | @param rdlen: The length of the wire-format rdata | |
450 | @type rdlen: int | |
451 | @param origin: The origin to use for relative names | |
452 | @type origin: dns.name.Name | |
453 | @rtype: dns.rdata.Rdata instance""" | |
404 | *rdclass*, an ``int``, the rdataclass. | |
405 | ||
406 | *rdtype*, an ``int``, the rdatatype. | |
407 | ||
408 | *wire*, a ``binary``, the wire-format message. | |
409 | ||
410 | *current*, an ``int``, the offset in wire of the beginning of | |
411 | the rdata. | |
412 | ||
413 | *rdlen*, an ``int``, the length of the wire-format rdata | |
414 | ||
415 | *origin*, a ``dns.name.Name`` (or ``None``). If not ``None``, | |
416 | then names will be relativized to this origin. | |
417 | ||
418 | Returns an instance of the chosen Rdata subclass. | |
419 | """ | |
454 | 420 | |
455 | 421 | wire = dns.wiredata.maybe_wrap(wire) |
456 | 422 | cls = get_rdata_class(rdclass, rdtype) |
457 | 423 | return cls.from_wire(rdclass, rdtype, wire, current, rdlen, origin) |
424 | ||
425 | ||
426 | class RdatatypeExists(dns.exception.DNSException): | |
427 | """DNS rdatatype already exists.""" | |
428 | supp_kwargs = {'rdclass', 'rdtype'} | |
429 | fmt = "The rdata type with class {rdclass} and rdtype {rdtype} " + \ | |
430 | "already exists." | |
431 | ||
432 | ||
433 | def register_type(implementation, rdtype, rdtype_text, is_singleton=False, | |
434 | rdclass=dns.rdataclass.IN): | |
435 | """Dynamically register a module to handle an rdatatype. | |
436 | ||
437 | *implementation*, a module implementing the type in the usual dnspython | |
438 | way. | |
439 | ||
440 | *rdtype*, an ``int``, the rdatatype to register. | |
441 | ||
442 | *rdtype_text*, a ``text``, the textual form of the rdatatype. | |
443 | ||
444 | *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e. | |
445 | RRsets of the type can have only one member.) | |
446 | ||
447 | *rdclass*, the rdataclass of the type, or ``dns.rdataclass.ANY`` if | |
448 | it applies to all classes. | |
449 | """ | |
450 | ||
451 | existing_cls = get_rdata_class(rdclass, rdtype) | |
452 | if existing_cls != GenericRdata: | |
453 | raise RdatatypeExists(rdclass=rdclass, rdtype=rdtype) | |
454 | _rdata_modules[(rdclass, rdtype)] = implementation | |
455 | dns.rdatatype.register_type(rdtype, rdtype_text, is_singleton) |
0 | from typing import Dict, Tuple, Any, Optional | |
1 | from .name import Name | |
2 | class Rdata: | |
3 | def __init__(self): | |
4 | self.address : str | |
5 | def to_wire(self, file, compress : Optional[Dict[Name,int]], origin : Optional[Name]) -> bytes: | |
6 | ... | |
7 | @classmethod | |
8 | def from_text(cls, rdclass : int, rdtype : int, tok, origin=None, relativize=True): | |
9 | ... | |
10 | _rdata_modules : Dict[Tuple[Any,Rdata],Any] | |
11 | ||
12 | def from_text(rdclass : int, rdtype : int, tok : Optional[str], origin : Optional[Name] = None, relativize : bool = True): | |
13 | ... | |
14 | ||
15 | def from_wire(rdclass : int, rdtype : int, wire : bytes, current : int, rdlen : int, origin : Optional[Name] = None): | |
16 | ... |
0 | # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2001-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | """DNS Rdata Classes. | |
16 | ||
17 | @var _by_text: The rdata class textual name to value mapping | |
18 | @type _by_text: dict | |
19 | @var _by_value: The rdata class value to textual name mapping | |
20 | @type _by_value: dict | |
21 | @var _metaclasses: If an rdataclass is a metaclass, there will be a mapping | |
22 | whose key is the rdatatype value and whose value is True in this dictionary. | |
23 | @type _metaclasses: dict""" | |
17 | """DNS Rdata Classes.""" | |
24 | 18 | |
25 | 19 | import re |
26 | 20 | |
46 | 40 | # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that |
47 | 41 | # would cause the mapping not to be true inverse. |
48 | 42 | |
49 | _by_value = dict((y, x) for x, y in _by_text.items()) | |
43 | _by_value = {y: x for x, y in _by_text.items()} | |
50 | 44 | |
51 | 45 | # Now that we've built the inverse map, we can add class aliases to |
52 | 46 | # the _by_text mapping. |
66 | 60 | |
67 | 61 | |
68 | 62 | class UnknownRdataclass(dns.exception.DNSException): |
69 | ||
70 | 63 | """A DNS class is unknown.""" |
71 | 64 | |
72 | 65 | |
73 | 66 | def from_text(text): |
74 | 67 | """Convert text into a DNS rdata class value. |
75 | @param text: the text | |
76 | @type text: string | |
77 | @rtype: int | |
78 | @raises dns.rdataclass.UnknownRdataclass: the class is unknown | |
79 | @raises ValueError: the rdata class value is not >= 0 and <= 65535 | |
68 | ||
69 | The input text can be a defined DNS RR class mnemonic or | |
70 | instance of the DNS generic class syntax. | |
71 | ||
72 | For example, "IN" and "CLASS1" will both result in a value of 1. | |
73 | ||
74 | Raises ``dns.rdatatype.UnknownRdataclass`` if the class is unknown. | |
75 | ||
76 | Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535. | |
77 | ||
78 | Returns an ``int``. | |
80 | 79 | """ |
81 | 80 | |
82 | 81 | value = _by_text.get(text.upper()) |
91 | 90 | |
92 | 91 | |
93 | 92 | def to_text(value): |
94 | """Convert a DNS rdata class to text. | |
95 | @param value: the rdata class value | |
96 | @type value: int | |
97 | @rtype: string | |
98 | @raises ValueError: the rdata class value is not >= 0 and <= 65535 | |
93 | """Convert a DNS rdata type value to text. | |
94 | ||
95 | If the value has a known mnemonic, it will be used, otherwise the | |
96 | DNS generic class syntax will be used. | |
97 | ||
98 | Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535. | |
99 | ||
100 | Returns a ``str``. | |
99 | 101 | """ |
100 | 102 | |
101 | 103 | if value < 0 or value > 65535: |
107 | 109 | |
108 | 110 | |
109 | 111 | def is_metaclass(rdclass): |
110 | """True if the class is a metaclass. | |
111 | @param rdclass: the rdata class | |
112 | @type rdclass: int | |
113 | @rtype: bool""" | |
112 | """True if the specified class is a metaclass. | |
113 | ||
114 | The currently defined metaclasses are ANY and NONE. | |
115 | ||
116 | *rdclass* is an ``int``. | |
117 | """ | |
114 | 118 | |
115 | 119 | if rdclass in _metaclasses: |
116 | 120 | return True |
0 | # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2001-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
30 | 32 | |
31 | 33 | |
32 | 34 | class DifferingCovers(dns.exception.DNSException): |
33 | ||
34 | 35 | """An attempt was made to add a DNS SIG/RRSIG whose covered type |
35 | 36 | is not the same as that of the other rdatas in the rdataset.""" |
36 | 37 | |
37 | 38 | |
38 | 39 | class IncompatibleTypes(dns.exception.DNSException): |
39 | ||
40 | 40 | """An attempt was made to add DNS RR data of an incompatible type.""" |
41 | 41 | |
42 | 42 | |
43 | 43 | class Rdataset(dns.set.Set): |
44 | 44 | |
45 | """A DNS rdataset. | |
46 | ||
47 | @ivar rdclass: The class of the rdataset | |
48 | @type rdclass: int | |
49 | @ivar rdtype: The type of the rdataset | |
50 | @type rdtype: int | |
51 | @ivar covers: The covered type. Usually this value is | |
52 | dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or | |
53 | dns.rdatatype.RRSIG, then the covers value will be the rdata | |
54 | type the SIG/RRSIG covers. The library treats the SIG and RRSIG | |
55 | types as if they were a family of | |
56 | types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much | |
57 | easier to work with than if RRSIGs covering different rdata | |
58 | types were aggregated into a single RRSIG rdataset. | |
59 | @type covers: int | |
60 | @ivar ttl: The DNS TTL (Time To Live) value | |
61 | @type ttl: int | |
62 | """ | |
45 | """A DNS rdataset.""" | |
63 | 46 | |
64 | 47 | __slots__ = ['rdclass', 'rdtype', 'covers', 'ttl'] |
65 | 48 | |
66 | def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE): | |
49 | def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE, ttl=0): | |
67 | 50 | """Create a new rdataset of the specified class and type. |
68 | 51 | |
69 | @see: the description of the class instance variables for the | |
70 | meaning of I{rdclass} and I{rdtype}""" | |
52 | *rdclass*, an ``int``, the rdataclass. | |
53 | ||
54 | *rdtype*, an ``int``, the rdatatype. | |
55 | ||
56 | *covers*, an ``int``, the covered rdatatype. | |
57 | ||
58 | *ttl*, an ``int``, the TTL. | |
59 | """ | |
71 | 60 | |
72 | 61 | super(Rdataset, self).__init__() |
73 | 62 | self.rdclass = rdclass |
74 | 63 | self.rdtype = rdtype |
75 | 64 | self.covers = covers |
76 | self.ttl = 0 | |
65 | self.ttl = ttl | |
77 | 66 | |
78 | 67 | def _clone(self): |
79 | 68 | obj = super(Rdataset, self)._clone() |
84 | 73 | return obj |
85 | 74 | |
86 | 75 | def update_ttl(self, ttl): |
87 | """Set the TTL of the rdataset to be the lesser of the set's current | |
76 | """Perform TTL minimization. | |
77 | ||
78 | Set the TTL of the rdataset to be the lesser of the set's current | |
88 | 79 | TTL or the specified TTL. If the set contains no rdatas, set the TTL |
89 | 80 | to the specified TTL. |
90 | @param ttl: The TTL | |
91 | @type ttl: int""" | |
81 | ||
82 | *ttl*, an ``int``. | |
83 | """ | |
92 | 84 | |
93 | 85 | if len(self) == 0: |
94 | 86 | self.ttl = ttl |
98 | 90 | def add(self, rd, ttl=None): |
99 | 91 | """Add the specified rdata to the rdataset. |
100 | 92 | |
101 | If the optional I{ttl} parameter is supplied, then | |
102 | self.update_ttl(ttl) will be called prior to adding the rdata. | |
103 | ||
104 | @param rd: The rdata | |
105 | @type rd: dns.rdata.Rdata object | |
106 | @param ttl: The TTL | |
107 | @type ttl: int""" | |
93 | If the optional *ttl* parameter is supplied, then | |
94 | ``self.update_ttl(ttl)`` will be called prior to adding the rdata. | |
95 | ||
96 | *rd*, a ``dns.rdata.Rdata``, the rdata | |
97 | ||
98 | *ttl*, an ``int``, the TTL. | |
99 | ||
100 | Raises ``dns.rdataset.IncompatibleTypes`` if the type and class | |
101 | do not match the type and class of the rdataset. | |
102 | ||
103 | Raises ``dns.rdataset.DifferingCovers`` if the type is a signature | |
104 | type and the covered type does not match that of the rdataset. | |
105 | """ | |
108 | 106 | |
109 | 107 | # |
110 | 108 | # If we're adding a signature, do some special handling to |
138 | 136 | def update(self, other): |
139 | 137 | """Add all rdatas in other to self. |
140 | 138 | |
141 | @param other: The rdataset from which to update | |
142 | @type other: dns.rdataset.Rdataset object""" | |
139 | *other*, a ``dns.rdataset.Rdataset``, the rdataset from which | |
140 | to update. | |
141 | """ | |
143 | 142 | |
144 | 143 | self.update_ttl(other.ttl) |
145 | 144 | super(Rdataset, self).update(other) |
156 | 155 | return self.to_text() |
157 | 156 | |
158 | 157 | def __eq__(self, other): |
159 | """Two rdatasets are equal if they have the same class, type, and | |
160 | covers, and contain the same rdata. | |
161 | @rtype: bool""" | |
162 | ||
163 | 158 | if not isinstance(other, Rdataset): |
164 | 159 | return False |
165 | 160 | if self.rdclass != other.rdclass or \ |
175 | 170 | override_rdclass=None, **kw): |
176 | 171 | """Convert the rdataset into DNS master file format. |
177 | 172 | |
178 | @see: L{dns.name.Name.choose_relativity} for more information | |
179 | on how I{origin} and I{relativize} determine the way names | |
173 | See ``dns.name.Name.choose_relativity`` for more information | |
174 | on how *origin* and *relativize* determine the way names | |
180 | 175 | are emitted. |
181 | 176 | |
182 | 177 | Any additional keyword arguments are passed on to the rdata |
183 | to_text() method. | |
184 | ||
185 | @param name: If name is not None, emit a RRs with I{name} as | |
186 | the owner name. | |
187 | @type name: dns.name.Name object | |
188 | @param origin: The origin for relative names, or None. | |
189 | @type origin: dns.name.Name object | |
190 | @param relativize: True if names should names be relativized | |
191 | @type relativize: bool""" | |
178 | ``to_text()`` method. | |
179 | ||
180 | *name*, a ``dns.name.Name``. If name is not ``None``, emit RRs with | |
181 | *name* as the owner name. | |
182 | ||
183 | *origin*, a ``dns.name.Name`` or ``None``, the origin for relative | |
184 | names. | |
185 | ||
186 | *relativize*, a ``bool``. If ``True``, names will be relativized | |
187 | to *origin*. | |
188 | """ | |
189 | ||
192 | 190 | if name is not None: |
193 | 191 | name = name.choose_relativity(origin, relativize) |
194 | 192 | ntext = str(name) |
207 | 205 | # some dynamic updates, so we don't need to print out the TTL |
208 | 206 | # (which is meaningless anyway). |
209 | 207 | # |
210 | s.write(u'%s%s%s %s\n' % (ntext, pad, | |
211 | dns.rdataclass.to_text(rdclass), | |
212 | dns.rdatatype.to_text(self.rdtype))) | |
208 | s.write(u'{}{}{} {}\n'.format(ntext, pad, | |
209 | dns.rdataclass.to_text(rdclass), | |
210 | dns.rdatatype.to_text(self.rdtype))) | |
213 | 211 | else: |
214 | 212 | for rd in self: |
215 | 213 | s.write(u'%s%s%d %s %s %s\n' % |
226 | 224 | override_rdclass=None, want_shuffle=True): |
227 | 225 | """Convert the rdataset to wire format. |
228 | 226 | |
229 | @param name: The owner name of the RRset that will be emitted | |
230 | @type name: dns.name.Name object | |
231 | @param file: The file to which the wire format data will be appended | |
232 | @type file: file | |
233 | @param compress: The compression table to use; the default is None. | |
234 | @type compress: dict | |
235 | @param origin: The origin to be appended to any relative names when | |
236 | they are emitted. The default is None. | |
237 | @returns: the number of records emitted | |
238 | @rtype: int | |
227 | *name*, a ``dns.name.Name`` is the owner name to use. | |
228 | ||
229 | *file* is the file where the name is emitted (typically a | |
230 | BytesIO file). | |
231 | ||
232 | *compress*, a ``dict``, is the compression table to use. If | |
233 | ``None`` (the default), names will not be compressed. | |
234 | ||
235 | *origin* is a ``dns.name.Name`` or ``None``. If the name is | |
236 | relative and origin is not ``None``, then *origin* will be appended | |
237 | to it. | |
238 | ||
239 | *override_rdclass*, an ``int``, is used as the class instead of the | |
240 | class of the rdataset. This is useful when rendering rdatasets | |
241 | associated with dynamic updates. | |
242 | ||
243 | *want_shuffle*, a ``bool``. If ``True``, then the order of the | |
244 | Rdatas within the Rdataset will be shuffled before rendering. | |
245 | ||
246 | Returns an ``int``, the number of records emitted. | |
239 | 247 | """ |
240 | 248 | |
241 | 249 | if override_rdclass is not None: |
271 | 279 | return len(self) |
272 | 280 | |
273 | 281 | def match(self, rdclass, rdtype, covers): |
274 | """Returns True if this rdataset matches the specified class, type, | |
275 | and covers""" | |
282 | """Returns ``True`` if this rdataset matches the specified class, | |
283 | type, and covers. | |
284 | """ | |
276 | 285 | if self.rdclass == rdclass and \ |
277 | 286 | self.rdtype == rdtype and \ |
278 | 287 | self.covers == covers: |
284 | 293 | """Create an rdataset with the specified class, type, and TTL, and with |
285 | 294 | the specified list of rdatas in text format. |
286 | 295 | |
287 | @rtype: dns.rdataset.Rdataset object | |
296 | Returns a ``dns.rdataset.Rdataset`` object. | |
288 | 297 | """ |
289 | 298 | |
290 | 299 | if isinstance(rdclass, string_types): |
303 | 312 | """Create an rdataset with the specified class, type, and TTL, and with |
304 | 313 | the specified rdatas in text format. |
305 | 314 | |
306 | @rtype: dns.rdataset.Rdataset object | |
315 | Returns a ``dns.rdataset.Rdataset`` object. | |
307 | 316 | """ |
308 | 317 | |
309 | 318 | return from_text_list(rdclass, rdtype, ttl, text_rdatas) |
313 | 322 | """Create an rdataset with the specified TTL, and with |
314 | 323 | the specified list of rdata objects. |
315 | 324 | |
316 | @rtype: dns.rdataset.Rdataset object | |
325 | Returns a ``dns.rdataset.Rdataset`` object. | |
317 | 326 | """ |
318 | 327 | |
319 | 328 | if len(rdatas) == 0: |
331 | 340 | """Create an rdataset with the specified TTL, and with |
332 | 341 | the specified rdata objects. |
333 | 342 | |
334 | @rtype: dns.rdataset.Rdataset object | |
343 | Returns a ``dns.rdataset.Rdataset`` object. | |
335 | 344 | """ |
336 | 345 | |
337 | 346 | return from_rdata_list(ttl, rdatas) |
0 | from typing import Optional, Dict, List, Union | |
1 | from io import BytesIO | |
2 | from . import exception, name, set, rdatatype, rdata, rdataset | |
3 | ||
4 | class DifferingCovers(exception.DNSException): | |
5 | """An attempt was made to add a DNS SIG/RRSIG whose covered type | |
6 | is not the same as that of the other rdatas in the rdataset.""" | |
7 | ||
8 | ||
9 | class IncompatibleTypes(exception.DNSException): | |
10 | """An attempt was made to add DNS RR data of an incompatible type.""" | |
11 | ||
12 | ||
13 | class Rdataset(set.Set): | |
14 | def __init__(self, rdclass, rdtype, covers=rdatatype.NONE, ttl=0): | |
15 | self.rdclass : int = rdclass | |
16 | self.rdtype : int = rdtype | |
17 | self.covers : int = covers | |
18 | self.ttl : int = ttl | |
19 | ||
20 | def update_ttl(self, ttl : int) -> None: | |
21 | ... | |
22 | ||
23 | def add(self, rd : rdata.Rdata, ttl : Optional[int] =None): | |
24 | ... | |
25 | ||
26 | def union_update(self, other : Rdataset): | |
27 | ... | |
28 | ||
29 | def intersection_update(self, other : Rdataset): | |
30 | ... | |
31 | ||
32 | def update(self, other : Rdataset): | |
33 | ... | |
34 | ||
35 | def to_text(self, name : Optional[name.Name] =None, origin : Optional[name.Name] =None, relativize=True, | |
36 | override_rdclass : Optional[int] =None, **kw) -> bytes: | |
37 | ... | |
38 | ||
39 | def to_wire(self, name : Optional[name.Name], file : BytesIO, compress : Optional[Dict[name.Name, int]] = None, origin : Optional[name.Name] = None, | |
40 | override_rdclass : Optional[int] = None, want_shuffle=True) -> int: | |
41 | ... | |
42 | ||
43 | def match(self, rdclass : int, rdtype : int, covers : int) -> bool: | |
44 | ... | |
45 | ||
46 | ||
47 | def from_text_list(rdclass : Union[int,str], rdtype : Union[int,str], ttl : int, text_rdatas : str) -> rdataset.Rdataset: | |
48 | ... | |
49 | ||
50 | def from_text(rdclass : Union[int,str], rdtype : Union[int,str], ttl : int, *text_rdatas : str) -> rdataset.Rdataset: | |
51 | ... | |
52 | ||
53 | def from_rdata_list(ttl : int, rdatas : List[rdata.Rdata]) -> rdataset.Rdataset: | |
54 | ... | |
55 | ||
56 | def from_rdata(ttl : int, *rdatas : List[rdata.Rdata]) -> rdataset.Rdataset: | |
57 | ... |
0 | # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2001-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | """DNS Rdata Types. | |
16 | ||
17 | @var _by_text: The rdata type textual name to value mapping | |
18 | @type _by_text: dict | |
19 | @var _by_value: The rdata type value to textual name mapping | |
20 | @type _by_value: dict | |
21 | @var _metatypes: If an rdatatype is a metatype, there will be a mapping | |
22 | whose key is the rdatatype value and whose value is True in this dictionary. | |
23 | @type _metatypes: dict | |
24 | @var _singletons: If an rdatatype is a singleton, there will be a mapping | |
25 | whose key is the rdatatype value and whose value is True in this dictionary. | |
26 | @type _singletons: dict""" | |
17 | """DNS Rdata Types.""" | |
27 | 18 | |
28 | 19 | import re |
29 | 20 | |
81 | 72 | HIP = 55 |
82 | 73 | CDS = 59 |
83 | 74 | CDNSKEY = 60 |
75 | OPENPGPKEY = 61 | |
84 | 76 | CSYNC = 62 |
85 | 77 | SPF = 99 |
86 | 78 | UNSPEC = 103 |
152 | 144 | 'HIP': HIP, |
153 | 145 | 'CDS': CDS, |
154 | 146 | 'CDNSKEY': CDNSKEY, |
147 | 'OPENPGPKEY': OPENPGPKEY, | |
155 | 148 | 'CSYNC': CSYNC, |
156 | 149 | 'SPF': SPF, |
157 | 150 | 'UNSPEC': UNSPEC, |
175 | 168 | # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that |
176 | 169 | # would cause the mapping not to be true inverse. |
177 | 170 | |
178 | _by_value = dict((y, x) for x, y in _by_text.items()) | |
179 | ||
171 | _by_value = {y: x for x, y in _by_text.items()} | |
180 | 172 | |
181 | 173 | _metatypes = { |
182 | 174 | OPT: True |
187 | 179 | NXT: True, |
188 | 180 | DNAME: True, |
189 | 181 | NSEC: True, |
190 | # CNAME is technically a singleton, but we allow multiple CNAMEs. | |
182 | CNAME: True, | |
191 | 183 | } |
192 | 184 | |
193 | 185 | _unknown_type_pattern = re.compile('TYPE([0-9]+)$', re.I) |
194 | 186 | |
195 | 187 | |
196 | 188 | class UnknownRdatatype(dns.exception.DNSException): |
197 | ||
198 | 189 | """DNS resource record type is unknown.""" |
199 | 190 | |
200 | 191 | |
201 | 192 | def from_text(text): |
202 | 193 | """Convert text into a DNS rdata type value. |
203 | @param text: the text | |
204 | @type text: string | |
205 | @raises dns.rdatatype.UnknownRdatatype: the type is unknown | |
206 | @raises ValueError: the rdata type value is not >= 0 and <= 65535 | |
207 | @rtype: int""" | |
194 | ||
195 | The input text can be a defined DNS RR type mnemonic or | |
196 | instance of the DNS generic type syntax. | |
197 | ||
198 | For example, "NS" and "TYPE2" will both result in a value of 2. | |
199 | ||
200 | Raises ``dns.rdatatype.UnknownRdatatype`` if the type is unknown. | |
201 | ||
202 | Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535. | |
203 | ||
204 | Returns an ``int``. | |
205 | """ | |
208 | 206 | |
209 | 207 | value = _by_text.get(text.upper()) |
210 | 208 | if value is None: |
218 | 216 | |
219 | 217 | |
220 | 218 | def to_text(value): |
221 | """Convert a DNS rdata type to text. | |
222 | @param value: the rdata type value | |
223 | @type value: int | |
224 | @raises ValueError: the rdata type value is not >= 0 and <= 65535 | |
225 | @rtype: string""" | |
219 | """Convert a DNS rdata type value to text. | |
220 | ||
221 | If the value has a known mnemonic, it will be used, otherwise the | |
222 | DNS generic type syntax will be used. | |
223 | ||
224 | Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535. | |
225 | ||
226 | Returns a ``str``. | |
227 | """ | |
226 | 228 | |
227 | 229 | if value < 0 or value > 65535: |
228 | 230 | raise ValueError("type must be between >= 0 and <= 65535") |
233 | 235 | |
234 | 236 | |
235 | 237 | def is_metatype(rdtype): |
236 | """True if the type is a metatype. | |
237 | @param rdtype: the type | |
238 | @type rdtype: int | |
239 | @rtype: bool""" | |
238 | """True if the specified type is a metatype. | |
239 | ||
240 | *rdtype* is an ``int``. | |
241 | ||
242 | The currently defined metatypes are TKEY, TSIG, IXFR, AXFR, MAILA, | |
243 | MAILB, ANY, and OPT. | |
244 | ||
245 | Returns a ``bool``. | |
246 | """ | |
240 | 247 | |
241 | 248 | if rdtype >= TKEY and rdtype <= ANY or rdtype in _metatypes: |
242 | 249 | return True |
244 | 251 | |
245 | 252 | |
246 | 253 | def is_singleton(rdtype): |
247 | """True if the type is a singleton. | |
248 | @param rdtype: the type | |
249 | @type rdtype: int | |
250 | @rtype: bool""" | |
254 | """Is the specified type a singleton type? | |
255 | ||
256 | Singleton types can only have a single rdata in an rdataset, or a single | |
257 | RR in an RRset. | |
258 | ||
259 | The currently defined singleton types are CNAME, DNAME, NSEC, NXT, and | |
260 | SOA. | |
261 | ||
262 | *rdtype* is an ``int``. | |
263 | ||
264 | Returns a ``bool``. | |
265 | """ | |
251 | 266 | |
252 | 267 | if rdtype in _singletons: |
253 | 268 | return True |
254 | 269 | return False |
270 | ||
271 | ||
272 | def register_type(rdtype, rdtype_text, is_singleton=False): # pylint: disable=redefined-outer-name | |
273 | """Dynamically register an rdatatype. | |
274 | ||
275 | *rdtype*, an ``int``, the rdatatype to register. | |
276 | ||
277 | *rdtype_text*, a ``text``, the textual form of the rdatatype. | |
278 | ||
279 | *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e. | |
280 | RRsets of the type can have only one member.) | |
281 | """ | |
282 | ||
283 | _by_text[rdtype_text] = rdtype | |
284 | _by_value[rdtype] = rdtype_text | |
285 | if is_singleton: | |
286 | _singletons[rdtype] = True |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2016 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2004-2007, 2009-2011, 2016 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
79 | 81 | self.altitude = altitude |
80 | 82 | |
81 | 83 | def to_text(self, origin=None, relativize=True, **kw): |
82 | return '%s %s %s' % (self.latitude.decode(), | |
84 | return '{} {} {}'.format(self.latitude.decode(), | |
83 | 85 | self.longitude.decode(), |
84 | 86 | self.altitude.decode()) |
85 | 87 |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
44 | 46 | self.os = os |
45 | 47 | |
46 | 48 | def to_text(self, origin=None, relativize=True, **kw): |
47 | return '"%s" "%s"' % (dns.rdata._escapify(self.cpu), | |
48 | dns.rdata._escapify(self.os)) | |
49 | return '"{}" "{}"'.format(dns.rdata._escapify(self.cpu), | |
50 | dns.rdata._escapify(self.os)) | |
49 | 51 | |
50 | 52 | @classmethod |
51 | 53 | def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2010, 2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
45 | 47 | |
46 | 48 | def to_text(self, origin=None, relativize=True, **kw): |
47 | 49 | if self.subaddress: |
48 | return '"%s" "%s"' % (dns.rdata._escapify(self.address), | |
50 | return '"{}" "{}"'.format(dns.rdata._escapify(self.address), | |
49 | 51 | dns.rdata._escapify(self.subaddress)) |
50 | 52 | else: |
51 | 53 | return '"%s"' % dns.rdata._escapify(self.address) |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
155 | 157 | if self.size != _default_size or \ |
156 | 158 | self.horizontal_precision != _default_hprec or \ |
157 | 159 | self.vertical_precision != _default_vprec: |
158 | text += " %0.2fm %0.2fm %0.2fm" % ( | |
160 | text += " {:0.2f}m {:0.2f}m {:0.2f}m".format( | |
159 | 161 | self.size / 100.0, self.horizontal_precision / 100.0, |
160 | 162 | self.vertical_precision / 100.0 |
161 | 163 | ) |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
49 | 51 | bits.append(dns.rdatatype.to_text(window * 256 + |
50 | 52 | i * 8 + j)) |
51 | 53 | text += (' ' + ' '.join(bits)) |
52 | return '%s%s' % (next, text) | |
54 | return '{}{}'.format(next, text) | |
53 | 55 | |
54 | 56 | @classmethod |
55 | 57 | def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): |
0 | # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2004-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
20 | 22 | import dns.exception |
21 | 23 | import dns.rdata |
22 | 24 | import dns.rdatatype |
23 | from dns._compat import xrange, text_type | |
25 | from dns._compat import xrange, text_type, PY3 | |
24 | 26 | |
25 | try: | |
27 | # pylint: disable=deprecated-string-function | |
28 | if PY3: | |
29 | b32_hex_to_normal = bytes.maketrans(b'0123456789ABCDEFGHIJKLMNOPQRSTUV', | |
30 | b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') | |
31 | b32_normal_to_hex = bytes.maketrans(b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', | |
32 | b'0123456789ABCDEFGHIJKLMNOPQRSTUV') | |
33 | else: | |
26 | 34 | b32_hex_to_normal = string.maketrans('0123456789ABCDEFGHIJKLMNOPQRSTUV', |
27 | 35 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') |
28 | 36 | b32_normal_to_hex = string.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', |
29 | 37 | '0123456789ABCDEFGHIJKLMNOPQRSTUV') |
30 | except AttributeError: | |
31 | b32_hex_to_normal = bytes.maketrans(b'0123456789ABCDEFGHIJKLMNOPQRSTUV', | |
32 | b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') | |
33 | b32_normal_to_hex = bytes.maketrans(b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', | |
34 | b'0123456789ABCDEFGHIJKLMNOPQRSTUV') | |
38 | # pylint: enable=deprecated-string-function | |
39 | ||
35 | 40 | |
36 | 41 | # hash algorithm constants |
37 | 42 | SHA1 = 1 |
129 | 134 | new_window = nrdtype // 256 |
130 | 135 | if new_window != window: |
131 | 136 | if octets != 0: |
132 | windows.append((window, ''.join(bitmap[0:octets]))) | |
137 | windows.append((window, bitmap[0:octets])) | |
133 | 138 | bitmap = bytearray(b'\0' * 32) |
134 | 139 | window = new_window |
135 | 140 | offset = nrdtype % 256 |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2016 Nominum, Inc. | |
3 | # | |
4 | # Permission to use, copy, modify, and distribute this software and its | |
5 | # documentation for any purpose with or without fee is hereby granted, | |
6 | # provided that the above copyright notice and this permission notice | |
7 | # appear in all copies. | |
8 | # | |
9 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | ||
17 | import base64 | |
18 | ||
19 | import dns.exception | |
20 | import dns.rdata | |
21 | import dns.tokenizer | |
22 | ||
23 | class OPENPGPKEY(dns.rdata.Rdata): | |
24 | ||
25 | """OPENPGPKEY record | |
26 | ||
27 | @ivar key: the key | |
28 | @type key: bytes | |
29 | @see: RFC 7929 | |
30 | """ | |
31 | ||
32 | def __init__(self, rdclass, rdtype, key): | |
33 | super(OPENPGPKEY, self).__init__(rdclass, rdtype) | |
34 | self.key = key | |
35 | ||
36 | def to_text(self, origin=None, relativize=True, **kw): | |
37 | return dns.rdata._base64ify(self.key) | |
38 | ||
39 | @classmethod | |
40 | def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): | |
41 | chunks = [] | |
42 | while 1: | |
43 | t = tok.get().unescape() | |
44 | if t.is_eol_or_eof(): | |
45 | break | |
46 | if not t.is_identifier(): | |
47 | raise dns.exception.SyntaxError | |
48 | chunks.append(t.value.encode()) | |
49 | b64 = b''.join(chunks) | |
50 | key = base64.b64decode(b64) | |
51 | return cls(rdclass, rdtype, key) | |
52 | ||
53 | def to_wire(self, file, compress=None, origin=None): | |
54 | file.write(self.key) | |
55 | ||
56 | @classmethod | |
57 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): | |
58 | key = wire[current: current + rdlen].unwrap() | |
59 | return cls(rdclass, rdtype, key) |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
38 | 40 | def to_text(self, origin=None, relativize=True, **kw): |
39 | 41 | mbox = self.mbox.choose_relativity(origin, relativize) |
40 | 42 | txt = self.txt.choose_relativity(origin, relativize) |
41 | return "%s %s" % (str(mbox), str(txt)) | |
43 | return "{} {}".format(str(mbox), str(txt)) | |
42 | 44 | |
43 | 45 | @classmethod |
44 | 46 | def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2005-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2005-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # Copyright (C) 2015 Red Hat, Inc. |
2 | 4 | # |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
16 | 18 | |
17 | 19 | __all__ = [ |
18 | 20 | 'AFSDB', |
21 | 'AVC', | |
22 | 'CAA', | |
19 | 23 | 'CDNSKEY', |
20 | 24 | 'CDS', |
21 | 25 | 'CERT', |
22 | 26 | 'CNAME', |
27 | 'CSYNC', | |
23 | 28 | 'DLV', |
24 | 29 | 'DNAME', |
25 | 30 | 'DNSKEY', |
36 | 41 | 'NSEC', |
37 | 42 | 'NSEC3', |
38 | 43 | 'NSEC3PARAM', |
39 | 'TLSA', | |
44 | 'OPENPGPKEY', | |
40 | 45 | 'PTR', |
41 | 46 | 'RP', |
42 | 47 | 'RRSIG', |
44 | 49 | 'SOA', |
45 | 50 | 'SPF', |
46 | 51 | 'SSHFP', |
52 | 'TLSA', | |
47 | 53 | 'TXT', |
54 | 'URI', | |
48 | 55 | 'X25', |
49 | 56 | ] |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
3 | # | |
4 | # Permission to use, copy, modify, and distribute this software and its | |
5 | # documentation for any purpose with or without fee is hereby granted, | |
6 | # provided that the above copyright notice and this permission notice | |
7 | # appear in all copies. | |
8 | # | |
9 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | ||
17 | import dns.rdtypes.mxbase | |
18 | import struct | |
19 | ||
20 | class A(dns.rdtypes.mxbase.MXBase): | |
21 | ||
22 | """A record for Chaosnet | |
23 | @ivar domain: the domain of the address | |
24 | @type domain: dns.name.Name object | |
25 | @ivar address: the 16-bit address | |
26 | @type address: int""" | |
27 | ||
28 | __slots__ = ['domain', 'address'] | |
29 | ||
30 | def __init__(self, rdclass, rdtype, address, domain): | |
31 | super(A, self).__init__(rdclass, rdtype, address, domain) | |
32 | self.domain = domain | |
33 | self.address = address | |
34 | ||
35 | def to_text(self, origin=None, relativize=True, **kw): | |
36 | domain = self.domain.choose_relativity(origin, relativize) | |
37 | return '%s %o' % (domain, self.address) | |
38 | ||
39 | @classmethod | |
40 | def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): | |
41 | domain = tok.get_name() | |
42 | address = tok.get_uint16(base=8) | |
43 | domain = domain.choose_relativity(origin, relativize) | |
44 | tok.get_eol() | |
45 | return cls(rdclass, rdtype, address, domain) | |
46 | ||
47 | def to_wire(self, file, compress=None, origin=None): | |
48 | self.domain.to_wire(file, compress, origin) | |
49 | pref = struct.pack("!H", self.address) | |
50 | file.write(pref) | |
51 | ||
52 | def to_digestable(self, origin=None): | |
53 | return self.domain.to_digestable(origin) + \ | |
54 | struct.pack("!H", self.address) | |
55 | ||
56 | @classmethod | |
57 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): | |
58 | (domain, cused) = dns.name.from_wire(wire[: current + rdlen-2], | |
59 | current) | |
60 | current += cused | |
61 | (address,) = struct.unpack('!H', wire[current: current + 2]) | |
62 | if cused+2 != rdlen: | |
63 | raise dns.exception.FormError | |
64 | if origin is not None: | |
65 | domain = domain.relativize(origin) | |
66 | return cls(rdclass, rdtype, address, domain) | |
67 | ||
68 | def choose_relativity(self, origin=None, relativize=True): | |
69 | self.domain = self.domain.choose_relativity(origin, relativize) |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
3 | # | |
4 | # Permission to use, copy, modify, and distribute this software and its | |
5 | # documentation for any purpose with or without fee is hereby granted, | |
6 | # provided that the above copyright notice and this permission notice | |
7 | # appear in all copies. | |
8 | # | |
9 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | ||
17 | """Class CH rdata type classes.""" | |
18 | ||
19 | __all__ = [ | |
20 | 'A', | |
21 | ] |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
47 | 49 | |
48 | 50 | @classmethod |
49 | 51 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): |
50 | address = dns.ipv4.inet_ntoa(wire[current: current + rdlen]).decode() | |
52 | address = dns.ipv4.inet_ntoa(wire[current: current + rdlen]) | |
51 | 53 | return cls(rdclass, rdtype, address) |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
17 | import binascii | |
18 | import codecs | |
15 | 19 | import struct |
16 | import binascii | |
17 | 20 | |
18 | 21 | import dns.exception |
19 | 22 | import dns.inet |
20 | 23 | import dns.rdata |
21 | 24 | import dns.tokenizer |
22 | from dns._compat import xrange | |
25 | from dns._compat import xrange, maybe_chr | |
23 | 26 | |
24 | 27 | |
25 | 28 | class APLItem(object): |
62 | 65 | # |
63 | 66 | last = 0 |
64 | 67 | for i in xrange(len(address) - 1, -1, -1): |
65 | if address[i] != chr(0): | |
68 | if address[i] != maybe_chr(0): | |
66 | 69 | last = i + 1 |
67 | 70 | break |
68 | 71 | address = address[0: last] |
120 | 123 | |
121 | 124 | @classmethod |
122 | 125 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): |
126 | ||
123 | 127 | items = [] |
124 | 128 | while 1: |
125 | 129 | if rdlen == 0: |
141 | 145 | l = len(address) |
142 | 146 | if header[0] == 1: |
143 | 147 | if l < 4: |
144 | address += '\x00' * (4 - l) | |
148 | address += b'\x00' * (4 - l) | |
145 | 149 | address = dns.inet.inet_ntop(dns.inet.AF_INET, address) |
146 | 150 | elif header[0] == 2: |
147 | 151 | if l < 16: |
148 | address += '\x00' * (16 - l) | |
152 | address += b'\x00' * (16 - l) | |
149 | 153 | address = dns.inet.inet_ntop(dns.inet.AF_INET6, address) |
150 | 154 | else: |
151 | 155 | # |
152 | 156 | # This isn't really right according to the RFC, but it |
153 | 157 | # seems better than throwing an exception |
154 | 158 | # |
155 | address = address.encode('hex_codec') | |
159 | address = codecs.encode(address, 'hex_codec') | |
156 | 160 | current += afdlen |
157 | 161 | rdlen -= afdlen |
158 | 162 | item = APLItem(header[0], negation, address, header[1]) |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
19 | 21 | 'AAAA', |
20 | 22 | 'APL', |
21 | 23 | 'DHCID', |
24 | 'IPSECKEY', | |
22 | 25 | 'KX', |
23 | 26 | 'NAPTR', |
24 | 27 | 'NSAP', |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
17 | 19 | __all__ = [ |
18 | 20 | 'ANY', |
19 | 21 | 'IN', |
22 | 'CH', | |
20 | 23 | 'euibase', |
21 | 24 | 'mxbase', |
22 | 25 | 'nsbase', |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
37 | 39 | # We construct the inverse mapping programmatically to ensure that we |
38 | 40 | # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that |
39 | 41 | # would cause the mapping not to be true inverse. |
40 | _flag_by_value = dict((y, x) for x, y in _flag_by_text.items()) | |
42 | _flag_by_value = {y: x for x, y in _flag_by_text.items()} | |
41 | 43 | |
42 | 44 | |
43 | 45 | def flags_to_text_set(flags): |
0 | from typing import Set, Any | |
1 | ||
2 | SEP : int | |
3 | REVOKE : int | |
4 | ZONE : int | |
5 | ||
6 | def flags_to_text_set(flags : int) -> Set[str]: | |
7 | ... | |
8 | ||
9 | def flags_from_text_set(texts_set) -> int: | |
10 | ... | |
11 | ||
12 | from .. import rdata | |
13 | ||
14 | class DNSKEYBase(rdata.Rdata): | |
15 | def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key): | |
16 | self.flags : int | |
17 | self.protocol : int | |
18 | self.key : str | |
19 | self.algorithm : int | |
20 | ||
21 | def to_text(self, origin : Any = None, relativize=True, **kw : Any): | |
22 | ... | |
23 | ||
24 | @classmethod | |
25 | def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): | |
26 | ... | |
27 | ||
28 | def to_wire(self, file, compress=None, origin=None): | |
29 | ... | |
30 | ||
31 | @classmethod | |
32 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): | |
33 | ... | |
34 | ||
35 | def flags_to_text_set(self) -> Set[str]: | |
36 | ... |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2010, 2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2006-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
19 | 21 | import dns.exception |
20 | 22 | import dns.rdata |
21 | 23 | import dns.tokenizer |
22 | from dns._compat import binary_type | |
24 | from dns._compat import binary_type, string_types | |
23 | 25 | |
24 | 26 | |
25 | 27 | class TXTBase(dns.rdata.Rdata): |
26 | 28 | |
27 | 29 | """Base class for rdata that is like a TXT record |
28 | 30 | |
29 | @ivar strings: the text strings | |
30 | @type strings: list of string | |
31 | @ivar strings: the strings | |
32 | @type strings: list of binary | |
31 | 33 | @see: RFC 1035""" |
32 | 34 | |
33 | 35 | __slots__ = ['strings'] |
34 | 36 | |
35 | 37 | def __init__(self, rdclass, rdtype, strings): |
36 | 38 | super(TXTBase, self).__init__(rdclass, rdtype) |
37 | if isinstance(strings, str): | |
39 | if isinstance(strings, binary_type) or \ | |
40 | isinstance(strings, string_types): | |
38 | 41 | strings = [strings] |
39 | self.strings = strings[:] | |
42 | self.strings = [] | |
43 | for string in strings: | |
44 | if isinstance(string, string_types): | |
45 | string = string.encode() | |
46 | self.strings.append(string) | |
40 | 47 | |
41 | 48 | def to_text(self, origin=None, relativize=True, **kw): |
42 | 49 | txt = '' |
43 | 50 | prefix = '' |
44 | 51 | for s in self.strings: |
45 | txt += '%s"%s"' % (prefix, dns.rdata._escapify(s)) | |
52 | txt += '{}"{}"'.format(prefix, dns.rdata._escapify(s)) | |
46 | 53 | prefix = ' ' |
47 | 54 | return txt |
48 | 55 |
0 | # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2001-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
31 | 33 | |
32 | 34 | |
33 | 35 | class Renderer(object): |
34 | ||
35 | 36 | """Helper class for building DNS wire-format messages. |
36 | 37 | |
37 | 38 | Most applications can use the higher-level L{dns.message.Message} |
53 | 54 | r.add_tsig(keyname, secret, 300, 1, 0, '', request_mac) |
54 | 55 | wire = r.get_wire() |
55 | 56 | |
56 | @ivar output: where rendering is written | |
57 | @type output: BytesIO object | |
58 | @ivar id: the message id | |
59 | @type id: int | |
60 | @ivar flags: the message flags | |
61 | @type flags: int | |
62 | @ivar max_size: the maximum size of the message | |
63 | @type max_size: int | |
64 | @ivar origin: the origin to use when rendering relative names | |
65 | @type origin: dns.name.Name object | |
66 | @ivar compress: the compression table | |
67 | @type compress: dict | |
68 | @ivar section: the section currently being rendered | |
69 | @type section: int (dns.renderer.QUESTION, dns.renderer.ANSWER, | |
70 | dns.renderer.AUTHORITY, or dns.renderer.ADDITIONAL) | |
71 | @ivar counts: list of the number of RRs in each section | |
72 | @type counts: int list of length 4 | |
73 | @ivar mac: the MAC of the rendered message (if TSIG was used) | |
74 | @type mac: string | |
57 | output, a BytesIO, where rendering is written | |
58 | ||
59 | id: the message id | |
60 | ||
61 | flags: the message flags | |
62 | ||
63 | max_size: the maximum size of the message | |
64 | ||
65 | origin: the origin to use when rendering relative names | |
66 | ||
67 | compress: the compression table | |
68 | ||
69 | section: an int, the section currently being rendered | |
70 | ||
71 | counts: list of the number of RRs in each section | |
72 | ||
73 | mac: the MAC of the rendered message (if TSIG was used) | |
75 | 74 | """ |
76 | 75 | |
77 | 76 | def __init__(self, id=None, flags=0, max_size=65535, origin=None): |
78 | """Initialize a new renderer. | |
79 | ||
80 | @param id: the message id | |
81 | @type id: int | |
82 | @param flags: the DNS message flags | |
83 | @type flags: int | |
84 | @param max_size: the maximum message size; the default is 65535. | |
85 | If rendering results in a message greater than I{max_size}, | |
86 | then L{dns.exception.TooBig} will be raised. | |
87 | @type max_size: int | |
88 | @param origin: the origin to use when rendering relative names | |
89 | @type origin: dns.name.Name or None. | |
90 | """ | |
77 | """Initialize a new renderer.""" | |
91 | 78 | |
92 | 79 | self.output = BytesIO() |
93 | 80 | if id is None: |
104 | 91 | self.mac = '' |
105 | 92 | |
106 | 93 | def _rollback(self, where): |
107 | """Truncate the output buffer at offset I{where}, and remove any | |
94 | """Truncate the output buffer at offset *where*, and remove any | |
108 | 95 | compression table entries that pointed beyond the truncation |
109 | 96 | point. |
110 | ||
111 | @param where: the offset | |
112 | @type where: int | |
113 | 97 | """ |
114 | 98 | |
115 | 99 | self.output.seek(where) |
127 | 111 | Sections must be rendered order: QUESTION, ANSWER, AUTHORITY, |
128 | 112 | ADDITIONAL. Sections may be empty. |
129 | 113 | |
130 | @param section: the section | |
131 | @type section: int | |
132 | @raises dns.exception.FormError: an attempt was made to set | |
114 | Raises dns.exception.FormError if an attempt was made to set | |
133 | 115 | a section value less than the current section. |
134 | 116 | """ |
135 | 117 | |
139 | 121 | self.section = section |
140 | 122 | |
141 | 123 | def add_question(self, qname, rdtype, rdclass=dns.rdataclass.IN): |
142 | """Add a question to the message. | |
143 | ||
144 | @param qname: the question name | |
145 | @type qname: dns.name.Name | |
146 | @param rdtype: the question rdata type | |
147 | @type rdtype: int | |
148 | @param rdclass: the question rdata class | |
149 | @type rdclass: int | |
150 | """ | |
124 | """Add a question to the message.""" | |
151 | 125 | |
152 | 126 | self._set_section(QUESTION) |
153 | 127 | before = self.output.tell() |
164 | 138 | |
165 | 139 | Any keyword arguments are passed on to the rdataset's to_wire() |
166 | 140 | routine. |
167 | ||
168 | @param section: the section | |
169 | @type section: int | |
170 | @param rrset: the rrset | |
171 | @type rrset: dns.rrset.RRset object | |
172 | 141 | """ |
173 | 142 | |
174 | 143 | self._set_section(section) |
186 | 155 | |
187 | 156 | Any keyword arguments are passed on to the rdataset's to_wire() |
188 | 157 | routine. |
189 | ||
190 | @param section: the section | |
191 | @type section: int | |
192 | @param name: the owner name | |
193 | @type name: dns.name.Name object | |
194 | @param rdataset: the rdataset | |
195 | @type rdataset: dns.rdataset.Rdataset object | |
196 | 158 | """ |
197 | 159 | |
198 | 160 | self._set_section(section) |
206 | 168 | self.counts[section] += n |
207 | 169 | |
208 | 170 | def add_edns(self, edns, ednsflags, payload, options=None): |
209 | """Add an EDNS OPT record to the message. | |
210 | ||
211 | @param edns: The EDNS level to use. | |
212 | @type edns: int | |
213 | @param ednsflags: EDNS flag values. | |
214 | @type ednsflags: int | |
215 | @param payload: The EDNS sender's payload field, which is the maximum | |
216 | size of UDP datagram the sender can handle. | |
217 | @type payload: int | |
218 | @param options: The EDNS options list | |
219 | @type options: list of dns.edns.Option instances | |
220 | @see: RFC 2671 | |
221 | """ | |
171 | """Add an EDNS OPT record to the message.""" | |
222 | 172 | |
223 | 173 | # make sure the EDNS version in ednsflags agrees with edns |
224 | 174 | ednsflags &= long(0xFF00FFFF) |
254 | 204 | |
255 | 205 | def add_tsig(self, keyname, secret, fudge, id, tsig_error, other_data, |
256 | 206 | request_mac, algorithm=dns.tsig.default_algorithm): |
257 | """Add a TSIG signature to the message. | |
258 | ||
259 | @param keyname: the TSIG key name | |
260 | @type keyname: dns.name.Name object | |
261 | @param secret: the secret to use | |
262 | @type secret: string | |
263 | @param fudge: TSIG time fudge | |
264 | @type fudge: int | |
265 | @param id: the message id to encode in the tsig signature | |
266 | @type id: int | |
267 | @param tsig_error: TSIG error code; default is 0. | |
268 | @type tsig_error: int | |
269 | @param other_data: TSIG other data. | |
270 | @type other_data: string | |
271 | @param request_mac: This message is a response to the request which | |
272 | had the specified MAC. | |
273 | @type request_mac: string | |
274 | @param algorithm: the TSIG algorithm to use | |
275 | @type algorithm: dns.name.Name object | |
276 | """ | |
277 | ||
278 | self._set_section(ADDITIONAL) | |
279 | before = self.output.tell() | |
207 | """Add a TSIG signature to the message.""" | |
208 | ||
280 | 209 | s = self.output.getvalue() |
281 | 210 | (tsig_rdata, self.mac, ctx) = dns.tsig.sign(s, |
282 | 211 | keyname, |
288 | 217 | other_data, |
289 | 218 | request_mac, |
290 | 219 | algorithm=algorithm) |
220 | self._write_tsig(tsig_rdata, keyname) | |
221 | ||
222 | def add_multi_tsig(self, ctx, keyname, secret, fudge, id, tsig_error, | |
223 | other_data, request_mac, | |
224 | algorithm=dns.tsig.default_algorithm): | |
225 | """Add a TSIG signature to the message. Unlike add_tsig(), this can be | |
226 | used for a series of consecutive DNS envelopes, e.g. for a zone | |
227 | transfer over TCP [RFC2845, 4.4]. | |
228 | ||
229 | For the first message in the sequence, give ctx=None. For each | |
230 | subsequent message, give the ctx that was returned from the | |
231 | add_multi_tsig() call for the previous message.""" | |
232 | ||
233 | s = self.output.getvalue() | |
234 | (tsig_rdata, self.mac, ctx) = dns.tsig.sign(s, | |
235 | keyname, | |
236 | secret, | |
237 | int(time.time()), | |
238 | fudge, | |
239 | id, | |
240 | tsig_error, | |
241 | other_data, | |
242 | request_mac, | |
243 | ctx=ctx, | |
244 | first=ctx is None, | |
245 | multi=True, | |
246 | algorithm=algorithm) | |
247 | self._write_tsig(tsig_rdata, keyname) | |
248 | return ctx | |
249 | ||
250 | def _write_tsig(self, tsig_rdata, keyname): | |
251 | self._set_section(ADDITIONAL) | |
252 | before = self.output.tell() | |
253 | ||
291 | 254 | keyname.to_wire(self.output, self.compress, self.origin) |
292 | 255 | self.output.write(struct.pack('!HHIH', dns.rdatatype.TSIG, |
293 | 256 | dns.rdataclass.ANY, 0, 0)) |
294 | 257 | rdata_start = self.output.tell() |
295 | 258 | self.output.write(tsig_rdata) |
259 | ||
296 | 260 | after = self.output.tell() |
297 | 261 | assert after - rdata_start < 65536 |
298 | 262 | if after >= self.max_size: |
299 | 263 | self._rollback(before) |
300 | 264 | raise dns.exception.TooBig |
265 | ||
301 | 266 | self.output.seek(rdata_start - 2) |
302 | 267 | self.output.write(struct.pack('!H', after - rdata_start)) |
303 | 268 | self.counts[ADDITIONAL] += 1 |
320 | 285 | self.output.seek(0, 2) |
321 | 286 | |
322 | 287 | def get_wire(self): |
323 | """Return the wire format message. | |
324 | ||
325 | @rtype: string | |
326 | """ | |
288 | """Return the wire format message.""" | |
327 | 289 | |
328 | 290 | return self.output.getvalue() |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | """DNS stub resolver. | |
16 | ||
17 | @var default_resolver: The default resolver object | |
18 | @type default_resolver: dns.resolver.Resolver object""" | |
17 | """DNS stub resolver.""" | |
19 | 18 | |
20 | 19 | import socket |
21 | 20 | import sys |
48 | 47 | import _winreg # pylint: disable=import-error |
49 | 48 | |
50 | 49 | class NXDOMAIN(dns.exception.DNSException): |
51 | ||
52 | 50 | """The DNS query name does not exist.""" |
53 | supp_kwargs = set(['qnames', 'responses']) | |
51 | supp_kwargs = {'qnames', 'responses'} | |
54 | 52 | fmt = None # we have our own __str__ implementation |
55 | 53 | |
56 | 54 | def _check_kwargs(self, qnames, responses=None): |
72 | 70 | if len(qnames) > 1: |
73 | 71 | msg = 'None of DNS query names exist' |
74 | 72 | else: |
75 | msg = self.__doc__[:-1] | |
73 | msg = 'The DNS query name does not exist' | |
76 | 74 | qnames = ', '.join(map(str, qnames)) |
77 | return "%s: %s" % (msg, qnames) | |
75 | return "{}: {}".format(msg, qnames) | |
78 | 76 | |
79 | 77 | def canonical_name(self): |
80 | 78 | if not 'qnames' in self.kwargs: |
106 | 104 | responses0[qname1] = responses1[qname1] |
107 | 105 | return NXDOMAIN(qnames=qnames0, responses=responses0) |
108 | 106 | |
107 | def qnames(self): | |
108 | """All of the names that were tried. | |
109 | ||
110 | Returns a list of ``dns.name.Name``. | |
111 | """ | |
112 | return self.kwargs['qnames'] | |
113 | ||
114 | def responses(self): | |
115 | """A map from queried names to their NXDOMAIN responses. | |
116 | ||
117 | Returns a dict mapping a ``dns.name.Name`` to a | |
118 | ``dns.message.Message``. | |
119 | """ | |
120 | return self.kwargs['responses'] | |
121 | ||
122 | def response(self, qname): | |
123 | """The response for query *qname*. | |
124 | ||
125 | Returns a ``dns.message.Message``. | |
126 | """ | |
127 | return self.kwargs['responses'][qname] | |
128 | ||
109 | 129 | |
110 | 130 | class YXDOMAIN(dns.exception.DNSException): |
111 | ||
112 | 131 | """The DNS query name is too long after DNAME substitution.""" |
113 | 132 | |
114 | 133 | # The definition of the Timeout exception has moved from here to the |
119 | 138 | |
120 | 139 | |
121 | 140 | class NoAnswer(dns.exception.DNSException): |
122 | ||
123 | 141 | """The DNS response does not contain an answer to the question.""" |
124 | 142 | fmt = 'The DNS response does not contain an answer ' + \ |
125 | 143 | 'to the question: {query}' |
126 | supp_kwargs = set(['response']) | |
144 | supp_kwargs = {'response'} | |
127 | 145 | |
128 | 146 | def _fmt_kwargs(self, **kwargs): |
129 | 147 | return super(NoAnswer, self)._fmt_kwargs( |
131 | 149 | |
132 | 150 | |
133 | 151 | class NoNameservers(dns.exception.DNSException): |
134 | ||
135 | 152 | """All nameservers failed to answer the query. |
136 | 153 | |
137 | 154 | errors: list of servers and respective errors |
138 | 155 | The type of errors is |
139 | [(server ip address, any object convertible to string)]. | |
156 | [(server IP address, any object convertible to string)]. | |
140 | 157 | Non-empty errors list will add explanatory message () |
141 | 158 | """ |
142 | 159 | |
143 | 160 | msg = "All nameservers failed to answer the query." |
144 | 161 | fmt = "%s {query}: {errors}" % msg[:-1] |
145 | supp_kwargs = set(['request', 'errors']) | |
162 | supp_kwargs = {'request', 'errors'} | |
146 | 163 | |
147 | 164 | def _fmt_kwargs(self, **kwargs): |
148 | 165 | srv_msgs = [] |
149 | 166 | for err in kwargs['errors']: |
150 | srv_msgs.append('Server %s %s port %s answered %s' % (err[0], | |
167 | srv_msgs.append('Server {} {} port {} answered {}'.format(err[0], | |
151 | 168 | 'TCP' if err[1] else 'UDP', err[2], err[3])) |
152 | 169 | return super(NoNameservers, self)._fmt_kwargs( |
153 | 170 | query=kwargs['request'].question, errors='; '.join(srv_msgs)) |
154 | 171 | |
155 | 172 | |
156 | 173 | class NotAbsolute(dns.exception.DNSException): |
157 | ||
158 | 174 | """An absolute domain name is required but a relative name was provided.""" |
159 | 175 | |
160 | 176 | |
161 | 177 | class NoRootSOA(dns.exception.DNSException): |
162 | ||
163 | 178 | """There is no SOA RR at the DNS root name. This should never happen!""" |
164 | 179 | |
165 | 180 | |
166 | 181 | class NoMetaqueries(dns.exception.DNSException): |
167 | ||
168 | 182 | """DNS metaqueries are not allowed.""" |
169 | 183 | |
170 | 184 | |
171 | 185 | class Answer(object): |
172 | ||
173 | """DNS stub resolver answer | |
186 | """DNS stub resolver answer. | |
174 | 187 | |
175 | 188 | Instances of this class bundle up the result of a successful DNS |
176 | 189 | resolution. |
177 | 190 | |
178 | 191 | For convenience, the answer object implements much of the sequence |
179 | protocol, forwarding to its rrset. E.g. "for a in answer" is | |
180 | equivalent to "for a in answer.rrset", "answer[i]" is equivalent | |
181 | to "answer.rrset[i]", and "answer[i:j]" is equivalent to | |
182 | "answer.rrset[i:j]". | |
192 | protocol, forwarding to its ``rrset`` attribute. E.g. | |
193 | ``for a in answer`` is equivalent to ``for a in answer.rrset``. | |
194 | ``answer[i]`` is equivalent to ``answer.rrset[i]``, and | |
195 | ``answer[i:j]`` is equivalent to ``answer.rrset[i:j]``. | |
183 | 196 | |
184 | 197 | Note that CNAMEs or DNAMEs in the response may mean that answer |
185 | node's name might not be the query name. | |
186 | ||
187 | @ivar qname: The query name | |
188 | @type qname: dns.name.Name object | |
189 | @ivar rdtype: The query type | |
190 | @type rdtype: int | |
191 | @ivar rdclass: The query class | |
192 | @type rdclass: int | |
193 | @ivar response: The response message | |
194 | @type response: dns.message.Message object | |
195 | @ivar rrset: The answer | |
196 | @type rrset: dns.rrset.RRset object | |
197 | @ivar expiration: The time when the answer expires | |
198 | @type expiration: float (seconds since the epoch) | |
199 | @ivar canonical_name: The canonical name of the query name | |
200 | @type canonical_name: dns.name.Name object | |
198 | RRset's name might not be the query name. | |
201 | 199 | """ |
202 | 200 | |
203 | 201 | def __init__(self, qname, rdtype, rdclass, response, |
277 | 275 | return self.rrset and iter(self.rrset) or iter(tuple()) |
278 | 276 | |
279 | 277 | def __getitem__(self, i): |
278 | if self.rrset is None: | |
279 | raise IndexError | |
280 | 280 | return self.rrset[i] |
281 | 281 | |
282 | 282 | def __delitem__(self, i): |
283 | if self.rrset is None: | |
284 | raise IndexError | |
283 | 285 | del self.rrset[i] |
284 | 286 | |
285 | 287 | |
286 | 288 | class Cache(object): |
287 | ||
288 | """Simple DNS answer cache. | |
289 | ||
290 | @ivar data: A dictionary of cached data | |
291 | @type data: dict | |
292 | @ivar cleaning_interval: The number of seconds between cleanings. The | |
293 | default is 300 (5 minutes). | |
294 | @type cleaning_interval: float | |
295 | @ivar next_cleaning: The time the cache should next be cleaned (in seconds | |
296 | since the epoch.) | |
297 | @type next_cleaning: float | |
298 | """ | |
289 | """Simple thread-safe DNS answer cache.""" | |
299 | 290 | |
300 | 291 | def __init__(self, cleaning_interval=300.0): |
301 | """Initialize a DNS cache. | |
302 | ||
303 | @param cleaning_interval: the number of seconds between periodic | |
304 | cleanings. The default is 300.0 | |
305 | @type cleaning_interval: float. | |
292 | """*cleaning_interval*, a ``float`` is the number of seconds between | |
293 | periodic cleanings. | |
306 | 294 | """ |
307 | 295 | |
308 | 296 | self.data = {} |
325 | 313 | self.next_cleaning = now + self.cleaning_interval |
326 | 314 | |
327 | 315 | def get(self, key): |
328 | """Get the answer associated with I{key}. Returns None if | |
329 | no answer is cached for the key. | |
330 | @param key: the key | |
331 | @type key: (dns.name.Name, int, int) tuple whose values are the | |
332 | query name, rdtype, and rdclass. | |
333 | @rtype: dns.resolver.Answer object or None | |
316 | """Get the answer associated with *key*. | |
317 | ||
318 | Returns None if no answer is cached for the key. | |
319 | ||
320 | *key*, a ``(dns.name.Name, int, int)`` tuple whose values are the | |
321 | query name, rdtype, and rdclass respectively. | |
322 | ||
323 | Returns a ``dns.resolver.Answer`` or ``None``. | |
334 | 324 | """ |
335 | 325 | |
336 | 326 | try: |
345 | 335 | |
346 | 336 | def put(self, key, value): |
347 | 337 | """Associate key and value in the cache. |
348 | @param key: the key | |
349 | @type key: (dns.name.Name, int, int) tuple whose values are the | |
350 | query name, rdtype, and rdclass. | |
351 | @param value: The answer being cached | |
352 | @type value: dns.resolver.Answer object | |
338 | ||
339 | *key*, a ``(dns.name.Name, int, int)`` tuple whose values are the | |
340 | query name, rdtype, and rdclass respectively. | |
341 | ||
342 | *value*, a ``dns.resolver.Answer``, the answer. | |
353 | 343 | """ |
354 | 344 | |
355 | 345 | try: |
362 | 352 | def flush(self, key=None): |
363 | 353 | """Flush the cache. |
364 | 354 | |
365 | If I{key} is specified, only that item is flushed. Otherwise | |
355 | If *key* is not ``None``, only that item is flushed. Otherwise | |
366 | 356 | the entire cache is flushed. |
367 | 357 | |
368 | @param key: the key to flush | |
369 | @type key: (dns.name.Name, int, int) tuple or None | |
358 | *key*, a ``(dns.name.Name, int, int)`` tuple whose values are the | |
359 | query name, rdtype, and rdclass respectively. | |
370 | 360 | """ |
371 | 361 | |
372 | 362 | try: |
382 | 372 | |
383 | 373 | |
384 | 374 | class LRUCacheNode(object): |
385 | ||
386 | """LRUCache node. | |
387 | """ | |
375 | """LRUCache node.""" | |
388 | 376 | |
389 | 377 | def __init__(self, key, value): |
390 | 378 | self.key = key |
410 | 398 | |
411 | 399 | |
412 | 400 | class LRUCache(object): |
413 | ||
414 | """Bounded least-recently-used DNS answer cache. | |
401 | """Thread-safe, bounded, least-recently-used DNS answer cache. | |
415 | 402 | |
416 | 403 | This cache is better than the simple cache (above) if you're |
417 | 404 | running a web crawler or other process that does a lot of |
418 | 405 | resolutions. The LRUCache has a maximum number of nodes, and when |
419 | 406 | it is full, the least-recently used node is removed to make space |
420 | 407 | for a new one. |
421 | ||
422 | @ivar data: A dictionary of cached data | |
423 | @type data: dict | |
424 | @ivar sentinel: sentinel node for circular doubly linked list of nodes | |
425 | @type sentinel: LRUCacheNode object | |
426 | @ivar max_size: The maximum number of nodes | |
427 | @type max_size: int | |
428 | 408 | """ |
429 | 409 | |
430 | 410 | def __init__(self, max_size=100000): |
431 | """Initialize a DNS cache. | |
432 | ||
433 | @param max_size: The maximum number of nodes to cache; the default is | |
434 | 100,000. Must be greater than 1. | |
435 | @type max_size: int | |
436 | """ | |
411 | """*max_size*, an ``int``, is the maximum number of nodes to cache; | |
412 | it must be greater than 0. | |
413 | """ | |
414 | ||
437 | 415 | self.data = {} |
438 | 416 | self.set_max_size(max_size) |
439 | 417 | self.sentinel = LRUCacheNode(None, None) |
445 | 423 | self.max_size = max_size |
446 | 424 | |
447 | 425 | def get(self, key): |
448 | """Get the answer associated with I{key}. Returns None if | |
449 | no answer is cached for the key. | |
450 | @param key: the key | |
451 | @type key: (dns.name.Name, int, int) tuple whose values are the | |
452 | query name, rdtype, and rdclass. | |
453 | @rtype: dns.resolver.Answer object or None | |
454 | """ | |
426 | """Get the answer associated with *key*. | |
427 | ||
428 | Returns None if no answer is cached for the key. | |
429 | ||
430 | *key*, a ``(dns.name.Name, int, int)`` tuple whose values are the | |
431 | query name, rdtype, and rdclass respectively. | |
432 | ||
433 | Returns a ``dns.resolver.Answer`` or ``None``. | |
434 | """ | |
435 | ||
455 | 436 | try: |
456 | 437 | self.lock.acquire() |
457 | 438 | node = self.data.get(key) |
470 | 451 | |
471 | 452 | def put(self, key, value): |
472 | 453 | """Associate key and value in the cache. |
473 | @param key: the key | |
474 | @type key: (dns.name.Name, int, int) tuple whose values are the | |
475 | query name, rdtype, and rdclass. | |
476 | @param value: The answer being cached | |
477 | @type value: dns.resolver.Answer object | |
478 | """ | |
454 | ||
455 | *key*, a ``(dns.name.Name, int, int)`` tuple whose values are the | |
456 | query name, rdtype, and rdclass respectively. | |
457 | ||
458 | *value*, a ``dns.resolver.Answer``, the answer. | |
459 | """ | |
460 | ||
479 | 461 | try: |
480 | 462 | self.lock.acquire() |
481 | 463 | node = self.data.get(key) |
495 | 477 | def flush(self, key=None): |
496 | 478 | """Flush the cache. |
497 | 479 | |
498 | If I{key} is specified, only that item is flushed. Otherwise | |
480 | If *key* is not ``None``, only that item is flushed. Otherwise | |
499 | 481 | the entire cache is flushed. |
500 | 482 | |
501 | @param key: the key to flush | |
502 | @type key: (dns.name.Name, int, int) tuple or None | |
503 | """ | |
483 | *key*, a ``(dns.name.Name, int, int)`` tuple whose values are the | |
484 | query name, rdtype, and rdclass respectively. | |
485 | """ | |
486 | ||
504 | 487 | try: |
505 | 488 | self.lock.acquire() |
506 | 489 | if key is not None: |
521 | 504 | |
522 | 505 | |
523 | 506 | class Resolver(object): |
524 | ||
525 | """DNS stub resolver | |
526 | ||
527 | @ivar domain: The domain of this host | |
528 | @type domain: dns.name.Name object | |
529 | @ivar nameservers: A list of nameservers to query. Each nameserver is | |
530 | a string which contains the IP address of a nameserver. | |
531 | @type nameservers: list of strings | |
532 | @ivar search: The search list. If the query name is a relative name, | |
533 | the resolver will construct an absolute query name by appending the search | |
534 | names one by one to the query name. | |
535 | @type search: list of dns.name.Name objects | |
536 | @ivar port: The port to which to send queries. The default is 53. | |
537 | @type port: int | |
538 | @ivar timeout: The number of seconds to wait for a response from a | |
539 | server, before timing out. | |
540 | @type timeout: float | |
541 | @ivar lifetime: The total number of seconds to spend trying to get an | |
542 | answer to the question. If the lifetime expires, a Timeout exception | |
543 | will occur. | |
544 | @type lifetime: float | |
545 | @ivar keyring: The TSIG keyring to use. The default is None. | |
546 | @type keyring: dict | |
547 | @ivar keyname: The TSIG keyname to use. The default is None. | |
548 | @type keyname: dns.name.Name object | |
549 | @ivar keyalgorithm: The TSIG key algorithm to use. The default is | |
550 | dns.tsig.default_algorithm. | |
551 | @type keyalgorithm: string | |
552 | @ivar edns: The EDNS level to use. The default is -1, no Edns. | |
553 | @type edns: int | |
554 | @ivar ednsflags: The EDNS flags | |
555 | @type ednsflags: int | |
556 | @ivar payload: The EDNS payload size. The default is 0. | |
557 | @type payload: int | |
558 | @ivar flags: The message flags to use. The default is None (i.e. not | |
559 | overwritten) | |
560 | @type flags: int | |
561 | @ivar cache: The cache to use. The default is None. | |
562 | @type cache: dns.resolver.Cache object | |
563 | @ivar retry_servfail: should we retry a nameserver if it says SERVFAIL? | |
564 | The default is 'false'. | |
565 | @type retry_servfail: bool | |
566 | """ | |
507 | """DNS stub resolver.""" | |
567 | 508 | |
568 | 509 | def __init__(self, filename='/etc/resolv.conf', configure=True): |
569 | """Initialize a resolver instance. | |
570 | ||
571 | @param filename: The filename of a configuration file in | |
572 | standard /etc/resolv.conf format. This parameter is meaningful | |
573 | only when I{configure} is true and the platform is POSIX. | |
574 | @type filename: string or file object | |
575 | @param configure: If True (the default), the resolver instance | |
576 | is configured in the normal fashion for the operating system | |
577 | the resolver is running on. (I.e. a /etc/resolv.conf file on | |
578 | POSIX systems and from the registry on Windows systems.) | |
579 | @type configure: bool""" | |
510 | """*filename*, a ``text`` or file object, specifying a file | |
511 | in standard /etc/resolv.conf format. This parameter is meaningful | |
512 | only when *configure* is true and the platform is POSIX. | |
513 | ||
514 | *configure*, a ``bool``. If True (the default), the resolver | |
515 | instance is configured in the normal fashion for the operating | |
516 | system the resolver is running on. (I.e. by reading a | |
517 | /etc/resolv.conf file on POSIX systems and from the registry | |
518 | on Windows systems.) | |
519 | """ | |
580 | 520 | |
581 | 521 | self.domain = None |
582 | 522 | self.nameservers = None |
605 | 545 | |
606 | 546 | def reset(self): |
607 | 547 | """Reset all resolver configuration to the defaults.""" |
548 | ||
608 | 549 | self.domain = \ |
609 | 550 | dns.name.Name(dns.name.from_text(socket.gethostname())[1:]) |
610 | 551 | if len(self.domain) == 0: |
627 | 568 | self.rotate = False |
628 | 569 | |
629 | 570 | def read_resolv_conf(self, f): |
630 | """Process f as a file in the /etc/resolv.conf format. If f is | |
631 | a string, it is used as the name of the file to open; otherwise it | |
571 | """Process *f* as a file in the /etc/resolv.conf format. If f is | |
572 | a ``text``, it is used as the name of the file to open; otherwise it | |
632 | 573 | is treated as the file itself.""" |
574 | ||
633 | 575 | if isinstance(f, string_types): |
634 | 576 | try: |
635 | 577 | f = open(f, 'r') |
683 | 625 | return split_char |
684 | 626 | |
685 | 627 | def _config_win32_nameservers(self, nameservers): |
686 | """Configure a NameServer registry entry.""" | |
687 | 628 | # we call str() on nameservers to convert it from unicode to ascii |
688 | 629 | nameservers = str(nameservers) |
689 | 630 | split_char = self._determine_split_char(nameservers) |
693 | 634 | self.nameservers.append(ns) |
694 | 635 | |
695 | 636 | def _config_win32_domain(self, domain): |
696 | """Configure a Domain registry entry.""" | |
697 | 637 | # we call str() on domain to convert it from unicode to ascii |
698 | 638 | self.domain = dns.name.from_text(str(domain)) |
699 | 639 | |
700 | 640 | def _config_win32_search(self, search): |
701 | """Configure a Search registry entry.""" | |
702 | 641 | # we call str() on search to convert it from unicode to ascii |
703 | 642 | search = str(search) |
704 | 643 | split_char = self._determine_split_char(search) |
707 | 646 | if s not in self.search: |
708 | 647 | self.search.append(dns.name.from_text(s)) |
709 | 648 | |
710 | def _config_win32_fromkey(self, key): | |
711 | """Extract DNS info from a registry key.""" | |
649 | def _config_win32_fromkey(self, key, always_try_domain): | |
712 | 650 | try: |
713 | 651 | servers, rtype = _winreg.QueryValueEx(key, 'NameServer') |
714 | 652 | except WindowsError: # pylint: disable=undefined-variable |
715 | 653 | servers = None |
716 | 654 | if servers: |
717 | 655 | self._config_win32_nameservers(servers) |
656 | if servers or always_try_domain: | |
718 | 657 | try: |
719 | 658 | dom, rtype = _winreg.QueryValueEx(key, 'Domain') |
720 | 659 | if dom: |
743 | 682 | |
744 | 683 | def read_registry(self): |
745 | 684 | """Extract resolver configuration from the Windows registry.""" |
685 | ||
746 | 686 | lm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) |
747 | 687 | want_scan = False |
748 | 688 | try: |
758 | 698 | r'SYSTEM\CurrentControlSet' |
759 | 699 | r'\Services\VxD\MSTCP') |
760 | 700 | try: |
761 | self._config_win32_fromkey(tcp_params) | |
701 | self._config_win32_fromkey(tcp_params, True) | |
762 | 702 | finally: |
763 | 703 | tcp_params.Close() |
764 | 704 | if want_scan: |
776 | 716 | if not self._win32_is_nic_enabled(lm, guid, key): |
777 | 717 | continue |
778 | 718 | try: |
779 | self._config_win32_fromkey(key) | |
719 | self._config_win32_fromkey(key, False) | |
780 | 720 | finally: |
781 | 721 | key.Close() |
782 | 722 | except EnvironmentError: |
841 | 781 | except WindowsError: # pylint: disable=undefined-variable |
842 | 782 | return False |
843 | 783 | |
844 | def _compute_timeout(self, start): | |
784 | def _compute_timeout(self, start, lifetime=None): | |
785 | lifetime = self.lifetime if lifetime is None else lifetime | |
845 | 786 | now = time.time() |
846 | 787 | duration = now - start |
847 | 788 | if duration < 0: |
853 | 794 | # happen, e.g. under vmware with older linux kernels. |
854 | 795 | # Pretend it didn't happen. |
855 | 796 | now = start |
856 | if duration >= self.lifetime: | |
797 | if duration >= lifetime: | |
857 | 798 | raise Timeout(timeout=duration) |
858 | return min(self.lifetime - duration, self.timeout) | |
799 | return min(lifetime - duration, self.timeout) | |
859 | 800 | |
860 | 801 | def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, |
861 | tcp=False, source=None, raise_on_no_answer=True, source_port=0): | |
802 | tcp=False, source=None, raise_on_no_answer=True, source_port=0, | |
803 | lifetime=None): | |
862 | 804 | """Query nameservers to find the answer to the question. |
863 | 805 | |
864 | The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects | |
806 | The *qname*, *rdtype*, and *rdclass* parameters may be objects | |
865 | 807 | of the appropriate type, or strings that can be converted into objects |
866 | of the appropriate type. E.g. For I{rdtype} the integer 2 and the | |
867 | the string 'NS' both mean to query for records with DNS rdata type NS. | |
868 | ||
869 | @param qname: the query name | |
870 | @type qname: dns.name.Name object or string | |
871 | @param rdtype: the query type | |
872 | @type rdtype: int or string | |
873 | @param rdclass: the query class | |
874 | @type rdclass: int or string | |
875 | @param tcp: use TCP to make the query (default is False). | |
876 | @type tcp: bool | |
877 | @param source: bind to this IP address (defaults to machine default | |
878 | IP). | |
879 | @type source: IP address in dotted quad notation | |
880 | @param raise_on_no_answer: raise NoAnswer if there's no answer | |
881 | (defaults is True). | |
882 | @type raise_on_no_answer: bool | |
883 | @param source_port: The port from which to send the message. | |
884 | The default is 0. | |
885 | @type source_port: int | |
886 | @rtype: dns.resolver.Answer instance | |
887 | @raises Timeout: no answers could be found in the specified lifetime | |
888 | @raises NXDOMAIN: the query name does not exist | |
889 | @raises YXDOMAIN: the query name is too long after DNAME substitution | |
890 | @raises NoAnswer: the response did not contain an answer and | |
891 | raise_on_no_answer is True. | |
892 | @raises NoNameservers: no non-broken nameservers are available to | |
893 | answer the question.""" | |
808 | of the appropriate type. | |
809 | ||
810 | *qname*, a ``dns.name.Name`` or ``text``, the query name. | |
811 | ||
812 | *rdtype*, an ``int`` or ``text``, the query type. | |
813 | ||
814 | *rdclass*, an ``int`` or ``text``, the query class. | |
815 | ||
816 | *tcp*, a ``bool``. If ``True``, use TCP to make the query. | |
817 | ||
818 | *source*, a ``text`` or ``None``. If not ``None``, bind to this IP | |
819 | address when making queries. | |
820 | ||
821 | *raise_on_no_answer*, a ``bool``. If ``True``, raise | |
822 | ``dns.resolver.NoAnswer`` if there's no answer to the question. | |
823 | ||
824 | *source_port*, an ``int``, the port from which to send the message. | |
825 | ||
826 | *lifetime*, a ``float``, how long query should run before timing out. | |
827 | ||
828 | Raises ``dns.exception.Timeout`` if no answers could be found | |
829 | in the specified lifetime. | |
830 | ||
831 | Raises ``dns.resolver.NXDOMAIN`` if the query name does not exist. | |
832 | ||
833 | Raises ``dns.resolver.YXDOMAIN`` if the query name is too long after | |
834 | DNAME substitution. | |
835 | ||
836 | Raises ``dns.resolver.NoAnswer`` if *raise_on_no_answer* is | |
837 | ``True`` and the query name exists but has no RRset of the | |
838 | desired type and class. | |
839 | ||
840 | Raises ``dns.resolver.NoNameservers`` if no non-broken | |
841 | nameservers are available to answer the question. | |
842 | ||
843 | Returns a ``dns.resolver.Answer`` instance. | |
844 | """ | |
894 | 845 | |
895 | 846 | if isinstance(qname, string_types): |
896 | 847 | qname = dns.name.from_text(qname, None) |
945 | 896 | if len(nameservers) == 0: |
946 | 897 | raise NoNameservers(request=request, errors=errors) |
947 | 898 | for nameserver in nameservers[:]: |
948 | timeout = self._compute_timeout(start) | |
899 | timeout = self._compute_timeout(start, lifetime) | |
949 | 900 | port = self.nameserver_ports.get(nameserver, self.port) |
950 | 901 | try: |
951 | 902 | tcp_attempt = tcp |
962 | 913 | if response.flags & dns.flags.TC: |
963 | 914 | # Response truncated; retry with TCP. |
964 | 915 | tcp_attempt = True |
965 | timeout = self._compute_timeout(start) | |
916 | timeout = self._compute_timeout(start, lifetime) | |
966 | 917 | response = \ |
967 | 918 | dns.query.tcp(request, nameserver, |
968 | 919 | timeout, port, |
1037 | 988 | # But we still have servers to try. Sleep a bit |
1038 | 989 | # so we don't pound them! |
1039 | 990 | # |
1040 | timeout = self._compute_timeout(start) | |
991 | timeout = self._compute_timeout(start, lifetime) | |
1041 | 992 | sleep_time = min(timeout, backoff) |
1042 | 993 | backoff *= 2 |
1043 | 994 | time.sleep(sleep_time) |
1058 | 1009 | algorithm=dns.tsig.default_algorithm): |
1059 | 1010 | """Add a TSIG signature to the query. |
1060 | 1011 | |
1061 | @param keyring: The TSIG keyring to use; defaults to None. | |
1062 | @type keyring: dict | |
1063 | @param keyname: The name of the TSIG key to use; defaults to None. | |
1064 | The key must be defined in the keyring. If a keyring is specified | |
1065 | but a keyname is not, then the key used will be the first key in the | |
1066 | keyring. Note that the order of keys in a dictionary is not defined, | |
1067 | so applications should supply a keyname when a keyring is used, unless | |
1068 | they know the keyring contains only one key. | |
1069 | @param algorithm: The TSIG key algorithm to use. The default | |
1070 | is dns.tsig.default_algorithm. | |
1071 | @type algorithm: string""" | |
1012 | See the documentation of the Message class for a complete | |
1013 | description of the keyring dictionary. | |
1014 | ||
1015 | *keyring*, a ``dict``, the TSIG keyring to use. If a | |
1016 | *keyring* is specified but a *keyname* is not, then the key | |
1017 | used will be the first key in the *keyring*. Note that the | |
1018 | order of keys in a dictionary is not defined, so applications | |
1019 | should supply a keyname when a keyring is used, unless they | |
1020 | know the keyring contains only one key. | |
1021 | ||
1022 | *keyname*, a ``dns.name.Name`` or ``None``, the name of the TSIG key | |
1023 | to use; defaults to ``None``. The key must be defined in the keyring. | |
1024 | ||
1025 | *algorithm*, a ``dns.name.Name``, the TSIG algorithm to use. | |
1026 | """ | |
1027 | ||
1072 | 1028 | self.keyring = keyring |
1073 | 1029 | if keyname is None: |
1074 | 1030 | self.keyname = list(self.keyring.keys())[0] |
1077 | 1033 | self.keyalgorithm = algorithm |
1078 | 1034 | |
1079 | 1035 | def use_edns(self, edns, ednsflags, payload): |
1080 | """Configure Edns. | |
1081 | ||
1082 | @param edns: The EDNS level to use. The default is -1, no Edns. | |
1083 | @type edns: int | |
1084 | @param ednsflags: The EDNS flags | |
1085 | @type ednsflags: int | |
1086 | @param payload: The EDNS payload size. The default is 0. | |
1087 | @type payload: int""" | |
1036 | """Configure EDNS behavior. | |
1037 | ||
1038 | *edns*, an ``int``, is the EDNS level to use. Specifying | |
1039 | ``None``, ``False``, or ``-1`` means "do not use EDNS", and in this case | |
1040 | the other parameters are ignored. Specifying ``True`` is | |
1041 | equivalent to specifying 0, i.e. "use EDNS0". | |
1042 | ||
1043 | *ednsflags*, an ``int``, the EDNS flag values. | |
1044 | ||
1045 | *payload*, an ``int``, is the EDNS sender's payload field, which is the | |
1046 | maximum size of UDP datagram the sender can handle. I.e. how big | |
1047 | a response to this message can be. | |
1048 | """ | |
1088 | 1049 | |
1089 | 1050 | if edns is None: |
1090 | 1051 | edns = -1 |
1093 | 1054 | self.payload = payload |
1094 | 1055 | |
1095 | 1056 | def set_flags(self, flags): |
1096 | """Overrides the default flags with your own | |
1097 | ||
1098 | @param flags: The flags to overwrite the default with | |
1099 | @type flags: int""" | |
1057 | """Overrides the default flags with your own. | |
1058 | ||
1059 | *flags*, an ``int``, the message flags to use. | |
1060 | """ | |
1061 | ||
1100 | 1062 | self.flags = flags |
1101 | 1063 | |
1064 | ||
1065 | #: The default resolver. | |
1102 | 1066 | default_resolver = None |
1103 | 1067 | |
1104 | 1068 | |
1112 | 1076 | def reset_default_resolver(): |
1113 | 1077 | """Re-initialize default resolver. |
1114 | 1078 | |
1115 | resolv.conf will be re-read immediatelly. | |
1079 | Note that the resolver configuration (i.e. /etc/resolv.conf on UNIX | |
1080 | systems) will be re-read immediately. | |
1116 | 1081 | """ |
1082 | ||
1117 | 1083 | global default_resolver |
1118 | 1084 | default_resolver = Resolver() |
1119 | 1085 | |
1120 | 1086 | |
1121 | 1087 | def query(qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, |
1122 | 1088 | tcp=False, source=None, raise_on_no_answer=True, |
1123 | source_port=0): | |
1089 | source_port=0, lifetime=None): | |
1124 | 1090 | """Query nameservers to find the answer to the question. |
1125 | 1091 | |
1126 | 1092 | This is a convenience function that uses the default resolver |
1127 | 1093 | object to make the query. |
1128 | @see: L{dns.resolver.Resolver.query} for more information on the | |
1129 | parameters.""" | |
1094 | ||
1095 | See ``dns.resolver.Resolver.query`` for more information on the | |
1096 | parameters. | |
1097 | """ | |
1098 | ||
1130 | 1099 | return get_default_resolver().query(qname, rdtype, rdclass, tcp, source, |
1131 | raise_on_no_answer, source_port) | |
1100 | raise_on_no_answer, source_port, | |
1101 | lifetime) | |
1132 | 1102 | |
1133 | 1103 | |
1134 | 1104 | def zone_for_name(name, rdclass=dns.rdataclass.IN, tcp=False, resolver=None): |
1135 | 1105 | """Find the name of the zone which contains the specified name. |
1136 | 1106 | |
1137 | @param name: the query name | |
1138 | @type name: absolute dns.name.Name object or string | |
1139 | @param rdclass: The query class | |
1140 | @type rdclass: int | |
1141 | @param tcp: use TCP to make the query (default is False). | |
1142 | @type tcp: bool | |
1143 | @param resolver: the resolver to use | |
1144 | @type resolver: dns.resolver.Resolver object or None | |
1145 | @rtype: dns.name.Name""" | |
1107 | *name*, an absolute ``dns.name.Name`` or ``text``, the query name. | |
1108 | ||
1109 | *rdclass*, an ``int``, the query class. | |
1110 | ||
1111 | *tcp*, a ``bool``. If ``True``, use TCP to make the query. | |
1112 | ||
1113 | *resolver*, a ``dns.resolver.Resolver`` or ``None``, the resolver to use. | |
1114 | If ``None``, the default resolver is used. | |
1115 | ||
1116 | Raises ``dns.resolver.NoRootSOA`` if there is no SOA RR at the DNS | |
1117 | root. (This is only likely to happen if you're using non-default | |
1118 | root servers in your network and they are misconfigured.) | |
1119 | ||
1120 | Returns a ``dns.name.Name``. | |
1121 | """ | |
1146 | 1122 | |
1147 | 1123 | if isinstance(name, string_types): |
1148 | 1124 | name = dns.name.from_text(name, dns.name.root) |
1239 | 1215 | v4addrs.append(rdata.address) |
1240 | 1216 | except dns.resolver.NXDOMAIN: |
1241 | 1217 | raise socket.gaierror(socket.EAI_NONAME) |
1242 | except: | |
1218 | except Exception: | |
1243 | 1219 | raise socket.gaierror(socket.EAI_SYSTEM) |
1244 | 1220 | port = None |
1245 | 1221 | try: |
1378 | 1354 | The resolver to use may be specified; if it's not, the default |
1379 | 1355 | resolver will be used. |
1380 | 1356 | |
1381 | @param resolver: the resolver to use | |
1382 | @type resolver: dns.resolver.Resolver object or None | |
1357 | resolver, a ``dns.resolver.Resolver`` or ``None``, the resolver to use. | |
1383 | 1358 | """ |
1359 | ||
1384 | 1360 | if resolver is None: |
1385 | 1361 | resolver = get_default_resolver() |
1386 | 1362 | global _resolver |
1394 | 1370 | |
1395 | 1371 | |
1396 | 1372 | def restore_system_resolver(): |
1397 | """Undo the effects of override_system_resolver(). | |
1398 | """ | |
1373 | """Undo the effects of prior override_system_resolver().""" | |
1374 | ||
1399 | 1375 | global _resolver |
1400 | 1376 | _resolver = None |
1401 | 1377 | socket.getaddrinfo = _original_getaddrinfo |
0 | from typing import Union, Optional, List | |
1 | from . import exception, rdataclass, name, rdatatype | |
2 | ||
3 | import socket | |
4 | _gethostbyname = socket.gethostbyname | |
5 | class NXDOMAIN(exception.DNSException): | |
6 | ... | |
7 | def query(qname : str, rdtype : Union[int,str] = 0, rdclass : Union[int,str] = 0, | |
8 | tcp=False, source=None, raise_on_no_answer=True, | |
9 | source_port=0): | |
10 | ... | |
11 | class LRUCache: | |
12 | def __init__(self, max_size=1000): | |
13 | ... | |
14 | def get(self, key): | |
15 | ... | |
16 | def put(self, key, val): | |
17 | ... | |
18 | class Answer: | |
19 | def __init__(self, qname, rdtype, rdclass, response, | |
20 | raise_on_no_answer=True): | |
21 | ... | |
22 | def zone_for_name(name, rdclass : int = rdataclass.IN, tcp=False, resolver : Optional[Resolver] = None): | |
23 | ... | |
24 | ||
25 | class Resolver: | |
26 | def __init__(self, configure): | |
27 | self.nameservers : List[str] | |
28 | def query(self, qname : str, rdtype : Union[int,str] = rdatatype.A, rdclass : Union[int,str] = rdataclass.IN, | |
29 | tcp : bool = False, source : Optional[str] = None, raise_on_no_answer=True, source_port : int = 0): | |
30 | ... |
0 | # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2006-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | """DNS Reverse Map Names. | |
16 | ||
17 | @var ipv4_reverse_domain: The DNS IPv4 reverse-map domain, in-addr.arpa. | |
18 | @type ipv4_reverse_domain: dns.name.Name object | |
19 | @var ipv6_reverse_domain: The DNS IPv6 reverse-map domain, ip6.arpa. | |
20 | @type ipv6_reverse_domain: dns.name.Name object | |
21 | """ | |
17 | """DNS Reverse Map Names.""" | |
22 | 18 | |
23 | 19 | import binascii |
24 | import sys | |
25 | 20 | |
26 | 21 | import dns.name |
27 | 22 | import dns.ipv6 |
28 | 23 | import dns.ipv4 |
24 | ||
25 | from dns._compat import PY3 | |
29 | 26 | |
30 | 27 | ipv4_reverse_domain = dns.name.from_text('in-addr.arpa.') |
31 | 28 | ipv6_reverse_domain = dns.name.from_text('ip6.arpa.') |
34 | 31 | def from_address(text): |
35 | 32 | """Convert an IPv4 or IPv6 address in textual form into a Name object whose |
36 | 33 | value is the reverse-map domain name of the address. |
37 | @param text: an IPv4 or IPv6 address in textual form (e.g. '127.0.0.1', | |
38 | '::1') | |
39 | @type text: str | |
40 | @rtype: dns.name.Name object | |
34 | ||
35 | *text*, a ``text``, is an IPv4 or IPv6 address in textual form | |
36 | (e.g. '127.0.0.1', '::1') | |
37 | ||
38 | Raises ``dns.exception.SyntaxError`` if the address is badly formed. | |
39 | ||
40 | Returns a ``dns.name.Name``. | |
41 | 41 | """ |
42 | ||
42 | 43 | try: |
43 | 44 | v6 = dns.ipv6.inet_aton(text) |
44 | 45 | if dns.ipv6.is_mapped(v6): |
45 | if sys.version_info >= (3,): | |
46 | if PY3: | |
46 | 47 | parts = ['%d' % byte for byte in v6[12:]] |
47 | 48 | else: |
48 | 49 | parts = ['%d' % ord(byte) for byte in v6[12:]] |
60 | 61 | |
61 | 62 | def to_address(name): |
62 | 63 | """Convert a reverse map domain name into textual address form. |
63 | @param name: an IPv4 or IPv6 address in reverse-map form. | |
64 | @type name: dns.name.Name object | |
65 | @rtype: str | |
64 | ||
65 | *name*, a ``dns.name.Name``, an IPv4 or IPv6 address in reverse-map name | |
66 | form. | |
67 | ||
68 | Raises ``dns.exception.SyntaxError`` if the name does not have a | |
69 | reverse-map form. | |
70 | ||
71 | Returns a ``text``. | |
66 | 72 | """ |
73 | ||
67 | 74 | if name.is_subdomain(ipv4_reverse_domain): |
68 | 75 | name = name.relativize(ipv4_reverse_domain) |
69 | 76 | labels = list(name.labels) |
0 | from . import name | |
1 | def from_address(text : str) -> name.Name: | |
2 | ... | |
3 | ||
4 | def to_address(name : name.Name) -> str: | |
5 | ... |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
66 | 68 | return self.to_text() |
67 | 69 | |
68 | 70 | def __eq__(self, other): |
69 | """Two RRsets are equal if they have the same name and the same | |
70 | rdataset | |
71 | ||
72 | @rtype: bool""" | |
73 | 71 | if not isinstance(other, RRset): |
74 | 72 | return False |
75 | 73 | if self.name != other.name: |
77 | 75 | return super(RRset, self).__eq__(other) |
78 | 76 | |
79 | 77 | def match(self, name, rdclass, rdtype, covers, deleting=None): |
80 | """Returns True if this rrset matches the specified class, type, | |
81 | covers, and deletion state.""" | |
78 | """Returns ``True`` if this rrset matches the specified class, type, | |
79 | covers, and deletion state. | |
80 | """ | |
82 | 81 | |
83 | 82 | if not super(RRset, self).match(rdclass, rdtype, covers): |
84 | 83 | return False |
89 | 88 | def to_text(self, origin=None, relativize=True, **kw): |
90 | 89 | """Convert the RRset into DNS master file format. |
91 | 90 | |
92 | @see: L{dns.name.Name.choose_relativity} for more information | |
93 | on how I{origin} and I{relativize} determine the way names | |
91 | See ``dns.name.Name.choose_relativity`` for more information | |
92 | on how *origin* and *relativize* determine the way names | |
94 | 93 | are emitted. |
95 | 94 | |
96 | 95 | Any additional keyword arguments are passed on to the rdata |
97 | to_text() method. | |
96 | ``to_text()`` method. | |
98 | 97 | |
99 | @param origin: The origin for relative names, or None. | |
100 | @type origin: dns.name.Name object | |
101 | @param relativize: True if names should names be relativized | |
102 | @type relativize: bool""" | |
98 | *origin*, a ``dns.name.Name`` or ``None``, the origin for relative | |
99 | names. | |
100 | ||
101 | *relativize*, a ``bool``. If ``True``, names will be relativized | |
102 | to *origin*. | |
103 | """ | |
103 | 104 | |
104 | 105 | return super(RRset, self).to_text(self.name, origin, relativize, |
105 | 106 | self.deleting, **kw) |
106 | 107 | |
107 | 108 | def to_wire(self, file, compress=None, origin=None, **kw): |
108 | """Convert the RRset to wire format.""" | |
109 | """Convert the RRset to wire format. | |
110 | ||
111 | All keyword arguments are passed to ``dns.rdataset.to_wire()``; see | |
112 | that function for details. | |
113 | ||
114 | Returns an ``int``, the number of records emitted. | |
115 | """ | |
109 | 116 | |
110 | 117 | return super(RRset, self).to_wire(self.name, file, compress, origin, |
111 | 118 | self.deleting, **kw) |
113 | 120 | def to_rdataset(self): |
114 | 121 | """Convert an RRset into an Rdataset. |
115 | 122 | |
116 | @rtype: dns.rdataset.Rdataset object | |
123 | Returns a ``dns.rdataset.Rdataset``. | |
117 | 124 | """ |
118 | 125 | return dns.rdataset.from_rdata_list(self.ttl, list(self)) |
119 | 126 | |
123 | 130 | """Create an RRset with the specified name, TTL, class, and type, and with |
124 | 131 | the specified list of rdatas in text format. |
125 | 132 | |
126 | @rtype: dns.rrset.RRset object | |
133 | Returns a ``dns.rrset.RRset`` object. | |
127 | 134 | """ |
128 | 135 | |
129 | 136 | if isinstance(name, string_types): |
144 | 151 | """Create an RRset with the specified name, TTL, class, and type and with |
145 | 152 | the specified rdatas in text format. |
146 | 153 | |
147 | @rtype: dns.rrset.RRset object | |
154 | Returns a ``dns.rrset.RRset`` object. | |
148 | 155 | """ |
149 | 156 | |
150 | 157 | return from_text_list(name, ttl, rdclass, rdtype, text_rdatas) |
154 | 161 | """Create an RRset with the specified name and TTL, and with |
155 | 162 | the specified list of rdata objects. |
156 | 163 | |
157 | @rtype: dns.rrset.RRset object | |
164 | Returns a ``dns.rrset.RRset`` object. | |
158 | 165 | """ |
159 | 166 | |
160 | 167 | if isinstance(name, string_types): |
175 | 182 | """Create an RRset with the specified name and TTL, and with |
176 | 183 | the specified rdata objects. |
177 | 184 | |
178 | @rtype: dns.rrset.RRset object | |
185 | Returns a ``dns.rrset.RRset`` object. | |
179 | 186 | """ |
180 | 187 | |
181 | 188 | return from_rdata_list(name, ttl, rdatas) |
0 | from typing import List, Optional | |
1 | from . import rdataset, rdatatype | |
2 | ||
3 | class RRset(rdataset.Rdataset): | |
4 | def __init__(self, name, rdclass : int , rdtype : int, covers=rdatatype.NONE, | |
5 | deleting : Optional[int] =None) -> None: | |
6 | self.name = name | |
7 | self.deleting = deleting | |
8 | def from_text(name : str, ttl : int, rdclass : str, rdtype : str, *text_rdatas : str): | |
9 | ... |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | """A simple Set class.""" | |
16 | ||
17 | ||
18 | 17 | class Set(object): |
19 | 18 | |
20 | 19 | """A simple set class. |
21 | 20 | |
22 | Sets are not in Python until 2.3, and rdata are not immutable so | |
23 | we cannot use sets.Set anyway. This class implements subset of | |
24 | the 2.3 Set interface using a list as the container. | |
25 | ||
26 | @ivar items: A list of the items which are in the set | |
27 | @type items: list""" | |
21 | This class was originally used to deal with sets being missing in | |
22 | ancient versions of python, but dnspython will continue to use it | |
23 | as these sets are based on lists and are thus indexable, and this | |
24 | ability is widely used in dnspython applications. | |
25 | """ | |
28 | 26 | |
29 | 27 | __slots__ = ['items'] |
30 | 28 | |
31 | 29 | def __init__(self, items=None): |
32 | 30 | """Initialize the set. |
33 | 31 | |
34 | @param items: the initial set of items | |
35 | @type items: any iterable or None | |
32 | *items*, an iterable or ``None``, the initial set of items. | |
36 | 33 | """ |
37 | 34 | |
38 | 35 | self.items = [] |
44 | 41 | return "dns.simpleset.Set(%s)" % repr(self.items) |
45 | 42 | |
46 | 43 | def add(self, item): |
47 | """Add an item to the set.""" | |
44 | """Add an item to the set. | |
45 | """ | |
46 | ||
48 | 47 | if item not in self.items: |
49 | 48 | self.items.append(item) |
50 | 49 | |
51 | 50 | def remove(self, item): |
52 | """Remove an item from the set.""" | |
51 | """Remove an item from the set. | |
52 | """ | |
53 | ||
53 | 54 | self.items.remove(item) |
54 | 55 | |
55 | 56 | def discard(self, item): |
56 | """Remove an item from the set if present.""" | |
57 | """Remove an item from the set if present. | |
58 | """ | |
59 | ||
57 | 60 | try: |
58 | 61 | self.items.remove(item) |
59 | 62 | except ValueError: |
78 | 81 | return obj |
79 | 82 | |
80 | 83 | def __copy__(self): |
81 | """Make a (shallow) copy of the set.""" | |
84 | """Make a (shallow) copy of the set. | |
85 | """ | |
86 | ||
82 | 87 | return self._clone() |
83 | 88 | |
84 | 89 | def copy(self): |
85 | """Make a (shallow) copy of the set.""" | |
90 | """Make a (shallow) copy of the set. | |
91 | """ | |
92 | ||
86 | 93 | return self._clone() |
87 | 94 | |
88 | 95 | def union_update(self, other): |
89 | 96 | """Update the set, adding any elements from other which are not |
90 | 97 | already in the set. |
91 | @param other: the collection of items with which to update the set | |
92 | @type other: Set object | |
93 | """ | |
98 | """ | |
99 | ||
94 | 100 | if not isinstance(other, Set): |
95 | 101 | raise ValueError('other must be a Set instance') |
96 | 102 | if self is other: |
101 | 107 | def intersection_update(self, other): |
102 | 108 | """Update the set, removing any elements from other which are not |
103 | 109 | in both sets. |
104 | @param other: the collection of items with which to update the set | |
105 | @type other: Set object | |
106 | """ | |
110 | """ | |
111 | ||
107 | 112 | if not isinstance(other, Set): |
108 | 113 | raise ValueError('other must be a Set instance') |
109 | 114 | if self is other: |
117 | 122 | def difference_update(self, other): |
118 | 123 | """Update the set, removing any elements from other which are in |
119 | 124 | the set. |
120 | @param other: the collection of items with which to update the set | |
121 | @type other: Set object | |
122 | """ | |
125 | """ | |
126 | ||
123 | 127 | if not isinstance(other, Set): |
124 | 128 | raise ValueError('other must be a Set instance') |
125 | 129 | if self is other: |
129 | 133 | self.discard(item) |
130 | 134 | |
131 | 135 | def union(self, other): |
132 | """Return a new set which is the union of I{self} and I{other}. | |
133 | ||
134 | @param other: the other set | |
135 | @type other: Set object | |
136 | @rtype: the same type as I{self} | |
136 | """Return a new set which is the union of ``self`` and ``other``. | |
137 | ||
138 | Returns the same Set type as this set. | |
137 | 139 | """ |
138 | 140 | |
139 | 141 | obj = self._clone() |
141 | 143 | return obj |
142 | 144 | |
143 | 145 | def intersection(self, other): |
144 | """Return a new set which is the intersection of I{self} and I{other}. | |
145 | ||
146 | @param other: the other set | |
147 | @type other: Set object | |
148 | @rtype: the same type as I{self} | |
146 | """Return a new set which is the intersection of ``self`` and | |
147 | ``other``. | |
148 | ||
149 | Returns the same Set type as this set. | |
149 | 150 | """ |
150 | 151 | |
151 | 152 | obj = self._clone() |
153 | 154 | return obj |
154 | 155 | |
155 | 156 | def difference(self, other): |
156 | """Return a new set which I{self} - I{other}, i.e. the items | |
157 | in I{self} which are not also in I{other}. | |
158 | ||
159 | @param other: the other set | |
160 | @type other: Set object | |
161 | @rtype: the same type as I{self} | |
157 | """Return a new set which ``self`` - ``other``, i.e. the items | |
158 | in ``self`` which are not also in ``other``. | |
159 | ||
160 | Returns the same Set type as this set. | |
162 | 161 | """ |
163 | 162 | |
164 | 163 | obj = self._clone() |
196 | 195 | def update(self, other): |
197 | 196 | """Update the set, adding any elements from other which are not |
198 | 197 | already in the set. |
199 | @param other: the collection of items with which to update the set | |
200 | @type other: any iterable type""" | |
198 | ||
199 | *other*, the collection of items with which to update the set, which | |
200 | may be any iterable type. | |
201 | """ | |
202 | ||
201 | 203 | for item in other: |
202 | 204 | self.add(item) |
203 | 205 | |
232 | 234 | del self.items[i] |
233 | 235 | |
234 | 236 | def issubset(self, other): |
235 | """Is I{self} a subset of I{other}? | |
236 | ||
237 | @rtype: bool | |
237 | """Is this set a subset of *other*? | |
238 | ||
239 | Returns a ``bool``. | |
238 | 240 | """ |
239 | 241 | |
240 | 242 | if not isinstance(other, Set): |
245 | 247 | return True |
246 | 248 | |
247 | 249 | def issuperset(self, other): |
248 | """Is I{self} a superset of I{other}? | |
249 | ||
250 | @rtype: bool | |
250 | """Is this set a superset of *other*? | |
251 | ||
252 | Returns a ``bool``. | |
251 | 253 | """ |
252 | 254 | |
253 | 255 | if not isinstance(other, Set): |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
43 | 45 | |
44 | 46 | |
45 | 47 | class UngetBufferFull(dns.exception.DNSException): |
46 | ||
47 | 48 | """An attempt was made to unget a token when the unget buffer was full.""" |
48 | 49 | |
49 | 50 | |
50 | 51 | class Token(object): |
51 | ||
52 | 52 | """A DNS master file format token. |
53 | 53 | |
54 | @ivar ttype: The token type | |
55 | @type ttype: int | |
56 | @ivar value: The token value | |
57 | @type value: string | |
58 | @ivar has_escape: Does the token value contain escapes? | |
59 | @type has_escape: bool | |
54 | ttype: The token type | |
55 | value: The token value | |
56 | has_escape: Does the token value contain escapes? | |
60 | 57 | """ |
61 | 58 | |
62 | 59 | def __init__(self, ttype, value='', has_escape=False): |
63 | """Initialize a token instance. | |
64 | ||
65 | @param ttype: The token type | |
66 | @type ttype: int | |
67 | @param value: The token value | |
68 | @type value: string | |
69 | @param has_escape: Does the token value contain escapes? | |
70 | @type has_escape: bool | |
71 | """ | |
60 | """Initialize a token instance.""" | |
61 | ||
72 | 62 | self.ttype = ttype |
73 | 63 | self.value = value |
74 | 64 | self.has_escape = has_escape |
159 | 149 | |
160 | 150 | |
161 | 151 | class Tokenizer(object): |
162 | ||
163 | 152 | """A DNS master file format tokenizer. |
164 | 153 | |
165 | A token is a (type, value) tuple, where I{type} is an int, and | |
166 | I{value} is a string. The valid types are EOF, EOL, WHITESPACE, | |
167 | IDENTIFIER, QUOTED_STRING, COMMENT, and DELIMITER. | |
168 | ||
169 | @ivar file: The file to tokenize | |
170 | @type file: file | |
171 | @ivar ungotten_char: The most recently ungotten character, or None. | |
172 | @type ungotten_char: string | |
173 | @ivar ungotten_token: The most recently ungotten token, or None. | |
174 | @type ungotten_token: (int, string) token tuple | |
175 | @ivar multiline: The current multiline level. This value is increased | |
154 | A token object is basically a (type, value) tuple. The valid | |
155 | types are EOF, EOL, WHITESPACE, IDENTIFIER, QUOTED_STRING, | |
156 | COMMENT, and DELIMITER. | |
157 | ||
158 | file: The file to tokenize | |
159 | ||
160 | ungotten_char: The most recently ungotten character, or None. | |
161 | ||
162 | ungotten_token: The most recently ungotten token, or None. | |
163 | ||
164 | multiline: The current multiline level. This value is increased | |
176 | 165 | by one every time a '(' delimiter is read, and decreased by one every time |
177 | 166 | a ')' delimiter is read. |
178 | @type multiline: int | |
179 | @ivar quoting: This variable is true if the tokenizer is currently | |
167 | ||
168 | quoting: This variable is true if the tokenizer is currently | |
180 | 169 | reading a quoted string. |
181 | @type quoting: bool | |
182 | @ivar eof: This variable is true if the tokenizer has encountered EOF. | |
183 | @type eof: bool | |
184 | @ivar delimiters: The current delimiter dictionary. | |
185 | @type delimiters: dict | |
186 | @ivar line_number: The current line number | |
187 | @type line_number: int | |
188 | @ivar filename: A filename that will be returned by the L{where} method. | |
189 | @type filename: string | |
170 | ||
171 | eof: This variable is true if the tokenizer has encountered EOF. | |
172 | ||
173 | delimiters: The current delimiter dictionary. | |
174 | ||
175 | line_number: The current line number | |
176 | ||
177 | filename: A filename that will be returned by the where() method. | |
190 | 178 | """ |
191 | 179 | |
192 | 180 | def __init__(self, f=sys.stdin, filename=None): |
193 | 181 | """Initialize a tokenizer instance. |
194 | 182 | |
195 | @param f: The file to tokenize. The default is sys.stdin. | |
183 | f: The file to tokenize. The default is sys.stdin. | |
196 | 184 | This parameter may also be a string, in which case the tokenizer |
197 | 185 | will take its input from the contents of the string. |
198 | @type f: file or string | |
199 | @param filename: the name of the filename that the L{where} method | |
186 | ||
187 | filename: the name of the filename that the where() method | |
200 | 188 | will return. |
201 | @type filename: string | |
202 | 189 | """ |
203 | 190 | |
204 | 191 | if isinstance(f, text_type): |
227 | 214 | |
228 | 215 | def _get_char(self): |
229 | 216 | """Read a character from input. |
230 | @rtype: string | |
231 | 217 | """ |
232 | 218 | |
233 | 219 | if self.ungotten_char is None: |
247 | 233 | def where(self): |
248 | 234 | """Return the current location in the input. |
249 | 235 | |
250 | @rtype: (string, int) tuple. The first item is the filename of | |
236 | Returns a (string, int) tuple. The first item is the filename of | |
251 | 237 | the input, the second is the current line number. |
252 | 238 | """ |
253 | 239 | |
260 | 246 | an error to try to unget a character when the unget buffer is not |
261 | 247 | empty. |
262 | 248 | |
263 | @param c: the character to unget | |
264 | @type c: string | |
265 | @raises UngetBufferFull: there is already an ungotten char | |
249 | c: the character to unget | |
250 | raises UngetBufferFull: there is already an ungotten char | |
266 | 251 | """ |
267 | 252 | |
268 | 253 | if self.ungotten_char is not None: |
277 | 262 | |
278 | 263 | If the tokenizer is in multiline mode, then newlines are whitespace. |
279 | 264 | |
280 | @rtype: int | |
265 | Returns the number of characters skipped. | |
281 | 266 | """ |
282 | 267 | |
283 | 268 | skipped = 0 |
292 | 277 | def get(self, want_leading=False, want_comment=False): |
293 | 278 | """Get the next token. |
294 | 279 | |
295 | @param want_leading: If True, return a WHITESPACE token if the | |
280 | want_leading: If True, return a WHITESPACE token if the | |
296 | 281 | first character read is whitespace. The default is False. |
297 | @type want_leading: bool | |
298 | @param want_comment: If True, return a COMMENT token if the | |
282 | ||
283 | want_comment: If True, return a COMMENT token if the | |
299 | 284 | first token read is a comment. The default is False. |
300 | @type want_comment: bool | |
301 | @rtype: Token object | |
302 | @raises dns.exception.UnexpectedEnd: input ended prematurely | |
303 | @raises dns.exception.SyntaxError: input was badly formed | |
285 | ||
286 | Raises dns.exception.UnexpectedEnd: input ended prematurely | |
287 | ||
288 | Raises dns.exception.SyntaxError: input was badly formed | |
289 | ||
290 | Returns a Token. | |
304 | 291 | """ |
305 | 292 | |
306 | 293 | if self.ungotten_token is not None: |
419 | 406 | an error to try to unget a token when the unget buffer is not |
420 | 407 | empty. |
421 | 408 | |
422 | @param token: the token to unget | |
423 | @type token: Token object | |
424 | @raises UngetBufferFull: there is already an ungotten token | |
409 | token: the token to unget | |
410 | ||
411 | Raises UngetBufferFull: there is already an ungotten token | |
425 | 412 | """ |
426 | 413 | |
427 | 414 | if self.ungotten_token is not None: |
430 | 417 | |
431 | 418 | def next(self): |
432 | 419 | """Return the next item in an iteration. |
433 | @rtype: (int, string) | |
420 | ||
421 | Returns a Token. | |
434 | 422 | """ |
435 | 423 | |
436 | 424 | token = self.get() |
445 | 433 | |
446 | 434 | # Helpers |
447 | 435 | |
448 | def get_int(self): | |
436 | def get_int(self, base=10): | |
449 | 437 | """Read the next token and interpret it as an integer. |
450 | 438 | |
451 | @raises dns.exception.SyntaxError: | |
452 | @rtype: int | |
439 | Raises dns.exception.SyntaxError if not an integer. | |
440 | ||
441 | Returns an int. | |
453 | 442 | """ |
454 | 443 | |
455 | 444 | token = self.get().unescape() |
457 | 446 | raise dns.exception.SyntaxError('expecting an identifier') |
458 | 447 | if not token.value.isdigit(): |
459 | 448 | raise dns.exception.SyntaxError('expecting an integer') |
460 | return int(token.value) | |
449 | return int(token.value, base) | |
461 | 450 | |
462 | 451 | def get_uint8(self): |
463 | 452 | """Read the next token and interpret it as an 8-bit unsigned |
464 | 453 | integer. |
465 | 454 | |
466 | @raises dns.exception.SyntaxError: | |
467 | @rtype: int | |
455 | Raises dns.exception.SyntaxError if not an 8-bit unsigned integer. | |
456 | ||
457 | Returns an int. | |
468 | 458 | """ |
469 | 459 | |
470 | 460 | value = self.get_int() |
473 | 463 | '%d is not an unsigned 8-bit integer' % value) |
474 | 464 | return value |
475 | 465 | |
476 | def get_uint16(self): | |
466 | def get_uint16(self, base=10): | |
477 | 467 | """Read the next token and interpret it as a 16-bit unsigned |
478 | 468 | integer. |
479 | 469 | |
480 | @raises dns.exception.SyntaxError: | |
481 | @rtype: int | |
482 | """ | |
483 | ||
484 | value = self.get_int() | |
470 | Raises dns.exception.SyntaxError if not a 16-bit unsigned integer. | |
471 | ||
472 | Returns an int. | |
473 | """ | |
474 | ||
475 | value = self.get_int(base=base) | |
485 | 476 | if value < 0 or value > 65535: |
486 | raise dns.exception.SyntaxError( | |
487 | '%d is not an unsigned 16-bit integer' % value) | |
477 | if base == 8: | |
478 | raise dns.exception.SyntaxError( | |
479 | '%o is not an octal unsigned 16-bit integer' % value) | |
480 | else: | |
481 | raise dns.exception.SyntaxError( | |
482 | '%d is not an unsigned 16-bit integer' % value) | |
488 | 483 | return value |
489 | 484 | |
490 | 485 | def get_uint32(self): |
491 | 486 | """Read the next token and interpret it as a 32-bit unsigned |
492 | 487 | integer. |
493 | 488 | |
494 | @raises dns.exception.SyntaxError: | |
495 | @rtype: int | |
489 | Raises dns.exception.SyntaxError if not a 32-bit unsigned integer. | |
490 | ||
491 | Returns an int. | |
496 | 492 | """ |
497 | 493 | |
498 | 494 | token = self.get().unescape() |
509 | 505 | def get_string(self, origin=None): |
510 | 506 | """Read the next token and interpret it as a string. |
511 | 507 | |
512 | @raises dns.exception.SyntaxError: | |
513 | @rtype: string | |
508 | Raises dns.exception.SyntaxError if not a string. | |
509 | ||
510 | Returns a string. | |
514 | 511 | """ |
515 | 512 | |
516 | 513 | token = self.get().unescape() |
519 | 516 | return token.value |
520 | 517 | |
521 | 518 | def get_identifier(self, origin=None): |
522 | """Read the next token and raise an exception if it is not an identifier. | |
523 | ||
524 | @raises dns.exception.SyntaxError: | |
525 | @rtype: string | |
519 | """Read the next token, which should be an identifier. | |
520 | ||
521 | Raises dns.exception.SyntaxError if not an identifier. | |
522 | ||
523 | Returns a string. | |
526 | 524 | """ |
527 | 525 | |
528 | 526 | token = self.get().unescape() |
533 | 531 | def get_name(self, origin=None): |
534 | 532 | """Read the next token and interpret it as a DNS name. |
535 | 533 | |
536 | @raises dns.exception.SyntaxError: | |
537 | @rtype: dns.name.Name object""" | |
534 | Raises dns.exception.SyntaxError if not a name. | |
535 | ||
536 | Returns a dns.name.Name. | |
537 | """ | |
538 | 538 | |
539 | 539 | token = self.get() |
540 | 540 | if not token.is_identifier(): |
545 | 545 | """Read the next token and raise an exception if it isn't EOL or |
546 | 546 | EOF. |
547 | 547 | |
548 | @raises dns.exception.SyntaxError: | |
549 | @rtype: string | |
548 | Returns a string. | |
550 | 549 | """ |
551 | 550 | |
552 | 551 | token = self.get() |
557 | 556 | return token.value |
558 | 557 | |
559 | 558 | def get_ttl(self): |
559 | """Read the next token and interpret it as a DNS TTL. | |
560 | ||
561 | Raises dns.exception.SyntaxError or dns.ttl.BadTTL if not an | |
562 | identifier or badly formed. | |
563 | ||
564 | Returns an int. | |
565 | """ | |
566 | ||
560 | 567 | token = self.get().unescape() |
561 | 568 | if not token.is_identifier(): |
562 | 569 | raise dns.exception.SyntaxError('expecting an identifier') |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
14 | 16 | |
15 | 17 | """DNS TSIG support.""" |
16 | 18 | |
19 | import hashlib | |
17 | 20 | import hmac |
18 | 21 | import struct |
19 | 22 | |
20 | 23 | import dns.exception |
21 | import dns.hash | |
22 | 24 | import dns.rdataclass |
23 | 25 | import dns.name |
24 | 26 | from ._compat import long, string_types, text_type |
67 | 69 | HMAC_SHA512 = dns.name.from_text("hmac-sha512") |
68 | 70 | |
69 | 71 | _hashes = { |
70 | HMAC_SHA224: 'SHA224', | |
71 | HMAC_SHA256: 'SHA256', | |
72 | HMAC_SHA384: 'SHA384', | |
73 | HMAC_SHA512: 'SHA512', | |
74 | HMAC_SHA1: 'SHA1', | |
75 | HMAC_MD5: 'MD5', | |
72 | HMAC_SHA224: hashlib.sha224, | |
73 | HMAC_SHA256: hashlib.sha256, | |
74 | HMAC_SHA384: hashlib.sha384, | |
75 | HMAC_SHA512: hashlib.sha512, | |
76 | HMAC_SHA1: hashlib.sha1, | |
77 | HMAC_MD5: hashlib.md5, | |
76 | 78 | } |
77 | 79 | |
78 | 80 | default_algorithm = HMAC_MD5 |
210 | 212 | algorithm = dns.name.from_text(algorithm) |
211 | 213 | |
212 | 214 | try: |
213 | return (algorithm.to_digestable(), dns.hash.hashes[_hashes[algorithm]]) | |
215 | return (algorithm.to_digestable(), _hashes[algorithm]) | |
214 | 216 | except KeyError: |
215 | 217 | raise NotImplementedError("TSIG algorithm " + str(algorithm) + |
216 | 218 | " is not supported") |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
0 | from typing import Dict | |
1 | from . import name | |
2 | ||
3 | def from_text(textring : Dict[str,str]) -> Dict[name.Name,bytes]: | |
4 | ... | |
5 | def to_text(keyring : Dict[name.Name,bytes]) -> Dict[str, str]: | |
6 | ... |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
19 | 21 | |
20 | 22 | |
21 | 23 | class BadTTL(dns.exception.SyntaxError): |
22 | ||
23 | 24 | """DNS TTL value is not well-formed.""" |
24 | 25 | |
25 | 26 | |
28 | 29 | |
29 | 30 | The BIND 8 units syntax for TTLs (e.g. '1w6d4h3m10s') is supported. |
30 | 31 | |
31 | @param text: the textual TTL | |
32 | @type text: string | |
33 | @raises dns.ttl.BadTTL: the TTL is not well-formed | |
34 | @rtype: int | |
32 | *text*, a ``text``, the textual TTL. | |
33 | ||
34 | Raises ``dns.ttl.BadTTL`` if the TTL is not well-formed. | |
35 | ||
36 | Returns an ``int``. | |
35 | 37 | """ |
36 | 38 | |
37 | 39 | if text.isdigit(): |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
31 | 33 | keyname=None, keyalgorithm=dns.tsig.default_algorithm): |
32 | 34 | """Initialize a new DNS Update object. |
33 | 35 | |
34 | @param zone: The zone which is being updated. | |
35 | @type zone: A dns.name.Name or string | |
36 | @param rdclass: The class of the zone; defaults to dns.rdataclass.IN. | |
37 | @type rdclass: An int designating the class, or a string whose value | |
38 | is the name of a class. | |
39 | @param keyring: The TSIG keyring to use; defaults to None. | |
40 | @type keyring: dict | |
41 | @param keyname: The name of the TSIG key to use; defaults to None. | |
42 | The key must be defined in the keyring. If a keyring is specified | |
43 | but a keyname is not, then the key used will be the first key in the | |
44 | keyring. Note that the order of keys in a dictionary is not defined, | |
45 | so applications should supply a keyname when a keyring is used, unless | |
46 | they know the keyring contains only one key. | |
47 | @type keyname: dns.name.Name or string | |
48 | @param keyalgorithm: The TSIG algorithm to use; defaults to | |
49 | dns.tsig.default_algorithm. Constants for TSIG algorithms are defined | |
50 | in dns.tsig, and the currently implemented algorithms are | |
51 | HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and | |
52 | HMAC_SHA512. | |
53 | @type keyalgorithm: string | |
36 | See the documentation of the Message class for a complete | |
37 | description of the keyring dictionary. | |
38 | ||
39 | *zone*, a ``dns.name.Name`` or ``text``, the zone which is being | |
40 | updated. | |
41 | ||
42 | *rdclass*, an ``int`` or ``text``, the class of the zone. | |
43 | ||
44 | *keyring*, a ``dict``, the TSIG keyring to use. If a | |
45 | *keyring* is specified but a *keyname* is not, then the key | |
46 | used will be the first key in the *keyring*. Note that the | |
47 | order of keys in a dictionary is not defined, so applications | |
48 | should supply a keyname when a keyring is used, unless they | |
49 | know the keyring contains only one key. | |
50 | ||
51 | *keyname*, a ``dns.name.Name`` or ``None``, the name of the TSIG key | |
52 | to use; defaults to ``None``. The key must be defined in the keyring. | |
53 | ||
54 | *keyalgorithm*, a ``dns.name.Name``, the TSIG algorithm to use. | |
54 | 55 | """ |
55 | 56 | super(Update, self).__init__() |
56 | 57 | self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE) |
76 | 77 | rrset.add(rd, ttl) |
77 | 78 | |
78 | 79 | def _add(self, replace, section, name, *args): |
79 | """Add records. The first argument is the replace mode. If | |
80 | false, RRs are added to an existing RRset; if true, the RRset | |
80 | """Add records. | |
81 | ||
82 | *replace* is the replacement mode. If ``False``, | |
83 | RRs are added to an existing RRset; if ``True``, the RRset | |
81 | 84 | is replaced with the specified contents. The second |
82 | 85 | argument is the section to add to. The third argument |
83 | 86 | is always a name. The other arguments can be: |
86 | 89 | |
87 | 90 | - ttl, rdata... |
88 | 91 | |
89 | - ttl, rdtype, string...""" | |
92 | - ttl, rdtype, string... | |
93 | """ | |
90 | 94 | |
91 | 95 | if isinstance(name, string_types): |
92 | 96 | name = dns.name.from_text(name, None) |
116 | 120 | self._add_rr(name, ttl, rd, section=section) |
117 | 121 | |
118 | 122 | def add(self, name, *args): |
119 | """Add records. The first argument is always a name. The other | |
123 | """Add records. | |
124 | ||
125 | The first argument is always a name. The other | |
120 | 126 | arguments can be: |
121 | 127 | |
122 | 128 | - rdataset... |
123 | 129 | |
124 | 130 | - ttl, rdata... |
125 | 131 | |
126 | - ttl, rdtype, string...""" | |
132 | - ttl, rdtype, string... | |
133 | """ | |
134 | ||
127 | 135 | self._add(False, self.authority, name, *args) |
128 | 136 | |
129 | 137 | def delete(self, name, *args): |
130 | """Delete records. The first argument is always a name. The other | |
138 | """Delete records. | |
139 | ||
140 | The first argument is always a name. The other | |
131 | 141 | arguments can be: |
132 | 142 | |
133 | - I{nothing} | |
143 | - *empty* | |
134 | 144 | |
135 | 145 | - rdataset... |
136 | 146 | |
137 | 147 | - rdata... |
138 | 148 | |
139 | - rdtype, [string...]""" | |
149 | - rdtype, [string...] | |
150 | """ | |
140 | 151 | |
141 | 152 | if isinstance(name, string_types): |
142 | 153 | name = dns.name.from_text(name, None) |
170 | 181 | self._add_rr(name, 0, rd, dns.rdataclass.NONE) |
171 | 182 | |
172 | 183 | def replace(self, name, *args): |
173 | """Replace records. The first argument is always a name. The other | |
184 | """Replace records. | |
185 | ||
186 | The first argument is always a name. The other | |
174 | 187 | arguments can be: |
175 | 188 | |
176 | 189 | - rdataset... |
180 | 193 | - ttl, rdtype, string... |
181 | 194 | |
182 | 195 | Note that if you want to replace the entire node, you should do |
183 | a delete of the name followed by one or more calls to add.""" | |
196 | a delete of the name followed by one or more calls to add. | |
197 | """ | |
184 | 198 | |
185 | 199 | self._add(True, self.authority, name, *args) |
186 | 200 | |
187 | 201 | def present(self, name, *args): |
188 | 202 | """Require that an owner name (and optionally an rdata type, |
189 | 203 | or specific rdataset) exists as a prerequisite to the |
190 | execution of the update. The first argument is always a name. | |
204 | execution of the update. | |
205 | ||
206 | The first argument is always a name. | |
191 | 207 | The other arguments can be: |
192 | 208 | |
193 | 209 | - rdataset... |
194 | 210 | |
195 | 211 | - rdata... |
196 | 212 | |
197 | - rdtype, string...""" | |
213 | - rdtype, string... | |
214 | """ | |
198 | 215 | |
199 | 216 | if isinstance(name, string_types): |
200 | 217 | name = dns.name.from_text(name, None) |
242 | 259 | def to_wire(self, origin=None, max_size=65535): |
243 | 260 | """Return a string containing the update in DNS compressed wire |
244 | 261 | format. |
245 | @rtype: string""" | |
262 | ||
263 | *origin*, a ``dns.name.Name`` or ``None``, the origin to be | |
264 | appended to any relative names. If *origin* is ``None``, then | |
265 | the origin of the ``dns.update.Update`` message object is used | |
266 | (i.e. the *zone* parameter passed when the Update object was | |
267 | created). | |
268 | ||
269 | *max_size*, an ``int``, the maximum size of the wire format | |
270 | output; default is 0, which means "the message's request | |
271 | payload, if nonzero, or 65535". | |
272 | ||
273 | Returns a ``binary``. | |
274 | """ | |
275 | ||
246 | 276 | if origin is None: |
247 | 277 | origin = self.origin |
248 | 278 | return super(Update, self).to_wire(origin, max_size) |
0 | from typing import Optional,Dict,Union,Any | |
1 | ||
2 | from . import message, tsig, rdataclass, name | |
3 | ||
4 | class Update(message.Message): | |
5 | def __init__(self, zone : Union[name.Name, str], rdclass : Union[int,str] = rdataclass.IN, keyring : Optional[Dict[name.Name,bytes]] = None, | |
6 | keyname : Optional[name.Name] = None, keyalgorithm : Optional[name.Name] = tsig.default_algorithm) -> None: | |
7 | self.id : int | |
8 | def add(self, name : Union[str,name.Name], *args : Any): | |
9 | ... | |
10 | def delete(self, name, *args : Any): | |
11 | ... | |
12 | def replace(self, name : Union[str,name.Name], *args : Any): | |
13 | ... | |
14 | def present(self, name : Union[str,name.Name], *args : Any): | |
15 | ... | |
16 | def absent(self, name : Union[str,name.Name], rdtype=None): | |
17 | """Require that an owner name (and optionally an rdata type) does | |
18 | not exist as a prerequisite to the execution of the update.""" | |
19 | def to_wire(self, origin : Optional[name.Name] = None, max_size=65535, **kw) -> bytes: | |
20 | ... |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
14 | 16 | |
15 | 17 | """dnspython release version information.""" |
16 | 18 | |
19 | #: MAJOR | |
17 | 20 | MAJOR = 1 |
18 | MINOR = 15 | |
21 | #: MINOR | |
22 | MINOR = 16 | |
23 | #: MICRO | |
19 | 24 | MICRO = 0 |
25 | #: RELEASELEVEL | |
20 | 26 | RELEASELEVEL = 0x0f |
27 | #: SERIAL | |
21 | 28 | SERIAL = 0 |
22 | 29 | |
23 | 30 | if RELEASELEVEL == 0x0f: |
31 | #: version | |
24 | 32 | version = '%d.%d.%d' % (MAJOR, MINOR, MICRO) |
25 | 33 | elif RELEASELEVEL == 0x00: |
26 | 34 | version = '%d.%d.%dx%d' % \ |
29 | 37 | version = '%d.%d.%d%x%d' % \ |
30 | 38 | (MAJOR, MINOR, MICRO, RELEASELEVEL, SERIAL) |
31 | 39 | |
40 | #: hexversion | |
32 | 41 | hexversion = MAJOR << 24 | MINOR << 16 | MICRO << 8 | RELEASELEVEL << 4 | \ |
33 | 42 | SERIAL |
0 | # Copyright (C) 2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2011,2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
14 | 16 | |
15 | 17 | """DNS Wire Data Helper""" |
16 | 18 | |
17 | import sys | |
18 | ||
19 | 19 | import dns.exception |
20 | from ._compat import binary_type, string_types | |
20 | from ._compat import binary_type, string_types, PY2 | |
21 | 21 | |
22 | 22 | # Figure out what constant python passes for an unspecified slice bound. |
23 | 23 | # It's supposed to be sys.maxint, yet on 64-bit windows sys.maxint is 2^31 - 1 |
31 | 31 | def __getitem__(self, key): |
32 | 32 | return key.stop |
33 | 33 | |
34 | if sys.version_info < (3,): | |
34 | if PY2: | |
35 | 35 | def __getslice__(self, i, j): # pylint: disable=getslice-method |
36 | 36 | return self.__getitem__(slice(i, j)) |
37 | 37 | |
39 | 39 | |
40 | 40 | |
41 | 41 | class WireData(binary_type): |
42 | # WireData is a string with stricter slicing | |
42 | # WireData is a binary type with stricter slicing | |
43 | 43 | |
44 | 44 | def __getitem__(self, key): |
45 | 45 | try: |
50 | 50 | start = key.start |
51 | 51 | stop = key.stop |
52 | 52 | |
53 | if sys.version_info < (3,): | |
53 | if PY2: | |
54 | 54 | if stop == _unspecified_bound: |
55 | 55 | # handle the case where the right bound is unspecified |
56 | 56 | stop = len(self) |
75 | 75 | except IndexError: |
76 | 76 | raise dns.exception.FormError |
77 | 77 | |
78 | if sys.version_info < (3,): | |
78 | if PY2: | |
79 | 79 | def __getslice__(self, i, j): # pylint: disable=getslice-method |
80 | 80 | return self.__getitem__(slice(i, j)) |
81 | 81 |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
27 | 29 | import dns.rdataclass |
28 | 30 | import dns.rdatatype |
29 | 31 | import dns.rdata |
32 | import dns.rdtypes.ANY.SOA | |
30 | 33 | import dns.rrset |
31 | 34 | import dns.tokenizer |
32 | 35 | import dns.ttl |
33 | 36 | import dns.grange |
34 | from ._compat import string_types, text_type | |
35 | ||
36 | ||
37 | _py3 = sys.version_info > (3,) | |
37 | from ._compat import string_types, text_type, PY3 | |
38 | 38 | |
39 | 39 | |
40 | 40 | class BadZone(dns.exception.DNSException): |
156 | 156 | return self.nodes.__iter__() |
157 | 157 | |
158 | 158 | def iterkeys(self): |
159 | if _py3: | |
160 | return self.nodes.keys() | |
159 | if PY3: | |
160 | return self.nodes.keys() # pylint: disable=dict-keys-not-iterating | |
161 | 161 | else: |
162 | 162 | return self.nodes.iterkeys() # pylint: disable=dict-iter-method |
163 | 163 | |
164 | 164 | def keys(self): |
165 | return self.nodes.keys() | |
165 | return self.nodes.keys() # pylint: disable=dict-keys-not-iterating | |
166 | 166 | |
167 | 167 | def itervalues(self): |
168 | if _py3: | |
169 | return self.nodes.values() | |
168 | if PY3: | |
169 | return self.nodes.values() # pylint: disable=dict-values-not-iterating | |
170 | 170 | else: |
171 | 171 | return self.nodes.itervalues() # pylint: disable=dict-iter-method |
172 | 172 | |
173 | 173 | def values(self): |
174 | return self.nodes.values() | |
174 | return self.nodes.values() # pylint: disable=dict-values-not-iterating | |
175 | 175 | |
176 | 176 | def items(self): |
177 | return self.nodes.items() | |
177 | return self.nodes.items() # pylint: disable=dict-items-not-iterating | |
178 | 178 | |
179 | 179 | iteritems = items |
180 | 180 | |
260 | 260 | exist? |
261 | 261 | @type create: bool |
262 | 262 | @raises KeyError: the node or rdata could not be found |
263 | @rtype: dns.rrset.RRset object | |
263 | @rtype: dns.rdataset.Rdataset object | |
264 | 264 | """ |
265 | 265 | |
266 | 266 | name = self._validate_name(name) |
295 | 295 | @param create: should the node and rdataset be created if they do not |
296 | 296 | exist? |
297 | 297 | @type create: bool |
298 | @rtype: dns.rrset.RRset object | |
298 | @rtype: dns.rdataset.Rdataset object or None | |
299 | 299 | """ |
300 | 300 | |
301 | 301 | try: |
450 | 450 | rdtype = dns.rdatatype.from_text(rdtype) |
451 | 451 | if isinstance(covers, string_types): |
452 | 452 | covers = dns.rdatatype.from_text(covers) |
453 | for (name, node) in self.iteritems(): | |
453 | for (name, node) in self.iteritems(): # pylint: disable=dict-iter-method | |
454 | 454 | for rds in node: |
455 | 455 | if rdtype == dns.rdatatype.ANY or \ |
456 | 456 | (rds.rdtype == rdtype and rds.covers == covers): |
473 | 473 | rdtype = dns.rdatatype.from_text(rdtype) |
474 | 474 | if isinstance(covers, string_types): |
475 | 475 | covers = dns.rdatatype.from_text(covers) |
476 | for (name, node) in self.iteritems(): | |
476 | for (name, node) in self.iteritems(): # pylint: disable=dict-iter-method | |
477 | 477 | for rds in node: |
478 | 478 | if rdtype == dns.rdatatype.ANY or \ |
479 | 479 | (rds.rdtype == rdtype and rds.covers == covers): |
524 | 524 | names = list(self.keys()) |
525 | 525 | names.sort() |
526 | 526 | else: |
527 | names = self.iterkeys() | |
527 | names = self.iterkeys() # pylint: disable=dict-iter-method | |
528 | 528 | for n in names: |
529 | 529 | l = self[n].to_text(n, origin=self.origin, |
530 | 530 | relativize=relativize) |
588 | 588 | |
589 | 589 | @ivar tok: The tokenizer |
590 | 590 | @type tok: dns.tokenizer.Tokenizer object |
591 | @ivar ttl: The default TTL | |
592 | @type ttl: int | |
591 | @ivar last_ttl: The last seen explicit TTL for an RR | |
592 | @type last_ttl: int | |
593 | @ivar last_ttl_known: Has last TTL been detected | |
594 | @type last_ttl_known: bool | |
595 | @ivar default_ttl: The default TTL from a $TTL directive or SOA RR | |
596 | @type default_ttl: int | |
597 | @ivar default_ttl_known: Has default TTL been detected | |
598 | @type default_ttl_known: bool | |
593 | 599 | @ivar last_name: The last name read |
594 | 600 | @type last_name: dns.name.Name object |
595 | 601 | @ivar current_origin: The current origin |
599 | 605 | @ivar zone: the zone |
600 | 606 | @type zone: dns.zone.Zone object |
601 | 607 | @ivar saved_state: saved reader state (used when processing $INCLUDE) |
602 | @type saved_state: list of (tokenizer, current_origin, last_name, file) | |
603 | tuples. | |
608 | @type saved_state: list of (tokenizer, current_origin, last_name, file, | |
609 | last_ttl, last_ttl_known, default_ttl, default_ttl_known) tuples. | |
604 | 610 | @ivar current_file: the file object of the $INCLUDed file being parsed |
605 | 611 | (None if no $INCLUDE is active). |
606 | 612 | @ivar allow_include: is $INCLUDE allowed? |
617 | 623 | self.tok = tok |
618 | 624 | self.current_origin = origin |
619 | 625 | self.relativize = relativize |
620 | self.ttl = 0 | |
626 | self.last_ttl = 0 | |
627 | self.last_ttl_known = False | |
628 | self.default_ttl = 0 | |
629 | self.default_ttl_known = False | |
621 | 630 | self.last_name = self.current_origin |
622 | 631 | self.zone = zone_factory(origin, rdclass, relativize=relativize) |
623 | 632 | self.saved_state = [] |
658 | 667 | # TTL |
659 | 668 | try: |
660 | 669 | ttl = dns.ttl.from_text(token.value) |
670 | self.last_ttl = ttl | |
671 | self.last_ttl_known = True | |
661 | 672 | token = self.tok.get() |
662 | 673 | if not token.is_identifier(): |
663 | 674 | raise dns.exception.SyntaxError |
664 | 675 | except dns.ttl.BadTTL: |
665 | ttl = self.ttl | |
676 | if not (self.last_ttl_known or self.default_ttl_known): | |
677 | raise dns.exception.SyntaxError("Missing default TTL value") | |
678 | if self.default_ttl_known: | |
679 | ttl = self.default_ttl | |
680 | else: | |
681 | ttl = self.last_ttl | |
666 | 682 | # Class |
667 | 683 | try: |
668 | 684 | rdclass = dns.rdataclass.from_text(token.value) |
700 | 716 | # helpful filename:line info. |
701 | 717 | (ty, va) = sys.exc_info()[:2] |
702 | 718 | raise dns.exception.SyntaxError( |
703 | "caught exception %s: %s" % (str(ty), str(va))) | |
719 | "caught exception {}: {}".format(str(ty), str(va))) | |
720 | ||
721 | if not self.default_ttl_known and isinstance(rd, dns.rdtypes.ANY.SOA.SOA): | |
722 | # The pre-RFC2308 and pre-BIND9 behavior inherits the zone default | |
723 | # TTL from the SOA minttl if no $TTL statement is present before the | |
724 | # SOA is parsed. | |
725 | self.default_ttl = rd.minimum | |
726 | self.default_ttl_known = True | |
704 | 727 | |
705 | 728 | rd.choose_relativity(self.zone.origin, self.relativize) |
706 | 729 | covers = rd.covers() |
777 | 800 | # TTL |
778 | 801 | try: |
779 | 802 | ttl = dns.ttl.from_text(token.value) |
803 | self.last_ttl = ttl | |
804 | self.last_ttl_known = True | |
780 | 805 | token = self.tok.get() |
781 | 806 | if not token.is_identifier(): |
782 | 807 | raise dns.exception.SyntaxError |
783 | 808 | except dns.ttl.BadTTL: |
784 | ttl = self.ttl | |
809 | if not (self.last_ttl_known or self.default_ttl_known): | |
810 | raise dns.exception.SyntaxError("Missing default TTL value") | |
811 | if self.default_ttl_known: | |
812 | ttl = self.default_ttl | |
813 | else: | |
814 | ttl = self.last_ttl | |
785 | 815 | # Class |
786 | 816 | try: |
787 | 817 | rdclass = dns.rdataclass.from_text(token.value) |
883 | 913 | self.current_origin, |
884 | 914 | self.last_name, |
885 | 915 | self.current_file, |
886 | self.ttl) = self.saved_state.pop(-1) | |
916 | self.last_ttl, | |
917 | self.last_ttl_known, | |
918 | self.default_ttl, | |
919 | self.default_ttl_known) = self.saved_state.pop(-1) | |
887 | 920 | continue |
888 | 921 | break |
889 | 922 | elif token.is_eol(): |
897 | 930 | token = self.tok.get() |
898 | 931 | if not token.is_identifier(): |
899 | 932 | raise dns.exception.SyntaxError("bad $TTL") |
900 | self.ttl = dns.ttl.from_text(token.value) | |
933 | self.default_ttl = dns.ttl.from_text(token.value) | |
934 | self.default_ttl_known = True | |
901 | 935 | self.tok.get_eol() |
902 | 936 | elif c == u'$ORIGIN': |
903 | 937 | self.current_origin = self.tok.get_name() |
922 | 956 | self.current_origin, |
923 | 957 | self.last_name, |
924 | 958 | self.current_file, |
925 | self.ttl)) | |
959 | self.last_ttl, | |
960 | self.last_ttl_known, | |
961 | self.default_ttl, | |
962 | self.default_ttl_known)) | |
926 | 963 | self.current_file = open(filename, 'r') |
927 | 964 | self.tok = dns.tokenizer.Tokenizer(self.current_file, |
928 | 965 | filename) |
1023 | 1060 | """ |
1024 | 1061 | |
1025 | 1062 | str_type = string_types |
1026 | opts = 'rU' | |
1063 | if PY3: | |
1064 | opts = 'r' | |
1065 | else: | |
1066 | opts = 'rU' | |
1027 | 1067 | |
1028 | 1068 | if isinstance(f, str_type): |
1029 | 1069 | if filename is None: |
0 | from typing import Generator, Optional, Union, Tuple, Iterable, Callable, Any, Iterator, TextIO, BinaryIO, Dict | |
1 | from . import rdata, zone, rdataclass, name, rdataclass, message, rdatatype, exception, node, rdataset, rrset, rdatatype | |
2 | ||
3 | class BadZone(exception.DNSException): ... | |
4 | class NoSOA(BadZone): ... | |
5 | class NoNS(BadZone): ... | |
6 | class UnknownOrigin(BadZone): ... | |
7 | ||
8 | class Zone: | |
9 | def __getitem__(self, key : str) -> node.Node: | |
10 | ... | |
11 | def __init__(self, origin : Union[str,name.Name], rdclass : int = rdataclass.IN, relativize : bool = True) -> None: | |
12 | self.nodes : Dict[str,node.Node] | |
13 | self.origin = origin | |
14 | def values(self): | |
15 | return self.nodes.values() | |
16 | def iterate_rdatas(self, rdtype : Union[int,str] = rdatatype.ANY, covers : Union[int,str] = None) -> Iterable[Tuple[name.Name, int, rdata.Rdata]]: | |
17 | ... | |
18 | def __iter__(self) -> Iterator[str]: | |
19 | ... | |
20 | def get_node(self, name : Union[name.Name,str], create=False) -> Optional[node.Node]: | |
21 | ... | |
22 | def find_rrset(self, name : Union[str,name.Name], rdtype : Union[int,str], covers=rdatatype.NONE) -> rrset.RRset: | |
23 | ... | |
24 | def find_rdataset(self, name : Union[str,name.Name], rdtype : Union[str,int], covers=rdatatype.NONE, | |
25 | create=False) -> rdataset.Rdataset: | |
26 | ... | |
27 | def get_rdataset(self, name : Union[str,name.Name], rdtype : Union[str,int], covers=rdatatype.NONE, create=False) -> Optional[rdataset.Rdataset]: | |
28 | ... | |
29 | def get_rrset(self, name : Union[str,name.Name], rdtype : Union[str,int], covers=rdatatype.NONE) -> Optional[rrset.RRset]: | |
30 | ... | |
31 | def replace_rdataset(self, name : Union[str,name.Name], replacement : rdataset.Rdataset) -> None: | |
32 | ... | |
33 | def delete_rdataset(self, name : Union[str,name.Name], rdtype : Union[str,int], covers=rdatatype.NONE) -> None: | |
34 | ... | |
35 | def iterate_rdatasets(self, rdtype : Union[str,int] =rdatatype.ANY, | |
36 | covers : Union[str,int] =rdatatype.NONE): | |
37 | ... | |
38 | def to_file(self, f : Union[TextIO, BinaryIO, str], sorted=True, relativize=True, nl : Optional[bytes] = None): | |
39 | ... | |
40 | def to_text(self, sorted=True, relativize=True, nl : Optional[bytes] = None) -> bytes: | |
41 | ... | |
42 | ||
43 | def from_xfr(xfr : Generator[Any,Any,message.Message], zone_factory : Callable[..., zone.Zone] = zone.Zone, relativize=True, check_origin=True): | |
44 | ... | |
45 | ||
46 | def from_text(text : str, origin : Optional[Union[str,name.Name]] = None, rdclass : int = rdataclass.IN, | |
47 | relativize=True, zone_factory : Callable[...,zone.Zone] = zone.Zone, filename : Optional[str] = None, | |
48 | allow_include=False, check_origin=True) -> zone.Zone: | |
49 | ... | |
50 | ||
51 | def from_file(f, origin : Optional[Union[str,name.Name]] = None, rdclass=rdataclass.IN, | |
52 | relativize=True, zone_factory : Callable[..., zone.Zone] = Zone, filename : Optional[str] = None, | |
53 | allow_include=True, check_origin=True) -> zone.Zone: | |
54 | ... |
0 | Metadata-Version: 1.1 | |
1 | Name: dnspython | |
2 | Version: 1.15.0 | |
3 | Summary: DNS toolkit | |
4 | Home-page: http://www.dnspython.org | |
5 | Author: Bob Halley | |
6 | Author-email: halley@dnspython.org | |
7 | License: BSD-like | |
8 | Download-URL: http://www.dnspython.org/kits/1.15.0/dnspython-1.15.0.tar.gz | |
9 | Description: dnspython is a DNS toolkit for Python. It supports almost all | |
10 | record types. It can be used for queries, zone transfers, and dynamic | |
11 | updates. It supports TSIG authenticated messages and EDNS0. | |
12 | ||
13 | dnspython provides both high and low level access to DNS. The high | |
14 | level classes perform queries for data of a given name, type, and | |
15 | class, and return an answer set. The low level classes allow | |
16 | direct manipulation of DNS zones, messages, names, and records. | |
17 | Platform: UNKNOWN | |
18 | Classifier: Development Status :: 5 - Production/Stable | |
19 | Classifier: Intended Audience :: Developers | |
20 | Classifier: Intended Audience :: System Administrators | |
21 | Classifier: License :: Freeware | |
22 | Classifier: Operating System :: Microsoft :: Windows :: Windows 95/98/2000 | |
23 | Classifier: Operating System :: POSIX | |
24 | Classifier: Programming Language :: Python | |
25 | Classifier: Topic :: Internet :: Name Service (DNS) | |
26 | Classifier: Topic :: Software Development :: Libraries :: Python Modules | |
27 | Classifier: Programming Language :: Python :: 2 | |
28 | Classifier: Programming Language :: Python :: 2.6 | |
29 | Classifier: Programming Language :: Python :: 2.7 | |
30 | Classifier: Programming Language :: Python :: 3 | |
31 | Classifier: Programming Language :: Python :: 3.3 | |
32 | Classifier: Programming Language :: Python :: 3.4 | |
33 | Classifier: Programming Language :: Python :: 3.5 | |
34 | Provides: dns |
0 | ChangeLog | |
1 | LICENSE | |
2 | MANIFEST.in | |
3 | setup.py | |
4 | dns/__init__.py | |
5 | dns/_compat.py | |
6 | dns/dnssec.py | |
7 | dns/e164.py | |
8 | dns/edns.py | |
9 | dns/entropy.py | |
10 | dns/exception.py | |
11 | dns/flags.py | |
12 | dns/grange.py | |
13 | dns/hash.py | |
14 | dns/inet.py | |
15 | dns/ipv4.py | |
16 | dns/ipv6.py | |
17 | dns/message.py | |
18 | dns/name.py | |
19 | dns/namedict.py | |
20 | dns/node.py | |
21 | dns/opcode.py | |
22 | dns/query.py | |
23 | dns/rcode.py | |
24 | dns/rdata.py | |
25 | dns/rdataclass.py | |
26 | dns/rdataset.py | |
27 | dns/rdatatype.py | |
28 | dns/renderer.py | |
29 | dns/resolver.py | |
30 | dns/reversename.py | |
31 | dns/rrset.py | |
32 | dns/set.py | |
33 | dns/tokenizer.py | |
34 | dns/tsig.py | |
35 | dns/tsigkeyring.py | |
36 | dns/ttl.py | |
37 | dns/update.py | |
38 | dns/version.py | |
39 | dns/wiredata.py | |
40 | dns/zone.py | |
41 | dns/rdtypes/__init__.py | |
42 | dns/rdtypes/dnskeybase.py | |
43 | dns/rdtypes/dsbase.py | |
44 | dns/rdtypes/euibase.py | |
45 | dns/rdtypes/mxbase.py | |
46 | dns/rdtypes/nsbase.py | |
47 | dns/rdtypes/txtbase.py | |
48 | dns/rdtypes/ANY/AFSDB.py | |
49 | dns/rdtypes/ANY/AVC.py | |
50 | dns/rdtypes/ANY/CAA.py | |
51 | dns/rdtypes/ANY/CDNSKEY.py | |
52 | dns/rdtypes/ANY/CDS.py | |
53 | dns/rdtypes/ANY/CERT.py | |
54 | dns/rdtypes/ANY/CNAME.py | |
55 | dns/rdtypes/ANY/CSYNC.py | |
56 | dns/rdtypes/ANY/DLV.py | |
57 | dns/rdtypes/ANY/DNAME.py | |
58 | dns/rdtypes/ANY/DNSKEY.py | |
59 | dns/rdtypes/ANY/DS.py | |
60 | dns/rdtypes/ANY/EUI48.py | |
61 | dns/rdtypes/ANY/EUI64.py | |
62 | dns/rdtypes/ANY/GPOS.py | |
63 | dns/rdtypes/ANY/HINFO.py | |
64 | dns/rdtypes/ANY/HIP.py | |
65 | dns/rdtypes/ANY/ISDN.py | |
66 | dns/rdtypes/ANY/LOC.py | |
67 | dns/rdtypes/ANY/MX.py | |
68 | dns/rdtypes/ANY/NS.py | |
69 | dns/rdtypes/ANY/NSEC.py | |
70 | dns/rdtypes/ANY/NSEC3.py | |
71 | dns/rdtypes/ANY/NSEC3PARAM.py | |
72 | dns/rdtypes/ANY/PTR.py | |
73 | dns/rdtypes/ANY/RP.py | |
74 | dns/rdtypes/ANY/RRSIG.py | |
75 | dns/rdtypes/ANY/RT.py | |
76 | dns/rdtypes/ANY/SOA.py | |
77 | dns/rdtypes/ANY/SPF.py | |
78 | dns/rdtypes/ANY/SSHFP.py | |
79 | dns/rdtypes/ANY/TLSA.py | |
80 | dns/rdtypes/ANY/TXT.py | |
81 | dns/rdtypes/ANY/URI.py | |
82 | dns/rdtypes/ANY/X25.py | |
83 | dns/rdtypes/ANY/__init__.py | |
84 | dns/rdtypes/IN/A.py | |
85 | dns/rdtypes/IN/AAAA.py | |
86 | dns/rdtypes/IN/APL.py | |
87 | dns/rdtypes/IN/DHCID.py | |
88 | dns/rdtypes/IN/IPSECKEY.py | |
89 | dns/rdtypes/IN/KX.py | |
90 | dns/rdtypes/IN/NAPTR.py | |
91 | dns/rdtypes/IN/NSAP.py | |
92 | dns/rdtypes/IN/NSAP_PTR.py | |
93 | dns/rdtypes/IN/PX.py | |
94 | dns/rdtypes/IN/SRV.py | |
95 | dns/rdtypes/IN/WKS.py | |
96 | dns/rdtypes/IN/__init__.py | |
97 | dnspython.egg-info/PKG-INFO | |
98 | dnspython.egg-info/SOURCES.txt | |
99 | dnspython.egg-info/dependency_links.txt | |
100 | dnspython.egg-info/top_level.txt | |
101 | examples/ddns.py | |
102 | examples/e164.py | |
103 | examples/mx.py | |
104 | examples/name.py | |
105 | examples/reverse.py | |
106 | examples/reverse_name.py | |
107 | examples/xfr.py | |
108 | examples/zonediff.py | |
109 | tests/Makefile | |
110 | tests/__init__.py | |
111 | tests/example | |
112 | tests/example1.good | |
113 | tests/example2.good | |
114 | tests/example3.good | |
115 | tests/test_bugs.py | |
116 | tests/test_dnssec.py | |
117 | tests/test_exceptions.py | |
118 | tests/test_flags.py | |
119 | tests/test_generate.py | |
120 | tests/test_grange.py | |
121 | tests/test_message.py | |
122 | tests/test_name.py | |
123 | tests/test_namedict.py | |
124 | tests/test_ntoaaton.py | |
125 | tests/test_rdata.py | |
126 | tests/test_rdtypeandclass.py | |
127 | tests/test_rdtypeanydnskey.py | |
128 | tests/test_rdtypeanyeui.py | |
129 | tests/test_rdtypeanyloc.py | |
130 | tests/test_resolver.py | |
131 | tests/test_rrset.py | |
132 | tests/test_set.py | |
133 | tests/test_tokenizer.py | |
134 | tests/test_update.py | |
135 | tests/test_wiredata.py | |
136 | tests/test_zone.py | |
137 | tests/utest.py⏎ |
0 | _build |
0 | # Minimal makefile for Sphinx documentation | |
1 | # | |
2 | ||
3 | # You can set these variables from the command line. | |
4 | SPHINXOPTS = | |
5 | SPHINXBUILD = sphinx-build | |
6 | SPHINXPROJ = dnspython | |
7 | SOURCEDIR = . | |
8 | BUILDDIR = _build | |
9 | ||
10 | # Put it first so that "make" without argument is like "make help". | |
11 | help: | |
12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) | |
13 | ||
14 | .PHONY: help Makefile | |
15 | ||
16 | # Catch-all target: route all unknown targets to Sphinx using the new | |
17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). | |
18 | %: Makefile | |
19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)⏎ |
0 | .. _community: | |
1 | ||
2 | Community | |
3 | ========= | |
4 | ||
5 | Bugs and Feature Requests | |
6 | ------------------------- | |
7 | ||
8 | Bugs and feature requests can be made using the github issues system at | |
9 | `github <https://github.com/rthalley/dnspython/issues>`_. | |
10 | ||
11 | Mailing Lists | |
12 | ------------- | |
13 | ||
14 | | `dnspython-announce <http://groups.google.com/group/dnspython-announce>`_ | |
15 | | `dnspython-users <http://groups.google.com/group/dnspython-users>`_ | |
16 | | `dnspython-dev <http://groups.google.com/group/dnspython-dev>`_ |
0 | #!/usr/bin/env python3 | |
1 | # -*- coding: utf-8 -*- | |
2 | # | |
3 | # dnspython documentation build configuration file, created by | |
4 | # sphinx-quickstart on Fri Dec 30 05:55:44 2016. | |
5 | # | |
6 | # This file is execfile()d with the current directory set to its | |
7 | # containing dir. | |
8 | # | |
9 | # Note that not all possible configuration values are present in this | |
10 | # autogenerated file. | |
11 | # | |
12 | # All configuration values have a default; values that are commented out | |
13 | # serve to show the default. | |
14 | ||
15 | # If extensions (or modules to document with autodoc) are in another directory, | |
16 | # add these directories to sys.path here. If the directory is relative to the | |
17 | # documentation root, use os.path.abspath to make it absolute, like shown here. | |
18 | # | |
19 | import os | |
20 | import sys | |
21 | sys.path.insert(0, os.path.abspath('..')) | |
22 | ||
23 | ||
24 | # -- General configuration ------------------------------------------------ | |
25 | ||
26 | # If your documentation needs a minimal Sphinx version, state it here. | |
27 | # | |
28 | # needs_sphinx = '1.0' | |
29 | ||
30 | # Add any Sphinx extension module names here, as strings. They can be | |
31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom | |
32 | # ones. | |
33 | extensions = ['sphinx.ext.autodoc', | |
34 | 'sphinx.ext.viewcode', | |
35 | 'sphinx.ext.githubpages'] | |
36 | ||
37 | # Add any paths that contain templates here, relative to this directory. | |
38 | templates_path = ['_templates'] | |
39 | ||
40 | # The suffix(es) of source filenames. | |
41 | # You can specify multiple suffix as a list of string: | |
42 | # | |
43 | # source_suffix = ['.rst', '.md'] | |
44 | source_suffix = '.rst' | |
45 | ||
46 | # The master toctree document. | |
47 | master_doc = 'index' | |
48 | ||
49 | # General information about the project. | |
50 | project = 'dnspython' | |
51 | copyright = '2016, Nominum, Inc.' | |
52 | author = 'Nominum, Inc.' | |
53 | ||
54 | # The version info for the project you're documenting, acts as replacement for | |
55 | # |version| and |release|, also used in various other places throughout the | |
56 | # built documents. | |
57 | # | |
58 | # The short X.Y version. | |
59 | version = '1.16' | |
60 | # The full version, including alpha/beta/rc tags. | |
61 | release = '1.16.0' | |
62 | ||
63 | # The language for content autogenerated by Sphinx. Refer to documentation | |
64 | # for a list of supported languages. | |
65 | # | |
66 | # This is also used if you do content translation via gettext catalogs. | |
67 | # Usually you set "language" from the command line for these cases. | |
68 | language = None | |
69 | ||
70 | # List of patterns, relative to source directory, that match files and | |
71 | # directories to ignore when looking for source files. | |
72 | # This patterns also effect to html_static_path and html_extra_path | |
73 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] | |
74 | ||
75 | # The name of the Pygments (syntax highlighting) style to use. | |
76 | pygments_style = 'sphinx' | |
77 | ||
78 | # If true, `todo` and `todoList` produce output, else they produce nothing. | |
79 | todo_include_todos = False | |
80 | ||
81 | # -- Options for autodoc -------------------------------------------------- | |
82 | ||
83 | autoclass_content = 'both' | |
84 | ||
85 | # -- Options for HTML output ---------------------------------------------- | |
86 | ||
87 | # The theme to use for HTML and HTML Help pages. See the documentation for | |
88 | # a list of builtin themes. | |
89 | # | |
90 | html_theme = 'alabaster' | |
91 | ||
92 | # Theme options are theme-specific and customize the look and feel of a theme | |
93 | # further. For a list of options available for each theme, see the | |
94 | # documentation. | |
95 | # | |
96 | # html_theme_options = {} | |
97 | ||
98 | # Add any paths that contain custom static files (such as style sheets) here, | |
99 | # relative to this directory. They are copied after the builtin static files, | |
100 | # so a file named "default.css" will overwrite the builtin "default.css". | |
101 | html_static_path = ['_static'] | |
102 | ||
103 | # -- Options for HTMLHelp output ------------------------------------------ | |
104 | ||
105 | # Output file base name for HTML help builder. | |
106 | htmlhelp_basename = 'dnspythondoc' | |
107 | ||
108 | ||
109 | # -- Options for LaTeX output --------------------------------------------- | |
110 | ||
111 | latex_elements = { | |
112 | # The paper size ('letterpaper' or 'a4paper'). | |
113 | # | |
114 | # 'papersize': 'letterpaper', | |
115 | ||
116 | # The font size ('10pt', '11pt' or '12pt'). | |
117 | # | |
118 | # 'pointsize': '10pt', | |
119 | ||
120 | # Additional stuff for the LaTeX preamble. | |
121 | # | |
122 | # 'preamble': '', | |
123 | ||
124 | # Latex figure (float) alignment | |
125 | # | |
126 | # 'figure_align': 'htbp', | |
127 | } | |
128 | ||
129 | # Grouping the document tree into LaTeX files. List of tuples | |
130 | # (source start file, target name, title, | |
131 | # author, documentclass [howto, manual, or own class]). | |
132 | latex_documents = [ | |
133 | (master_doc, 'dnspython.tex', 'dnspython Documentation', | |
134 | 'Nominum, Inc.', 'manual'), | |
135 | ] | |
136 | ||
137 | ||
138 | # -- Options for manual page output --------------------------------------- | |
139 | ||
140 | # One entry per manual page. List of tuples | |
141 | # (source start file, name, description, authors, manual section). | |
142 | man_pages = [ | |
143 | (master_doc, 'dnspython', 'dnspython Documentation', | |
144 | [author], 1) | |
145 | ] | |
146 | ||
147 | ||
148 | # -- Options for Texinfo output ------------------------------------------- | |
149 | ||
150 | # Grouping the document tree into Texinfo files. List of tuples | |
151 | # (source start file, target name, title, author, | |
152 | # dir menu entry, description, category) | |
153 | texinfo_documents = [ | |
154 | (master_doc, 'dnspython', 'dnspython Documentation', | |
155 | author, 'dnspython', 'One line description of project.', | |
156 | 'Miscellaneous'), | |
157 | ] | |
158 | ||
159 | ||
160 |
0 | .. module:: dns.dnssec | |
1 | .. _dnssec: | |
2 | ||
3 | DNSSEC | |
4 | ====== | |
5 | ||
6 | Dnspython can do simple DNSSEC signature validation, but currently has no | |
7 | facilities for signing. In order to use DNSSEC functions, you must have | |
8 | ``pycryptodome`` or ``pycryptodomex`` installed. If you want to do elliptic | |
9 | curves, you must also have ``ecdsa`` installed. | |
10 | ||
11 | DNSSEC Algorithms | |
12 | ----------------- | |
13 | ||
14 | .. autodata:: dns.dnssec.RSAMD5 | |
15 | .. autodata:: dns.dnssec.DH | |
16 | .. autodata:: dns.dnssec.DSA | |
17 | .. autodata:: dns.dnssec.ECC | |
18 | .. autodata:: dns.dnssec.RSASHA1 | |
19 | .. autodata:: dns.dnssec.DSANSEC3SHA1 | |
20 | .. autodata:: dns.dnssec.RSASHA1NSEC3SHA1 | |
21 | .. autodata:: dns.dnssec.RSASHA256 | |
22 | .. autodata:: dns.dnssec.RSASHA512 | |
23 | .. autodata:: dns.dnssec.ECDSAP256SHA256 | |
24 | .. autodata:: dns.dnssec.ECDSAP384SHA384 | |
25 | .. autodata:: dns.dnssec.INDIRECT | |
26 | .. autodata:: dns.dnssec.PRIVATEDNS | |
27 | .. autodata:: dns.dnssec.PRIVATEOID | |
28 | ||
29 | DNSSEC Functions | |
30 | ---------------- | |
31 | ||
32 | .. autofunction:: dns.dnssec.algorithm_from_text | |
33 | .. autofunction:: dns.dnssec.algorithm_to_text | |
34 | .. autofunction:: dns.dnssec.key_id | |
35 | .. autofunction:: dns.dnssec.make_ds | |
36 | .. autofunction:: dns.dnssec.validate | |
37 | .. autofunction:: dns.dnssec.validate_rrsig |
0 | .. _exceptions: | |
1 | ||
2 | Exceptions | |
3 | ========== | |
4 | ||
5 | Common Exceptions | |
6 | ----------------- | |
7 | ||
8 | .. automodule:: dns.exception | |
9 | :members: | |
10 | ||
11 | dns.dnssec Exceptions | |
12 | --------------------- | |
13 | ||
14 | .. autoexception:: dns.dnssec.UnsupportedAlgorithm | |
15 | .. autoexception:: dns.dnssec.ValidationFailure | |
16 | ||
17 | ||
18 | dns.message Exceptions | |
19 | ---------------------- | |
20 | ||
21 | .. autoexception:: dns.message.BadEDNS | |
22 | .. autoexception:: dns.message.BadTSIG | |
23 | .. autoexception:: dns.message.ShortHeader | |
24 | .. autoexception:: dns.message.TrailingJunk | |
25 | .. autoexception:: dns.message.UnknownHeaderField | |
26 | .. autoexception:: dns.message.UnknownTSIGKey | |
27 | ||
28 | dns.name Exceptions | |
29 | ------------------- | |
30 | ||
31 | .. autoexception:: dns.name.AbsoluteConcatenation | |
32 | .. autoexception:: dns.name.BadEscape | |
33 | .. autoexception:: dns.name.BadLabelType | |
34 | .. autoexception:: dns.name.BadPointer | |
35 | .. autoexception:: dns.name.EmptyLabel | |
36 | .. autoexception:: dns.name.IDNAException | |
37 | .. autoexception:: dns.name.LabelTooLong | |
38 | .. autoexception:: dns.name.NameTooLong | |
39 | .. autoexception:: dns.name.NeedAbsoluteNameOrOrigin | |
40 | .. autoexception:: dns.name.NoIDNA2008 | |
41 | .. autoexception:: dns.name.NoParent | |
42 | ||
43 | dns.opcode Exceptions | |
44 | --------------------- | |
45 | ||
46 | .. autoexception:: dns.opcode.UnknownOpcode | |
47 | ||
48 | dns.query Exceptions | |
49 | -------------------- | |
50 | ||
51 | .. autoexception:: dns.query.BadResponse | |
52 | .. autoexception:: dns.query.UnexpectedSource | |
53 | .. autoexception:: dns.query.TransferError | |
54 | ||
55 | ||
56 | dns.rcode Exceptions | |
57 | -------------------- | |
58 | ||
59 | .. autoexception:: dns.rcode.UnknownRcode | |
60 | ||
61 | dns.rdataset Exceptions | |
62 | ----------------------- | |
63 | ||
64 | .. autoexception:: dns.rdataset.DifferingCovers | |
65 | .. autoexception:: dns.rdataset.IncompatibleTypes | |
66 | ||
67 | dns.resolver Exceptions | |
68 | ----------------------- | |
69 | ||
70 | .. autoexception:: dns.resolver.NoAnswer | |
71 | .. autoexception:: dns.resolver.NoMetaqueries | |
72 | .. autoexception:: dns.resolver.NoNameservers | |
73 | .. autoexception:: dns.resolver.NoRootSOA | |
74 | .. autoexception:: dns.resolver.NotAbsolute | |
75 | .. autoexception:: dns.resolver.NXDOMAIN | |
76 | .. autoexception:: dns.resolver.YXDOMAIN | |
77 | ||
78 | dns.tokenizer Exceptions | |
79 | ------------------------ | |
80 | ||
81 | .. autoexception:: dns.tokenizer.UngetBufferFull | |
82 | ||
83 | dns.ttl Exceptions | |
84 | ------------------ | |
85 | ||
86 | .. autoexception:: dns.ttl.BadTTL |
0 | .. dnspython documentation master file | |
1 | ||
2 | dnspython | |
3 | ========= | |
4 | ||
5 | Dnspython is a DNS toolkit for Python. It can be used for queries, | |
6 | zone transfers, dynamic updates, nameserver testing, and many other | |
7 | things. | |
8 | ||
9 | Dnspython provides both high and low level access to the DNS. The high | |
10 | level classes perform queries for data of a given name, type, and | |
11 | class, and return an answer set. The low level classes allow direct | |
12 | manipulation of DNS zones, messages, names, and records. Almost all | |
13 | RR types are supported. | |
14 | ||
15 | dnspython originated at Nominum where it was developed for testing DNS | |
16 | nameservers. Nominum has generously allowed it to be open sourced | |
17 | under a BSD-style license, and helps support its future development by | |
18 | continuing to employ the author. | |
19 | ||
20 | .. toctree:: | |
21 | :maxdepth: 1 | |
22 | :caption: Contents: | |
23 | ||
24 | whatsnew | |
25 | community | |
26 | installation | |
27 | manual | |
28 | rfc | |
29 | ||
30 | Indices and tables | |
31 | ================== | |
32 | ||
33 | * :ref:`genindex` | |
34 | * :ref:`modindex` | |
35 | * :ref:`search` |
0 | .. _installation: | |
1 | ||
2 | Installation | |
3 | ============ | |
4 | ||
5 | Requirements | |
6 | ------------ | |
7 | ||
8 | Python 2.7, 3.4 or later. | |
9 | ||
10 | Installation | |
11 | ------------ | |
12 | ||
13 | Many free operating system distributions have dnspython packaged for | |
14 | you, so you should check there first. | |
15 | ||
16 | The next easiest option is to use ``pip``:: | |
17 | ||
18 | pip install dnspython | |
19 | ||
20 | If ``pip`` is not available, you can download the latest zip file from | |
21 | `PyPI <https://pypi.python.org/pypi/dnspython/>`_, unzip it. | |
22 | ||
23 | On a UNIX-like system, you then run:: | |
24 | ||
25 | sudo python setup.py install | |
26 | ||
27 | while on a Windows system you would run:: | |
28 | ||
29 | python setup.py install | |
30 | ||
31 | Finally, you have the option of cloning the dnspython source from github | |
32 | and building it:: | |
33 | ||
34 | git clone https://github.com/rthalley/dnspython.git | |
35 | ||
36 | And then run ``setup.py`` as above. | |
37 | ||
38 | Please be aware that the master branch of dnspython on github is under | |
39 | active development and may not always be stable. | |
40 | ||
41 | ||
42 | Optional Modules | |
43 | ---------------- | |
44 | ||
45 | The following modules are optional, but recommended for full functionality. | |
46 | ||
47 | If ``pycryptodome`` / ``pycryptodomex`` is installed, then dnspython will be | |
48 | able to do low-level DNSSEC RSA and DSA signature validation. | |
49 | ||
50 | If ``ecdsa`` is installed, then Elliptic Curve signature algorithms will | |
51 | be available for low-level DNSSEC signature validation. | |
52 | ||
53 | If ``idna`` is installed, then IDNA 2008 will be available. |
0 | Dnspython Manual | |
1 | ================ | |
2 | ||
3 | .. toctree:: | |
4 | :maxdepth: 2 | |
5 | :caption: Contents: | |
6 | ||
7 | py2vs3 | |
8 | name | |
9 | rdata | |
10 | message | |
11 | dnssec | |
12 | query | |
13 | resolver | |
14 | exceptions | |
15 | utilities |
0 | .. _message-class: | |
1 | ||
2 | The dns.message.Message Class | |
3 | ----------------------------- | |
4 | ||
5 | .. autoclass:: dns.message.Message | |
6 | :members: | |
7 | ||
8 | .. attribute:: id | |
9 | ||
10 | An ``int``, the query id; the default is a randomly chosen id. | |
11 | ||
12 | .. attribute:: flags | |
13 | ||
14 | An ``int``, the DNS flags of the message. | |
15 | ||
16 | .. attribute:: question | |
17 | ||
18 | The question section, a list of ``dns.rrset.RRset`` objects. | |
19 | ||
20 | .. attribute:: answer | |
21 | ||
22 | The answer section, a list of ``dns.rrset.RRset`` objects. | |
23 | ||
24 | .. attribute:: authority | |
25 | ||
26 | The authority section, a list of ``dns.rrset.RRset`` objects. | |
27 | ||
28 | .. attribute:: additional | |
29 | ||
30 | The additional section, a list of ``dns.rrset.RRset`` objects. | |
31 | ||
32 | .. attribute:: edns | |
33 | ||
34 | An ``int``, the EDNS level to use. The default is -1, no EDNS. | |
35 | ||
36 | .. attribute:: ednsflags | |
37 | ||
38 | An ``int``, the EDNS flags. | |
39 | ||
40 | .. attribute:: payload | |
41 | ||
42 | An ``int``, the EDNS payload size. The default is 0. | |
43 | ||
44 | .. attribute:: options | |
45 | ||
46 | The EDNS options, a list of ``dns.edns.Option`` objects. The default | |
47 | is the empty list. | |
48 | ||
49 | .. attribute:: request_payload | |
50 | ||
51 | The associated request's EDNS payload size. This field is meaningful | |
52 | in response messages, and if set to a non-zero value, will limit | |
53 | the size of the response to the specified size. The default is 0, | |
54 | which means "use the default limit" which is currently 65535. | |
55 | ||
56 | .. attribute:: keyring | |
57 | ||
58 | The TSIG keyring to use. The default is `None`. A TSIG keyring | |
59 | is a dictionary mapping from TSIG key name, a ``dns.name.Name``, to | |
60 | a TSIG secret, a ``binary``. | |
61 | ||
62 | .. attribute:: keyname | |
63 | ||
64 | The TSIG keyname to use, a ``dns.name.Name``. The default is ``None``. | |
65 | ||
66 | .. attribute:: keyalgorithm | |
67 | ||
68 | A ``dns.name.Name``, the TSIG algorithm to use. Defaults to | |
69 | ``dns.tsig.default_algorithm``. Constants for TSIG algorithms are | |
70 | defined the in ``dns.tsig`` module. | |
71 | ||
72 | .. attribute:: request_mac | |
73 | ||
74 | A ``binary``, the TSIG MAC of the request message associated with | |
75 | this message; used when validating TSIG signatures. | |
76 | ||
77 | .. attribute:: fudge | |
78 | ||
79 | An ``int``, the TSIG time fudge. The default is 300 seconds. | |
80 | ||
81 | .. attribute:: original_id | |
82 | ||
83 | An ``int``, the TSIG original id; defaults to the message's id. | |
84 | ||
85 | .. attribute:: tsig_error | |
86 | ||
87 | An ``int``, the TSIG error code. The default is 0. | |
88 | ||
89 | .. attribute:: other_data | |
90 | ||
91 | A ``binary``, the TSIG "other data". The default is the empty | |
92 | ``binary``. | |
93 | ||
94 | .. attribute:: mac | |
95 | ||
96 | A ``binary``, the TSIG MAC for this message. | |
97 | ||
98 | .. attribute:: xfr | |
99 | ||
100 | A ``bool``. This attribute is true when the message being used | |
101 | for the results of a DNS zone transfer. The default is ``False``. | |
102 | ||
103 | .. attribute:: origin | |
104 | ||
105 | A ``dns.name.Name``. The origin of the zone in messages which are | |
106 | used for zone transfers or for DNS dynamic updates. The default | |
107 | is ``None``. | |
108 | ||
109 | .. attribute:: tsig_ctx | |
110 | ||
111 | An ``hmac.HMAC``, the TSIG signature context associated with this | |
112 | message. The default is ``None``. | |
113 | ||
114 | .. attribute:: had_tsig | |
115 | ||
116 | A ``bool``, which is ``True`` if the message had a TSIG signature | |
117 | when it was decoded from wire format. | |
118 | ||
119 | .. attribute:: multi | |
120 | ||
121 | A ``bool``, which is ``True`` if this message is part of a | |
122 | multi-message sequence. The default is ``False``. | |
123 | This attribute is used when validating TSIG signatures | |
124 | on messages which are part of a zone transfer. | |
125 | ||
126 | .. attribute:: first | |
127 | ||
128 | A ``bool``, which is ``True`` if this message is stand-alone, | |
129 | or the first of a multi-message sequence. The default is ``True``. | |
130 | This variable is used when validating TSIG signatures | |
131 | on messages which are part of a zone transfer. | |
132 | ||
133 | .. attribute:: index | |
134 | ||
135 | A ``dict``, an index of RRsets in the message. The index key is | |
136 | ``(section, name, rdclass, rdtype, covers, deleting)``. The default | |
137 | is ``{}``. Indexing improves the performance of finding RRsets. | |
138 | Indexing can be disabled by setting the index to ``None``. | |
139 | ||
140 | The following constants may be used to specify sections in the | |
141 | ``find_rrset()`` and ``get_rrset()`` methods: | |
142 | ||
143 | .. autodata:: dns.message.QUESTION | |
144 | .. autodata:: dns.message.ANSWER | |
145 | .. autodata:: dns.message.AUTHORITY | |
146 | .. autodata:: dns.message.ADDITIONAL |
0 | .. _message-edns: | |
1 | ||
2 | Message EDNS Options | |
3 | -------------------- | |
4 | ||
5 | EDNS allows for larger messages and also provides an extension | |
6 | mechanism for the protocol. EDNS *options* are typed data, and are | |
7 | treated much like Rdata. For example, if dnsython encouters the EDNS | |
8 | ``ECS`` option code when parsing a DNS wire format message, it | |
9 | will create a ``dns.edns.ECSOption`` object to represent it. | |
10 | ||
11 | .. autodata:: dns.edns.NSID | |
12 | .. autodata:: dns.edns.DAU | |
13 | .. autodata:: dns.edns.DHU | |
14 | .. autodata:: dns.edns.N3U | |
15 | .. autodata:: dns.edns.ECS | |
16 | .. autodata:: dns.edns.EXPIRE | |
17 | .. autodata:: dns.edns.COOKIE | |
18 | .. autodata:: dns.edns.KEEPALIVE | |
19 | .. autodata:: dns.edns.PADDING | |
20 | .. autodata:: dns.edns.CHAIN | |
21 | ||
22 | .. autoclass:: dns.edns.Option | |
23 | :members: | |
24 | ||
25 | .. autoclass:: dns.edns.GenericOption | |
26 | :members: | |
27 | ||
28 | .. autoclass:: dns.edns.ECSOption | |
29 | :members: | |
30 | ||
31 | .. autofunction:: dns.edns.get_option_class | |
32 | .. autofunction:: dns.edns.option_from_wire |
0 | .. _message-flags: | |
1 | ||
2 | Message Flags | |
3 | ============= | |
4 | ||
5 | DNS message flags are used for signalling of various kinds | |
6 | in the DNS protocol. For example, the ``QR`` flag indicates | |
7 | that a message is a response to a prior query. | |
8 | ||
9 | Messages flags are encoded in two locations: the DNS header | |
10 | and the EDNS flags field. | |
11 | ||
12 | Header Flags | |
13 | ------------ | |
14 | ||
15 | .. autodata:: dns.flags.QR | |
16 | .. autodata:: dns.flags.AA | |
17 | .. autodata:: dns.flags.TC | |
18 | .. autodata:: dns.flags.RD | |
19 | .. autodata:: dns.flags.RA | |
20 | .. autodata:: dns.flags.AD | |
21 | .. autodata:: dns.flags.CD | |
22 | ||
23 | .. autofunction:: dns.flags.from_text | |
24 | .. autofunction:: dns.flags.to_text | |
25 | ||
26 | EDNS Flags | |
27 | ---------- | |
28 | ||
29 | .. autodata:: dns.flags.DO | |
30 | ||
31 | .. autofunction:: dns.flags.edns_from_text | |
32 | .. autofunction:: dns.flags.edns_to_text |
0 | .. _message-make: | |
1 | ||
2 | Making DNS Messages | |
3 | ------------------- | |
4 | ||
5 | .. autofunction:: dns.message.from_file | |
6 | .. autofunction:: dns.message.from_text | |
7 | .. autofunction:: dns.message.from_wire | |
8 | .. autofunction:: dns.message.make_query | |
9 | .. autofunction:: dns.message.make_response |
0 | .. _message-opcode: | |
1 | ||
2 | Message Opcodes | |
3 | --------------- | |
4 | ||
5 | DNS Opcodes describe what kind of operation a DNS message is requesting | |
6 | or replying to. Opcodes are embedded in the flags field in the DNS | |
7 | header. | |
8 | ||
9 | .. autodata:: dns.opcode.QUERY | |
10 | .. autodata:: dns.opcode.IQUERY | |
11 | .. autodata:: dns.opcode.STATUS | |
12 | .. autodata:: dns.opcode.NOTIFY | |
13 | .. autodata:: dns.opcode.UPDATE | |
14 | ||
15 | .. autofunction:: dns.opcode.from_text | |
16 | .. autofunction:: dns.opcode.to_text | |
17 | .. autofunction:: dns.opcode.from_flags | |
18 | .. autofunction:: dns.opcode.to_flags | |
19 | .. autofunction:: dns.opcode.is_update |
0 | .. _message-rcode: | |
1 | ||
2 | Message Rcodes | |
3 | -------------- | |
4 | ||
5 | A DNS Rcode describes the result of a DNS request. If EDNS is not in | |
6 | use, then the rcode is encoded solely in the DNS header. If EDNS is | |
7 | in use, then the rcode is encoded using bits form both the header and | |
8 | the EDNS OPT RR. | |
9 | ||
10 | .. autodata:: dns.rcode.NOERROR | |
11 | .. autodata:: dns.rcode.FORMERR | |
12 | .. autodata:: dns.rcode.SERVFAIL | |
13 | .. autodata:: dns.rcode.NXDOMAIN | |
14 | .. autodata:: dns.rcode.NOTIMP | |
15 | .. autodata:: dns.rcode.REFUSED | |
16 | .. autodata:: dns.rcode.YXDOMAIN | |
17 | .. autodata:: dns.rcode.YXRRSET | |
18 | .. autodata:: dns.rcode.NXRRSET | |
19 | .. autodata:: dns.rcode.NOTAUTH | |
20 | .. autodata:: dns.rcode.NOTZONE | |
21 | .. autodata:: dns.rcode.BADVERS | |
22 | ||
23 | .. autofunction:: dns.rcode.from_text | |
24 | .. autofunction:: dns.rcode.to_text | |
25 | .. autofunction:: dns.rcode.from_flags | |
26 | .. autofunction:: dns.rcode.to_flags |
0 | .. _message-update: | |
1 | ||
2 | The dns.update.Update Class | |
3 | --------------------------- | |
4 | ||
5 | DNS Dynamic Update message have a complex encoding, so | |
6 | dnspython provides a subclass of ``dns.message.Message`` | |
7 | specialized for creating them. | |
8 | ||
9 | .. autoclass:: dns.update.Update | |
10 | :members: |
0 | .. module:: dns.message | |
1 | .. _message: | |
2 | ||
3 | DNS Messages | |
4 | ============ | |
5 | ||
6 | Objects of the dns.message.Message class represent a single DNS message, | |
7 | as defined by `RFC 1035 <https://tools.ietf.org/html/rfc1035>`_ and its | |
8 | many updates and extensions. | |
9 | ||
10 | The module provides tools for constructing and manipulating messages. | |
11 | TSIG signatures and EDNS are also supported. Messages can be dumped to | |
12 | a textual form, and also read from that form. | |
13 | ||
14 | .. toctree:: | |
15 | ||
16 | message-class | |
17 | message-make | |
18 | message-flags | |
19 | message-opcode | |
20 | message-rcode | |
21 | message-edns | |
22 | message-update |
0 | .. _name-class: | |
1 | ||
2 | The dns.name.Name Class and Predefined Names | |
3 | -------------------------------------------- | |
4 | ||
5 | .. autoclass:: dns.name.Name | |
6 | :members: | |
7 | ||
8 | .. attribute:: labels | |
9 | ||
10 | A tuple of ``binary`` in DNS wire format specifying the DNS | |
11 | labels in the name, in order from least-signficiant label | |
12 | (i.e. farthest from the origin) to most-significant label. | |
13 | ||
14 | .. data:: dns.name.root | |
15 | ||
16 | The root name, i.e. ``dns.name.Name([b''])``. | |
17 | ||
18 | .. data:: dns.name.empty | |
19 | ||
20 | The empty name, i.e. ``dns.name.Name([])``. |
0 | .. _name-codecs: | |
1 | ||
2 | International Domain Name CODECs | |
3 | -------------------------------- | |
4 | ||
5 | Representing non-ASCII text in the DNS is a complex and evolving | |
6 | topic. Generally speaking, Unicode is converted into an ASCII-only, | |
7 | case-insensitive form called "Punycode" by complex rules. There are | |
8 | two standard specifications for this process, "IDNA 2003", which is | |
9 | widely used, and the revised and not fully compatible standard "IDNA | |
10 | 2008". There are also varying degrees of strictness that can be applied | |
11 | in encoding and decoding. Explaining the standards in detail is | |
12 | out of scope for this document; Unicode Technical Standard #46 | |
13 | http://unicode.org/reports/tr46/ is a good place to start learning more. | |
14 | ||
15 | Dnspython provides "codecs" to implement International Domain Name policy | |
16 | according to the user's desire. | |
17 | ||
18 | .. autoclass:: dns.name.IDNACodec | |
19 | :members: | |
20 | .. autoclass:: dns.name.IDNA2003Codec | |
21 | :members: | |
22 | .. autoclass:: dns.name.IDNA2008Codec | |
23 | :members: | |
24 | ||
25 | .. data:: dns.name.IDNA_2003_Practical | |
26 | ||
27 | The "practical" codec encodes using IDNA 2003 rules and decodes | |
28 | punycode without checking for strict IDNA 2003 compliance. | |
29 | ||
30 | .. data:: dns.name.IDNA_2003_Strict | |
31 | ||
32 | The "strict" codec encodes using IDNA 2003 rules and decodes | |
33 | punycode checking for IDNA 2003 compliance. | |
34 | ||
35 | .. data:: dns.name.IDNA_2003 | |
36 | ||
37 | A synonym for ``dns.name.IDNA_2003_Practical``. | |
38 | ||
39 | .. data:: dns.name.IDNA_2008_Practical | |
40 | ||
41 | The "practical" codec encodes using IDNA 2008 rules with UTS 46 | |
42 | compatibility processing, and allowing pure ASCII labels. It | |
43 | decodes punycode without checking for strict IDNA 2008 compliance. | |
44 | ||
45 | .. data:: dns.name.IDNA_2008_Strict | |
46 | ||
47 | The "strict" codec encodes using IDNA 2008 rules and decodes | |
48 | punycode checking for IDNA 2008 compliance. | |
49 | ||
50 | .. data:: dns.name.IDNA_2008_UTS_46 | |
51 | ||
52 | The "UTS 46" codec encodes using IDNA 2008 rules with UTS 46 | |
53 | compatibility processing and decodes punycode without checking for | |
54 | IDNA 2008 compliance. | |
55 | ||
56 | .. data:: dns.name.IDNA_2008_Transitional | |
57 | ||
58 | The "UTS 46" codec encodes using IDNA 2008 rules with UTS 46 | |
59 | compatibility processing in the "transitional mode" and decodes | |
60 | punycode without checking for IDNA 2008 compliance. | |
61 | ||
62 | .. data:: dns.name.IDNA_2008 | |
63 | ||
64 | A synonym for ``dns.name.IDNA_2008_Practical``. |
0 | .. _name-dict: | |
1 | ||
2 | Name Dictionary | |
3 | =============== | |
4 | ||
5 | .. autoclass:: dns.namedict.NameDict | |
6 | :members: |
0 | .. _name-helpers: | |
1 | ||
2 | Name Helpers | |
3 | ------------ | |
4 | ||
5 | Sometimes you want to look up an address in the DNS instead of a name. | |
6 | Dnspython provides a helper functions for converting between addresses | |
7 | and their "reverse map" form in the DNS. | |
8 | ||
9 | For example: | |
10 | ||
11 | ========= ========================================================================= | |
12 | Address DNS Reverse Name | |
13 | ========= ========================================================================= | |
14 | 127.0.0.1 1.0.0.127.in-addr.arpa. | |
15 | ::1 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. | |
16 | ========= ========================================================================= | |
17 | ||
18 | | | |
19 | ||
20 | .. autofunction:: dns.reversename.from_address | |
21 | .. autofunction:: dns.reversename.to_address | |
22 | ||
23 | Dnspython also provides helpers for converting E.164 numbers (i.e. | |
24 | telephone numbers) into the names used for them in the DNS. | |
25 | ||
26 | For example: | |
27 | ||
28 | ================ ================================== | |
29 | Number DNS E.164 Name | |
30 | ================ ================================== | |
31 | +1.650.555.1212 2.1.2.1.5.5.5.0.5.6.1.e164.arpa. | |
32 | +44 20 7946 0123 3.2.1.0.6.4.9.7.0.2.4.4.e164.arpa. | |
33 | ================ ================================== | |
34 | ||
35 | | | |
36 | ||
37 | .. autofunction:: dns.e164.from_e164 | |
38 | .. autofunction:: dns.e164.to_e164 | |
39 | ||
40 |
0 | .. _name-make: | |
1 | ||
2 | Making DNS Names | |
3 | ---------------- | |
4 | ||
5 | .. autofunction:: dns.name.from_text | |
6 | .. autofunction:: dns.name.from_unicode | |
7 | .. autofunction:: dns.name.from_wire |
0 | .. module:: dns.name | |
1 | .. _name: | |
2 | ||
3 | DNS Names | |
4 | ========= | |
5 | ||
6 | Objects of the dns.name.Name class represent an immutable domain name. | |
7 | The representation is a tuple of labels, with each lable being a ``binary`` | |
8 | object in the DNS wire format. Typically names are not created by | |
9 | supplying the labels tuple directly, but rather by converting from DNS | |
10 | text format or the DNS wire format. | |
11 | ||
12 | Labels are in the same order as in the DNS textual form, e.g. the labels | |
13 | value for ``www.dnspython.org.`` is ``(b'www', b'dnspython', b'org', b'')`` on | |
14 | Python 3, and ``('www', 'dnspython', 'org', '')`` on Python 2. | |
15 | ||
16 | Names may be *absolute* or *relative*. Absolute names end in the root label, | |
17 | which is an empty ``binary``. Relative names do not end in the root label. To | |
18 | convert a relative name to an absolute name requires specifying an *origin*. | |
19 | Typically the origin is known by context. Dnspython provides tools to | |
20 | relativize and derelativize names. It's a good idea not to mix relative | |
21 | and absolute names, other than in the context of a zone. Names encoded | |
22 | in the DNS wire protocol are always absolute. Dnspython's functions to | |
23 | make names from text also default to an origin of the root name, and thus | |
24 | to make a relative name using them you must specify an origin of None or | |
25 | ``dns.name.empty``. | |
26 | ||
27 | Names are compared and ordered according to the rules of the DNS. The | |
28 | order is the DNSSEC canonical ordering. Relative names always sort before | |
29 | absolute names. | |
30 | ||
31 | Names may also be compared according to the DNS tree hierarchy with | |
32 | the ``fullcompare()`` method. For example ```www.dnspython.org.`` is | |
33 | a subdomain of ``dnspython.org.``. See the method description for | |
34 | full details. | |
35 | ||
36 | .. toctree:: | |
37 | ||
38 | name-class | |
39 | name-make | |
40 | name-dict | |
41 | name-helpers | |
42 | name-codecs |
0 | Python 2 vs. Python 3 | |
1 | --------------------- | |
2 | ||
3 | Dnspython was originally written in Python 2, and for some years had a | |
4 | separate Python 3 branch. Thanks to some excellent work by | |
5 | contributors to the project, there is now a single source tree that | |
6 | works for both. | |
7 | ||
8 | The most significant user-visible differences between the two are in | |
9 | the representations of binary data and textual data. For Python 3, | |
10 | binary data is stored using the `bytes` type, and textual data is stored | |
11 | using the `str` type. For Python 2, binary data is stored using the | |
12 | `str` type, and textual data can use the `str` or `unicode` types. | |
13 | Because there is a single source tree, the documentation will refer to | |
14 | `binary` and `text` when describing the types of binary data or | |
15 | textual data, respectively. | |
16 | ||
17 |
0 | .. module:: dns.query | |
1 | .. _query: | |
2 | ||
3 | DNS Query Support | |
4 | ================= | |
5 | ||
6 | The ``dns.query`` module is for sending messages to DNS servers, and | |
7 | processing their responses. If you want "stub resolver" behavior, then | |
8 | you should use the higher level ``dns.resolver`` module; see :ref:`resolver`. | |
9 | ||
10 | For UDP and TCP, the module provides a single "do everything" query | |
11 | function, and also provides the send and receive halves of this function | |
12 | individually for situations where more sophisticated I/O handling is in | |
13 | being used by the application. | |
14 | ||
15 | UDP | |
16 | --- | |
17 | ||
18 | .. autofunction:: dns.query.udp | |
19 | .. autofunction:: dns.query.send_udp | |
20 | .. autofunction:: dns.query.receive_udp | |
21 | ||
22 | TCP | |
23 | --- | |
24 | ||
25 | .. autofunction:: dns.query.tcp | |
26 | .. autofunction:: dns.query.send_tcp | |
27 | .. autofunction:: dns.query.receive_tcp | |
28 | ||
29 | Zone Transfers | |
30 | -------------- | |
31 | ||
32 | .. autofunction:: dns.query.xfr |
0 | .. _rdata-class: | |
1 | ||
2 | DNS Rdata Base Class | |
3 | ==================== | |
4 | ||
5 | All Rdata objects are instances of some subclass of ``dns.rdata.Rdata``. | |
6 | The Rdata factory functions described in :ref:`rdata-make` will create | |
7 | objects which are instances of the most appropriate subclass. For example, | |
8 | a AAAA record will be an instance of the ``dns.rdtypes.IN.AAAA`` class, | |
9 | but a record of TYPE12345, which we don't know anything specific about, | |
10 | will be an instance of ``dns.rdata.GenericRdata``. | |
11 | ||
12 | .. autoclass:: dns.rdata.Rdata | |
13 | :members: |
0 | .. _rdata-make: | |
1 | ||
2 | Making DNS Rdata | |
3 | ---------------- | |
4 | ||
5 | .. autofunction:: dns.rdata.from_text | |
6 | .. autofunction:: dns.rdata.from_wire |
0 | .. _rdata-set-classes: | |
1 | ||
2 | Rdataset, RRset and Node Classes | |
3 | ================================ | |
4 | ||
5 | An ``Rdataset`` is a set of ``Rdata`` objects which all have the same | |
6 | rdatatype, rdataclass, and covered type. ``Rdatasets`` also have a | |
7 | ``ttl`` (DNS time-to-live) field. Rdatasets support the normal Python | |
8 | set API, but are also ordered. | |
9 | ||
10 | An ``RRset`` is a subclass of ``Rdataset`` that also has an owner | |
11 | name, i.e. a ``dns.name.Name`` that says where in the DNS tree this | |
12 | set is located. | |
13 | ||
14 | A ``Node`` is a set of ``Rdataset`` objects, the Rdatasets being | |
15 | interpreted as at the same place (i.e. same owner name) int the DNS | |
16 | tree. Nodes are primarily used in ``Zone`` objects. | |
17 | ||
18 | .. autoclass:: dns.rdataset.Rdataset | |
19 | :members: | |
20 | ||
21 | .. autoclass:: dns.rrset.RRset | |
22 | :members: | |
23 | ||
24 | .. autoclass:: dns.node.Node | |
25 | :members: |
0 | .. _rdata-make: | |
1 | ||
2 | Making DNS Rdatasets and RRsets | |
3 | =============================== | |
4 | ||
5 | .. autofunction:: dns.rdataset.from_text | |
6 | .. autofunction:: dns.rdataset.from_text_list | |
7 | .. autofunction:: dns.rdataset.from_rdata | |
8 | .. autofunction:: dns.rdataset.from_rdata_list | |
9 | .. autofunction:: dns.rrset.from_text | |
10 | .. autofunction:: dns.rrset.from_text_list | |
11 | .. autofunction:: dns.rrset.from_rdata | |
12 | .. autofunction:: dns.rrset.from_rdata_list |
0 | .. _rdata-subclasses: | |
1 | ||
2 | Rdata Subclass Reference | |
3 | ======================== | |
4 | ||
5 | XXXRTH This is just a placeholder for proper documentation of all | |
6 | the subclasses, which will basically just be documenting the attributes | |
7 | of the subclass, as well as any method specific to the subclass. | |
8 | ||
9 | .. autoclass:: dns.rdata.GenericRdata | |
10 | :members: | |
11 | ||
12 | .. automodule:: dns.rdtypes.dnskeybase | |
13 | :members: | |
14 | ||
15 | .. automodule:: dns.rdtypes.dsbase | |
16 | :members: | |
17 | ||
18 | .. automodule:: dns.rdtypes.euibase | |
19 | :members: | |
20 | ||
21 | .. automodule:: dns.rdtypes.mxbase | |
22 | :members: | |
23 | ||
24 | .. automodule:: dns.rdtypes.nsbase | |
25 | :members: | |
26 | ||
27 | .. automodule:: dns.rdtypes.txtbase | |
28 | :members: | |
29 | ||
30 | .. automodule:: dns.rdtypes.ANY.AFSDB | |
31 | :members: | |
32 | ||
33 | .. automodule:: dns.rdtypes.ANY.AVC | |
34 | :members: | |
35 | ||
36 | .. automodule:: dns.rdtypes.ANY.CAA | |
37 | :members: | |
38 | ||
39 | .. automodule:: dns.rdtypes.ANY.CDNSKEY | |
40 | :members: | |
41 | ||
42 | .. automodule:: dns.rdtypes.ANY.CDS | |
43 | :members: | |
44 | ||
45 | .. automodule:: dns.rdtypes.ANY.CERT | |
46 | :members: | |
47 | ||
48 | .. automodule:: dns.rdtypes.ANY.CNAME | |
49 | :members: | |
50 | ||
51 | .. automodule:: dns.rdtypes.ANY.CSYNC | |
52 | :members: | |
53 | ||
54 | .. automodule:: dns.rdtypes.ANY.DLV | |
55 | :members: | |
56 | ||
57 | .. automodule:: dns.rdtypes.ANY.DNAME | |
58 | :members: | |
59 | ||
60 | .. automodule:: dns.rdtypes.ANY.DNSKEY | |
61 | :members: | |
62 | ||
63 | .. automodule:: dns.rdtypes.ANY.DS | |
64 | :members: | |
65 | ||
66 | .. automodule:: dns.rdtypes.ANY.EUI48 | |
67 | :members: | |
68 | ||
69 | .. automodule:: dns.rdtypes.ANY.EUI64 | |
70 | :members: | |
71 | ||
72 | .. automodule:: dns.rdtypes.ANY.GPOS | |
73 | :members: | |
74 | ||
75 | .. automodule:: dns.rdtypes.ANY.HINFO | |
76 | :members: | |
77 | ||
78 | .. automodule:: dns.rdtypes.ANY.HIP | |
79 | :members: | |
80 | ||
81 | .. automodule:: dns.rdtypes.ANY.ISDN | |
82 | :members: | |
83 | ||
84 | .. automodule:: dns.rdtypes.ANY.LOC | |
85 | :members: | |
86 | ||
87 | .. automodule:: dns.rdtypes.ANY.MX | |
88 | :members: | |
89 | ||
90 | .. automodule:: dns.rdtypes.ANY.NS | |
91 | :members: | |
92 | ||
93 | .. automodule:: dns.rdtypes.ANY.NSEC | |
94 | :members: | |
95 | ||
96 | .. automodule:: dns.rdtypes.ANY.NSEC3 | |
97 | :members: | |
98 | ||
99 | .. automodule:: dns.rdtypes.ANY.NSEC3PARAM | |
100 | :members: | |
101 | ||
102 | .. automodule:: dns.rdtypes.ANY.OPENPGPKEY | |
103 | :members: | |
104 | ||
105 | .. automodule:: dns.rdtypes.ANY.PTR | |
106 | :members: | |
107 | ||
108 | .. automodule:: dns.rdtypes.ANY.RP | |
109 | :members: | |
110 | ||
111 | .. automodule:: dns.rdtypes.ANY.RRSIG | |
112 | :members: | |
113 | ||
114 | .. automodule:: dns.rdtypes.ANY.RT | |
115 | :members: | |
116 | ||
117 | .. automodule:: dns.rdtypes.ANY.SOA | |
118 | :members: | |
119 | ||
120 | .. automodule:: dns.rdtypes.ANY.SPF | |
121 | :members: | |
122 | ||
123 | .. automodule:: dns.rdtypes.ANY.SSHFP | |
124 | :members: | |
125 | ||
126 | .. automodule:: dns.rdtypes.ANY.TLSA | |
127 | :members: | |
128 | ||
129 | .. automodule:: dns.rdtypes.ANY.TXT | |
130 | :members: | |
131 | ||
132 | .. automodule:: dns.rdtypes.ANY.URI | |
133 | :members: | |
134 | ||
135 | .. automodule:: dns.rdtypes.ANY.X25 | |
136 | :members: | |
137 | ||
138 | .. automodule:: dns.rdtypes.IN.A | |
139 | :members: | |
140 | ||
141 | .. automodule:: dns.rdtypes.IN.AAAA | |
142 | :members: | |
143 | ||
144 | .. automodule:: dns.rdtypes.IN.APL | |
145 | :members: | |
146 | ||
147 | .. automodule:: dns.rdtypes.IN.DHCID | |
148 | :members: | |
149 | ||
150 | .. automodule:: dns.rdtypes.IN.IPSECKEY | |
151 | :members: | |
152 | ||
153 | .. automodule:: dns.rdtypes.IN.KX | |
154 | :members: | |
155 | ||
156 | .. automodule:: dns.rdtypes.IN.NAPTR | |
157 | :members: | |
158 | ||
159 | .. automodule:: dns.rdtypes.IN.NSAP | |
160 | :members: | |
161 | ||
162 | .. automodule:: dns.rdtypes.IN.NSAP_PTR | |
163 | :members: | |
164 | ||
165 | .. automodule:: dns.rdtypes.IN.PX | |
166 | :members: | |
167 | ||
168 | .. automodule:: dns.rdtypes.IN.SRV | |
169 | :members: | |
170 | ||
171 | .. automodule:: dns.rdtypes.IN.WKS | |
172 | :members: | |
173 |
0 | .. _rdata-types: | |
1 | ||
2 | Rdata classes and types | |
3 | ----------------------- | |
4 | ||
5 | Sets of typed data can be associated with a given name. A single typed | |
6 | datum is called an *rdata*. The type of an rdata is specified by its | |
7 | *rdataclass* and *rdatatype*. The class is almost always `IN`, the Internet | |
8 | class, and may often be omitted in the dnspython APIs. | |
9 | ||
10 | The ``dns.rdataclass`` module provides constants for each defined | |
11 | rdata class, as well as some helpful functions. The ``dns.rdatatype`` | |
12 | module does the same for rdata types. Examples of the constants are:: | |
13 | ||
14 | dns.rdataclass.IN | |
15 | dns.rdatatype.AAAA | |
16 | ||
17 | .. automodule:: dns.rdataclass | |
18 | :members: | |
19 | ||
20 | .. automodule:: dns.rdatatype | |
21 | :members: | |
22 | ||
23 | .. toctree:: | |
24 | rdataclass-list | |
25 | rdatatype-list |
0 | .. _rdata: | |
1 | ||
2 | DNS Rdata | |
3 | ========= | |
4 | ||
5 | .. toctree:: | |
6 | ||
7 | rdata-types | |
8 | rdata-class | |
9 | rdata-make | |
10 | rdata-subclasses | |
11 | rdata-set-classes | |
12 | rdata-set-make |
0 | Rdataclasses | |
1 | ============ | |
2 | ||
3 | .. py:data:: dns.rdataclass.ANY | |
4 | :annotation: = 255 | |
5 | .. py:data:: dns.rdataclass.CH | |
6 | :annotation: = 3 | |
7 | .. py:data:: dns.rdataclass.CHAOS | |
8 | :annotation: = 3 | |
9 | .. py:data:: dns.rdataclass.HESIOD | |
10 | :annotation: = 4 | |
11 | .. py:data:: dns.rdataclass.HS | |
12 | :annotation: = 4 | |
13 | .. py:data:: dns.rdataclass.IN | |
14 | :annotation: = 1 | |
15 | .. py:data:: dns.rdataclass.INTERNET | |
16 | :annotation: = 1 | |
17 | .. py:data:: dns.rdataclass.NONE | |
18 | :annotation: = 254 | |
19 | .. py:data:: dns.rdataclass.RESERVED0 | |
20 | :annotation: = 0 |
0 | Rdatatypes | |
1 | ========== | |
2 | ||
3 | .. py:data:: dns.rdatatype.A | |
4 | :annotation: = 1 | |
5 | .. py:data:: dns.rdatatype.A6 | |
6 | :annotation: = 38 | |
7 | .. py:data:: dns.rdatatype.AAAA | |
8 | :annotation: = 28 | |
9 | .. py:data:: dns.rdatatype.AFSDB | |
10 | :annotation: = 18 | |
11 | .. py:data:: dns.rdatatype.ANY | |
12 | :annotation: = 255 | |
13 | .. py:data:: dns.rdatatype.APL | |
14 | :annotation: = 42 | |
15 | .. py:data:: dns.rdatatype.AVC | |
16 | :annotation: = 258 | |
17 | .. py:data:: dns.rdatatype.AXFR | |
18 | :annotation: = 252 | |
19 | .. py:data:: dns.rdatatype.CAA | |
20 | :annotation: = 257 | |
21 | .. py:data:: dns.rdatatype.CDNSKEY | |
22 | :annotation: = 60 | |
23 | .. py:data:: dns.rdatatype.CDS | |
24 | :annotation: = 59 | |
25 | .. py:data:: dns.rdatatype.CERT | |
26 | :annotation: = 37 | |
27 | .. py:data:: dns.rdatatype.CNAME | |
28 | :annotation: = 5 | |
29 | .. py:data:: dns.rdatatype.CSYNC | |
30 | :annotation: = 62 | |
31 | .. py:data:: dns.rdatatype.DHCID | |
32 | :annotation: = 49 | |
33 | .. py:data:: dns.rdatatype.DLV | |
34 | :annotation: = 32769 | |
35 | .. py:data:: dns.rdatatype.DNAME | |
36 | :annotation: = 39 | |
37 | .. py:data:: dns.rdatatype.DNSKEY | |
38 | :annotation: = 48 | |
39 | .. py:data:: dns.rdatatype.DS | |
40 | :annotation: = 43 | |
41 | .. py:data:: dns.rdatatype.EUI48 | |
42 | :annotation: = 108 | |
43 | .. py:data:: dns.rdatatype.EUI64 | |
44 | :annotation: = 109 | |
45 | .. py:data:: dns.rdatatype.GPOS | |
46 | :annotation: = 27 | |
47 | .. py:data:: dns.rdatatype.HINFO | |
48 | :annotation: = 13 | |
49 | .. py:data:: dns.rdatatype.HIP | |
50 | :annotation: = 55 | |
51 | .. py:data:: dns.rdatatype.IPSECKEY | |
52 | :annotation: = 45 | |
53 | .. py:data:: dns.rdatatype.ISDN | |
54 | :annotation: = 20 | |
55 | .. py:data:: dns.rdatatype.IXFR | |
56 | :annotation: = 251 | |
57 | .. py:data:: dns.rdatatype.KEY | |
58 | :annotation: = 25 | |
59 | .. py:data:: dns.rdatatype.KX | |
60 | :annotation: = 36 | |
61 | .. py:data:: dns.rdatatype.LOC | |
62 | :annotation: = 29 | |
63 | .. py:data:: dns.rdatatype.MAILA | |
64 | :annotation: = 254 | |
65 | .. py:data:: dns.rdatatype.MAILB | |
66 | :annotation: = 253 | |
67 | .. py:data:: dns.rdatatype.MB | |
68 | :annotation: = 7 | |
69 | .. py:data:: dns.rdatatype.MD | |
70 | :annotation: = 3 | |
71 | .. py:data:: dns.rdatatype.MF | |
72 | :annotation: = 4 | |
73 | .. py:data:: dns.rdatatype.MG | |
74 | :annotation: = 8 | |
75 | .. py:data:: dns.rdatatype.MINFO | |
76 | :annotation: = 14 | |
77 | .. py:data:: dns.rdatatype.MR | |
78 | :annotation: = 9 | |
79 | .. py:data:: dns.rdatatype.MX | |
80 | :annotation: = 15 | |
81 | .. py:data:: dns.rdatatype.NAPTR | |
82 | :annotation: = 35 | |
83 | .. py:data:: dns.rdatatype.NONE | |
84 | :annotation: = 0 | |
85 | .. py:data:: dns.rdatatype.NS | |
86 | :annotation: = 2 | |
87 | .. py:data:: dns.rdatatype.NSAP | |
88 | :annotation: = 22 | |
89 | .. py:data:: dns.rdatatype.NSAP-PTR | |
90 | :annotation: = 23 | |
91 | .. py:data:: dns.rdatatype.NSEC | |
92 | :annotation: = 47 | |
93 | .. py:data:: dns.rdatatype.NSEC3 | |
94 | :annotation: = 50 | |
95 | .. py:data:: dns.rdatatype.NSEC3PARAM | |
96 | :annotation: = 51 | |
97 | .. py:data:: dns.rdatatype.NULL | |
98 | :annotation: = 10 | |
99 | .. py:data:: dns.rdatatype.NXT | |
100 | :annotation: = 30 | |
101 | .. py:data:: dns.rdatatype.OPT | |
102 | :annotation: = 41 | |
103 | .. py:data:: dns.rdatatype.PTR | |
104 | :annotation: = 12 | |
105 | .. py:data:: dns.rdatatype.PX | |
106 | :annotation: = 26 | |
107 | .. py:data:: dns.rdatatype.RP | |
108 | :annotation: = 17 | |
109 | .. py:data:: dns.rdatatype.RRSIG | |
110 | :annotation: = 46 | |
111 | .. py:data:: dns.rdatatype.RT | |
112 | :annotation: = 21 | |
113 | .. py:data:: dns.rdatatype.SIG | |
114 | :annotation: = 24 | |
115 | .. py:data:: dns.rdatatype.SOA | |
116 | :annotation: = 6 | |
117 | .. py:data:: dns.rdatatype.SPF | |
118 | :annotation: = 99 | |
119 | .. py:data:: dns.rdatatype.SRV | |
120 | :annotation: = 33 | |
121 | .. py:data:: dns.rdatatype.SSHFP | |
122 | :annotation: = 44 | |
123 | .. py:data:: dns.rdatatype.TA | |
124 | :annotation: = 32768 | |
125 | .. py:data:: dns.rdatatype.TKEY | |
126 | :annotation: = 249 | |
127 | .. py:data:: dns.rdatatype.TLSA | |
128 | :annotation: = 52 | |
129 | .. py:data:: dns.rdatatype.TSIG | |
130 | :annotation: = 250 | |
131 | .. py:data:: dns.rdatatype.TXT | |
132 | :annotation: = 16 | |
133 | .. py:data:: dns.rdatatype.UNSPEC | |
134 | :annotation: = 103 | |
135 | .. py:data:: dns.rdatatype.URI | |
136 | :annotation: = 256 | |
137 | .. py:data:: dns.rdatatype.WKS | |
138 | :annotation: = 11 | |
139 | .. py:data:: dns.rdatatype.X25 | |
140 | :annotation: = 19 |
0 | .. _resolver-caching: | |
1 | ||
2 | Resolver Caching Classes | |
3 | ======================== | |
4 | ||
5 | This is a placeholder. | |
6 | ||
7 | .. autoclass:: dns.resolver.Cache | |
8 | :members: | |
9 | ||
10 | .. autoclass:: dns.resolver.LRUCache | |
11 | :members: | |
12 |
0 | .. _resolver-class: | |
1 | ||
2 | The dns.resolver.Resolver and dns.resolver.Answer Classes | |
3 | --------------------------------------------------------- | |
4 | ||
5 | .. autoclass:: dns.resolver.Resolver | |
6 | :members: | |
7 | ||
8 | .. attribute:: domain | |
9 | ||
10 | A ``dns.name.Name``, the domain of this host. | |
11 | ||
12 | .. attribute:: nameservers | |
13 | ||
14 | A ``list`` of ``text``, each item containing an IPv4 or IPv6 address. | |
15 | ||
16 | .. attribute:: search | |
17 | ||
18 | A ``list`` of dns.name.Name objects. If the query name is a | |
19 | relative name, the resolver will construct absolute query names | |
20 | to try by appending values from the search list. | |
21 | ||
22 | .. attribute:: port | |
23 | ||
24 | An ``int``, the default DNS port to send to if not overriden by | |
25 | *nameserver_ports*. The default value is 53. | |
26 | ||
27 | .. attribute:: nameserver_ports | |
28 | ||
29 | A ``dict`` mapping an IPv4 or IPv6 address ``text`` to an ``int``. | |
30 | This specifies the port to use when sending to a nameserver. If | |
31 | a port is not defined for an address, the value of the *port* | |
32 | attribute will be used. | |
33 | ||
34 | .. attribute:: timeout | |
35 | ||
36 | A ``float``, the number of seconds to wait for a response from | |
37 | a server. | |
38 | ||
39 | .. attribute:: lifetime | |
40 | ||
41 | A ``float``, the number of seconds to spend trying to get an | |
42 | answer to the question. If the lifetime expires a | |
43 | ``dns.exception.Timeout`` exception will be raised. | |
44 | ||
45 | .. attribute:: cache: | |
46 | ||
47 | An object implementing the caching protocol, e.g. a | |
48 | ``dns.resolver.Cache`` or a ``dns.resolver.LRUCache``. The default | |
49 | is ``None``, in which case there is no local caching. | |
50 | ||
51 | .. attribute:: retry_servfail | |
52 | ||
53 | A ``bool``. Should we retry a nameserver if it says ``SERVFAIL``? | |
54 | The default is ``False``. | |
55 | ||
56 | .. attribute:: keyring | |
57 | ||
58 | A ``dict``, the TSIG keyring to use. If a *keyring* is | |
59 | specified but a *keyname* is not, then the key used will be | |
60 | the first key in the *keyring*. Note that the order of keys | |
61 | in a dictionary is not defined, so applications should supply | |
62 | a keyname when a keyring is used, unless they know the keyring | |
63 | contains only one key. | |
64 | ||
65 | .. attribute:: keyname | |
66 | ||
67 | A ``dns.name.Name`` or ``None``, the name of the TSIG key to | |
68 | use; defaults to ``None``. The key must be defined in the | |
69 | keyring. | |
70 | ||
71 | .. attribute:: keyalgorithm | |
72 | ||
73 | A ``dns.name.Name`` or ``text``, the TSIG algorithm to use. | |
74 | *edns*, an ``int``, is the EDNS level to use. Specifying | |
75 | ``None``, ``False``, or ``-1`` means "do not use EDNS", and in | |
76 | this case the other parameters are ignored. Specifying | |
77 | ``True`` is equivalent to specifying 0, i.e. "use EDNS0". | |
78 | ||
79 | .. attribute:: ednsflags | |
80 | ||
81 | An ``int``, the EDNS flag values. | |
82 | ||
83 | .. attribute:: payload | |
84 | ||
85 | An ``int``, is the EDNS sender's payload field, which is the | |
86 | maximum size of UDP datagram the sender can handle. I.e. how big | |
87 | a response to this message can be. | |
88 | ||
89 | .. attribute:: flags | |
90 | ||
91 | An ``int`` or ``None``, the message flags to use. If ``None``, | |
92 | then the default flags as set by the ``dns.message.Message`` | |
93 | constructor will be used. | |
94 | ||
95 | ||
96 | .. autoclass:: dns.resolver.Answer | |
97 | :members: | |
98 | ||
99 | .. attribute:: qname | |
100 | ||
101 | A ``dns.name.Name``, the query name. | |
102 | ||
103 | .. attribute:: rdclass | |
104 | ||
105 | An ``int``, the query class. | |
106 | ||
107 | .. attribute:: rdtype | |
108 | ||
109 | An ``int``, the query type. | |
110 | ||
111 | .. attribute:: response | |
112 | ||
113 | A ``dns.message.Message``, the response message. | |
114 | ||
115 | .. attribute:: rrset | |
116 | ||
117 | A ``dns.rrset.RRset`` or ``None``, the answer RRset. | |
118 | ||
119 | .. attribute:: expiration | |
120 | ||
121 | A ``float``, the time when the answer expires. | |
122 | ||
123 | .. attribute:: canonical_name | |
124 | ||
125 | A ``dns.name.Name``, the canonical name of the query name, | |
126 | i.e. the owner name of the answer RRset after any CNAME and DNAME | |
127 | chaining. |
0 | .. _resolver-functions: | |
1 | ||
2 | Resolver Functions and The Default Resolver | |
3 | =========================================== | |
4 | ||
5 | .. autofunction:: dns.resolver.query | |
6 | .. autofunction:: dns.resolver.zone_for_name | |
7 | .. autodata:: dns.resolver.default_resolver | |
8 | .. autofunction:: dns.resolver.get_default_resolver | |
9 | .. autofunction:: dns.resolver.reset_default_resolver |
0 | .. _resolver-override: | |
1 | ||
2 | Overriding the System Resolver | |
3 | ------------------------------ | |
4 | ||
5 | Sometimes it can be useful to make all of Python use dnspython's resolver | |
6 | rather than the default functionality in the ``socket`` module. Dnspython | |
7 | can redefine the entires in the socket module to point at its own code, and | |
8 | it can also restore them back to the regular Python defaults. | |
9 | ||
10 | .. autofunction:: dns.resolver.override_system_resolver | |
11 | .. autofunction:: dns.resolver.restore_system_resolver |
0 | .. module:: dns.resolver | |
1 | .. _resolver: | |
2 | ||
3 | Stub Resolver | |
4 | ============= | |
5 | ||
6 | This is a placeholder. | |
7 | ||
8 | .. toctree:: | |
9 | ||
10 | resolver-class | |
11 | resolver-functions | |
12 | resolver-caching | |
13 | resolver-override |
0 | .. _rfc: | |
1 | ||
2 | DNS RFC Reference | |
3 | ================= | |
4 | ||
5 | The DNS is defined by a large number of RFCs, many of which have been | |
6 | extensively updated or obsoleted. This chapter aims to provide a | |
7 | roadmap and reference for this confusing space. The chapter does not | |
8 | aim to be encyclopedically complete, however, as the key information | |
9 | would then be lost in the noise. The curious are encouraged to click | |
10 | on the "Updated by" links on the IETF pages to see the finer points, or | |
11 | the "Obsoletes" links to go spelunking into the history of the DNS. | |
12 | ||
13 | DNSSEC gets its own section instead of being included in the "Core" | |
14 | list because there are many DNSSEC related RFCs and it's helpful to group | |
15 | them together. It's not a statement that DNSSEC isn't part of the "Core" | |
16 | of the DNS. | |
17 | ||
18 | The IANA `DNS Parameters <http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml>`_ registry is the offical reference site for all DNS | |
19 | constants. | |
20 | ||
21 | ||
22 | Core RFCs | |
23 | --------- | |
24 | ||
25 | `RFC 1034 <https://tools.ietf.org/html/rfc1034>`_ | |
26 | Introduction to the DNS and description of basic behavior. | |
27 | ||
28 | `RFC 1035 <https://tools.ietf.org/html/rfc1035>`_ | |
29 | The core DNS wire protocol and master file format. | |
30 | ||
31 | `RFC 1995 <https://tools.ietf.org/html/rfc1995>`_ | |
32 | Incremental zone transfer (IXFR). | |
33 | ||
34 | `RFC 1996 <https://tools.ietf.org/html/rfc1996>`_ | |
35 | The NOTIFY protocol. | |
36 | ||
37 | `RFC 2181 <https://tools.ietf.org/html/rfc2181>`_ | |
38 | Clarifications to the specification. | |
39 | ||
40 | `RFC 2308 <https://tools.ietf.org/html/rfc2308>`_ | |
41 | Negative Caching. | |
42 | ||
43 | `RFC 2845 <https://tools.ietf.org/html/rfc2845>`_ | |
44 | Transaction Sigatures (TSIG) | |
45 | ||
46 | `RFC 3007 <https://tools.ietf.org/html/rfc3007>`_ | |
47 | Dynamic Updates | |
48 | ||
49 | `RFC 3645 <https://tools.ietf.org/html/rfc3645>`_ | |
50 | GSS-TSIG. | |
51 | ||
52 | Note that dnspython does not currently have GSS-TSIG support. | |
53 | GSS-TSIG is most frequently used when updating Microsoft | |
54 | Active-Directory-based DNS servers. | |
55 | ||
56 | `RFC 5936 <https://tools.ietf.org/html/rfc5936>`_ | |
57 | Zone transfers (AXFR). | |
58 | ||
59 | `RFC 6891 <https://tools.ietf.org/html/rfc6891>`_ | |
60 | EDNS (version 0) | |
61 | ||
62 | `RFC 8020 <https://tools.ietf.org/html/rfc8020>`_ | |
63 | Clarification on the meaning of NXDOMAIN. | |
64 | ||
65 | DNSSEC RFCs | |
66 | ----------- | |
67 | ||
68 | `RFC 4033 <https://tools.ietf.org/html/rfc4033>`_ | |
69 | Introduction and requirements. | |
70 | ||
71 | `RFC 4034 <https://tools.ietf.org/html/rfc4034>`_ | |
72 | Resource records. | |
73 | ||
74 | `RFC 4035 <https://tools.ietf.org/html/rfc4035>`_ | |
75 | Protocol. | |
76 | ||
77 | `RFC 4470 <https://tools.ietf.org/html/rfc4470>`_ | |
78 | Minimally covering NSEC records and On-line Signing. | |
79 | ||
80 | `RFC 6840 <https://tools.ietf.org/html/rfc6840>`_ | |
81 | Clarifications and implementation Notes. | |
82 | ||
83 | Misc RFCs | |
84 | --------- | |
85 | ||
86 | `RFC 1101 <https://tools.ietf.org/html/rfc1101>`_ | |
87 | Reverse mapping name form for IPv4. | |
88 | ||
89 | `RFC 1982 <https://tools.ietf.org/html/rfc1982>`_ | |
90 | Serial number arithmetic. | |
91 | ||
92 | `RFC 4343 <https://tools.ietf.org/html/rfc4343>`_ | |
93 | Case-sensitivity clarification. | |
94 | ||
95 | RFCs for RR types | |
96 | ----------------- | |
97 | ||
98 | There are many more RR types than are listed here; if a type is not | |
99 | listed it means it is obsolete, deprecated, or rare "in the wild". | |
100 | Some newer types that are currently rare are listed because they may | |
101 | well be more heavily used in the not-to-distant future. | |
102 | See the | |
103 | IANA `DNS Parameters <http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml>`_ registry for a complete list. | |
104 | ||
105 | A | |
106 | `RFC 1035 <https://tools.ietf.org/html/rfc1035>`_ | |
107 | AAAA | |
108 | `RFC 3596 <https://tools.ietf.org/html/rfc3596>`_ | |
109 | CDS | |
110 | `RFC 7344 <https://tools.ietf.org/html/rfc7344>`_ | |
111 | CDNSKEY | |
112 | `RFC 7344 <https://tools.ietf.org/html/rfc7344>`_ | |
113 | CNAME | |
114 | `RFC 1035 <https://tools.ietf.org/html/rfc1035>`_ | |
115 | CSYNC | |
116 | `RFC 7477 <https://tools.ietf.org/html/rfc7477>`_ | |
117 | DNAME | |
118 | `RFC 6672 <https://tools.ietf.org/html/rfc6672>`_ | |
119 | DNSKEY | |
120 | `RFC 4034 <https://tools.ietf.org/html/rfc4034>`_ | |
121 | DS | |
122 | `RFC 4034 <https://tools.ietf.org/html/rfc4034>`_ | |
123 | LOC | |
124 | `RFC 1876 <https://tools.ietf.org/html/rfc1876>`_ | |
125 | MX | |
126 | `RFC 1035 <https://tools.ietf.org/html/rfc1035>`_ | |
127 | NAPTR | |
128 | `RFC 3403 <https://tools.ietf.org/html/rfc3403>`_ | |
129 | NS | |
130 | `RFC 1035 <https://tools.ietf.org/html/rfc1035>`_ | |
131 | NSEC | |
132 | `RFC 4034 <https://tools.ietf.org/html/rfc4034>`_ | |
133 | NSEC3 | |
134 | `RFC 5155 <https://tools.ietf.org/html/rfc5155>`_ | |
135 | NSEC3PARAM | |
136 | `RFC 5155 <https://tools.ietf.org/html/rfc5155>`_ | |
137 | OPENPGPKEY | |
138 | `RFC 7929 <https://tools.ietf.org/html/rfc7929>`_ | |
139 | PTR | |
140 | `RFC 1035 <https://tools.ietf.org/html/rfc1035>`_ | |
141 | RRSIG | |
142 | `RFC 4034 <https://tools.ietf.org/html/rfc4034>`_ | |
143 | SOA | |
144 | `RFC 1035 <https://tools.ietf.org/html/rfc1035>`_ | |
145 | SPF | |
146 | `RFC 7208 <https://tools.ietf.org/html/rfc7208>`_ | |
147 | SRV | |
148 | `RFC 2782 <https://tools.ietf.org/html/rfc2782>`_ | |
149 | TLSA | |
150 | `RFC 6698 <https://tools.ietf.org/html/rfc6698>`_ | |
151 | TXT | |
152 | `RFC 1035 <https://tools.ietf.org/html/rfc1035>`_ |
0 | #!/usr/bin/env python3 | |
1 | ||
2 | import importlib | |
3 | import sys | |
4 | ||
5 | name = sys.argv[1] | |
6 | title = sys.argv[2] | |
7 | ||
8 | print(title) | |
9 | print('=' * len(title)) | |
10 | print() | |
11 | ||
12 | module = importlib.import_module(name) | |
13 | for t in sorted(module._by_text.keys()): | |
14 | print('.. py:data:: {}.{}'.format(name, t)) | |
15 | print(' :annotation: = {}'.format(module._by_text[t])) |
0 | .. _utilities: | |
1 | ||
2 | Miscellaneous Utilities | |
3 | ----------------------- | |
4 | ||
5 | .. automodule:: dns.inet | |
6 | :members: | |
7 | ||
8 | .. automodule:: dns.ipv4 | |
9 | :members: | |
10 | ||
11 | .. automodule:: dns.ipv6 | |
12 | :members: | |
13 | ||
14 | .. autofunction:: dns.ttl.from_text | |
15 | ||
16 | .. automodule:: dns.set | |
17 | :members: | |
18 | ||
19 | .. automodule:: dns.version | |
20 | :members: |
0 | .. _whatsnew: | |
1 | ||
2 | What's New in dnspython 1.16.0 | |
3 | ============================== | |
4 | ||
5 | New Features | |
6 | ------------ | |
7 | ||
8 | Bug Fixes | |
9 | --------- | |
10 |
0 | #!/usr/bin/env python | |
1 | ||
2 | # Two ways of querying a specific nameserver. | |
3 | ||
4 | from __future__ import print_function | |
5 | ||
6 | import dns.message | |
7 | import dns.rdataclass | |
8 | import dns.rdatatype | |
9 | import dns.query | |
10 | ||
11 | # This way is just like nslookup/dig: | |
12 | ||
13 | qname = dns.name.from_text('amazon.com') | |
14 | q = dns.message.make_query(qname, dns.rdatatype.NS) | |
15 | print('The query is:') | |
16 | print(q) | |
17 | print('') | |
18 | r = dns.query.udp(q, '8.8.8.8') | |
19 | print('The response is:') | |
20 | print(r) | |
21 | print('') | |
22 | print('The nameservers are:') | |
23 | ns_rrset = r.find_rrset(r.answer, qname, dns.rdataclass.IN, dns.rdatatype.NS) | |
24 | for rr in ns_rrset: | |
25 | print(rr.target) | |
26 | print('') | |
27 | print('') | |
28 | ||
29 | # A higher-level way | |
30 | ||
31 | import dns.resolver | |
32 | ||
33 | resolver = dns.resolver.Resolver(configure=False) | |
34 | resolver.nameservers = ['8.8.8.8'] | |
35 | answer = resolver.query('amazon.com', 'NS') | |
36 | print('The nameservers are:') | |
37 | for rr in answer: | |
38 | print(rr.target) |
0 | #!/usr/bin/env python3 | |
1 | ||
2 | # This is just a toy, real code would check that the received message | |
3 | # really was a NOTIFY, and otherwise handle errors. | |
4 | ||
5 | from __future__ import print_function | |
6 | ||
7 | import socket | |
8 | ||
9 | import dns.flags | |
10 | import dns.message | |
11 | import dns.rdataclass | |
12 | import dns.rdatatype | |
13 | import dns.name | |
14 | ||
15 | from typing import cast | |
16 | ||
17 | address = '127.0.0.1' | |
18 | port = 53535 | |
19 | ||
20 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
21 | s.bind((address, port)) | |
22 | while True: | |
23 | (wire, address) = s.recvfrom(512) | |
24 | notify = dns.message.from_wire(wire) | |
25 | soa = notify.find_rrset(notify.answer, notify.question[0].name, | |
26 | dns.rdataclass.IN, dns.rdatatype.SOA) | |
27 | ||
28 | # Do something with the SOA RR here | |
29 | print('The serial number for', soa.name, 'is', soa[0].serial) | |
30 | ||
31 | response = dns.message.make_response(notify) # type: dns.message.Message | |
32 | response.flags |= dns.flags.AA | |
33 | wire = response.to_wire(cast(dns.name.Name, response)) | |
34 | s.sendto(wire, address) |
21 | 21 | import dns.ipv4 |
22 | 22 | import os.path |
23 | 23 | import sys |
24 | from typing import Dict, List # pylint: disable=unused-import | |
24 | 25 | |
25 | reverse_map = {} | |
26 | reverse_map = {} # type: Dict[str, List[str]] | |
26 | 27 | |
27 | 28 | for filename in sys.argv[1:]: |
28 | 29 | zone = dns.zone.from_file(filename, os.path.basename(filename), |
29 | 30 | relativize=False) |
30 | 31 | for (name, ttl, rdata) in zone.iterate_rdatas('A'): |
32 | print(type(rdata)) | |
31 | 33 | try: |
32 | 34 | reverse_map[rdata.address].append(name.to_text()) |
33 | 35 | except KeyError: |
34 | 36 | reverse_map[rdata.address] = [name.to_text()] |
35 | 37 | |
36 | keys = reverse_map.keys() | |
37 | keys.sort(key=dns.ipv4.inet_aton) | |
38 | for k in keys: | |
38 | for k in sorted(reverse_map.keys(), key=dns.ipv4.inet_aton): | |
39 | 39 | v = reverse_map[k] |
40 | 40 | v.sort() |
41 | 41 | print(k, v) |
9 | 9 | master_answer = dns.resolver.query(soa_answer[0].mname, 'A') |
10 | 10 | |
11 | 11 | z = dns.zone.from_xfr(dns.query.xfr(master_answer[0].address, 'dnspython.org')) |
12 | names = z.nodes.keys() | |
13 | names.sort() | |
14 | for n in names: | |
12 | for n in sorted(z.nodes.keys()): | |
15 | 13 | print(z[n].to_text(n)) |
21 | 21 | """See diff_zones.__doc__ for more information""" |
22 | 22 | |
23 | 23 | from __future__ import print_function |
24 | from typing import cast, Union, Any # pylint: disable=unused-import | |
24 | 25 | |
25 | 26 | __all__ = ['diff_zones', 'format_changes_plain', 'format_changes_html'] |
26 | 27 | |
27 | 28 | try: |
28 | 29 | import dns.zone |
30 | import dns.node | |
29 | 31 | except ImportError: |
30 | 32 | raise SystemExit("Please install dnspython") |
31 | 33 | |
32 | 34 | |
33 | def diff_zones(zone1, zone2, ignore_ttl=False, ignore_soa=False): | |
35 | def diff_zones(zone1, # type: dns.zone.Zone | |
36 | zone2, # type: dns.zone.Zone | |
37 | ignore_ttl=False, | |
38 | ignore_soa=False | |
39 | ): # type: (...) -> list | |
34 | 40 | """diff_zones(zone1, zone2, ignore_ttl=False, ignore_soa=False) -> changes |
35 | 41 | Compares two dns.zone.Zone objects and returns a list of all changes |
36 | 42 | in the format (name, oldnode, newnode). |
46 | 52 | |
47 | 53 | changes = [] |
48 | 54 | for name in zone1: |
49 | name = str(name) | |
50 | n1 = zone1.get_node(name) | |
51 | n2 = zone2.get_node(name) | |
55 | namestr = str(name) | |
56 | n1 = cast(dns.node.Node, zone1.get_node(namestr)) | |
57 | n2 = cast(dns.node.Node, zone2.get_node(namestr)) | |
52 | 58 | if not n2: |
53 | 59 | changes.append((str(name), n1, n2)) |
54 | 60 | elif _nodes_differ(n1, n2, ignore_ttl, ignore_soa): |
55 | 61 | changes.append((str(name), n1, n2)) |
56 | 62 | |
57 | 63 | for name in zone2: |
58 | n1 = zone1.get_node(name) | |
59 | if not n1: | |
60 | n2 = zone2.get_node(name) | |
61 | changes.append((str(name), n1, n2)) | |
64 | n3 = cast(dns.node.Node, zone1.get_node(name)) | |
65 | if not n3: | |
66 | n4 = cast(dns.node.Node, zone2.get_node(name)) | |
67 | changes.append((str(name), n3, n4)) | |
62 | 68 | return changes |
63 | 69 | |
64 | def _nodes_differ(n1, n2, ignore_ttl, ignore_soa): | |
70 | def _nodes_differ(n1, # type: dns.node.Node | |
71 | n2, # type: dns.node.Node | |
72 | ignore_ttl, # type: bool | |
73 | ignore_soa # type: bool | |
74 | ): # type: (...) -> bool | |
65 | 75 | if ignore_soa or not ignore_ttl: |
66 | 76 | # Compare datasets directly |
67 | 77 | for r in n1.rdatasets: |
77 | 87 | continue |
78 | 88 | if r not in n1.rdatasets: |
79 | 89 | return True |
90 | assert False | |
80 | 91 | else: |
81 | 92 | return n1 != n2 |
82 | 93 | |
83 | def format_changes_plain(oldf, newf, changes, ignore_ttl=False): | |
94 | def format_changes_plain(oldf, # type: str | |
95 | newf, # type: str | |
96 | changes, # type: list | |
97 | ignore_ttl=False | |
98 | ): # type: (...) -> str | |
84 | 99 | """format_changes(oldfile, newfile, changes, ignore_ttl=False) -> str |
85 | 100 | Given 2 filenames and a list of changes from diff_zones, produce diff-like |
86 | 101 | output. If ignore_ttl is True, TTL-only changes are not displayed""" |
87 | 102 | |
88 | ret = "--- %s\n+++ %s\n" % (oldf, newf) | |
103 | ret = "--- {}\n+++ {}\n".format(oldf, newf) | |
89 | 104 | for name, old, new in changes: |
90 | 105 | ret += "@ %s\n" % name |
91 | 106 | if not old: |
109 | 124 | ret += "+ %s\n" % str(r).replace('\n', '\n+ ') |
110 | 125 | return ret |
111 | 126 | |
112 | def format_changes_html(oldf, newf, changes, ignore_ttl=False): | |
127 | def format_changes_html(oldf, # type: str | |
128 | newf, # type: str | |
129 | changes, # type: list | |
130 | ignore_ttl=False | |
131 | ): # type: (...) -> str | |
113 | 132 | """format_changes(oldfile, newfile, changes, ignore_ttl=False) -> str |
114 | 133 | Given 2 filenames and a list of changes from diff_zones, produce nice html |
115 | 134 | output. If ignore_ttl is True, TTL-only changes are not displayed""" |
160 | 179 | |
161 | 180 | |
162 | 181 | # Make this module usable as a script too. |
163 | def main(): | |
182 | def main(): # type: () -> None | |
164 | 183 | import argparse |
165 | 184 | import subprocess |
166 | 185 | import sys |
189 | 208 | opts, args = p.parse_args() |
190 | 209 | opts.use_vc = opts.use_git or opts.use_bzr or opts.use_rcs |
191 | 210 | |
192 | def _open(what, err): | |
211 | def _open(what, err): # type: (Union[list,str], str) -> Any | |
193 | 212 | if isinstance(what, list): |
194 | 213 | # Must be a list, open subprocess |
195 | 214 | try: |
224 | 243 | else: |
225 | 244 | if len(args) == 3: |
226 | 245 | filename, oldr, newr = args |
227 | oldn = "%s:%s" % (oldr, filename) | |
228 | newn = "%s:%s" % (newr, filename) | |
246 | oldn = "{}:{}".format(oldr, filename) | |
247 | newn = "{}:{}".format(newr, filename) | |
229 | 248 | else: |
230 | 249 | filename, oldr = args |
231 | 250 | newr = None |
232 | oldn = "%s:%s" % (oldr, filename) | |
251 | oldn = "{}:{}".format(oldr, filename) | |
233 | 252 | newn = filename |
234 | 253 | |
235 | 254 | old, new = None, None |
236 | 255 | oldz, newz = None, None |
237 | 256 | if opts.use_bzr: |
238 | 257 | old = _open(["bzr", "cat", "-r" + oldr, filename], |
239 | "Unable to retrieve revision %s of %s" % (oldr, filename)) | |
240 | if newr != None: | |
258 | "Unable to retrieve revision {} of {}".format(oldr, filename)) | |
259 | if newr is not None: | |
241 | 260 | new = _open(["bzr", "cat", "-r" + newr, filename], |
242 | "Unable to retrieve revision %s of %s" % (newr, filename)) | |
261 | "Unable to retrieve revision {} of {}".format(newr, filename)) | |
243 | 262 | elif opts.use_git: |
244 | 263 | old = _open(["git", "show", oldn], |
245 | "Unable to retrieve revision %s of %s" % (oldr, filename)) | |
246 | if newr != None: | |
264 | "Unable to retrieve revision {} of {}".format(oldr, filename)) | |
265 | if newr is not None: | |
247 | 266 | new = _open(["git", "show", newn], |
248 | "Unable to retrieve revision %s of %s" % (newr, filename)) | |
267 | "Unable to retrieve revision {} of {}".format(newr, filename)) | |
249 | 268 | elif opts.use_rcs: |
250 | 269 | old = _open(["co", "-q", "-p", "-r" + oldr, filename], |
251 | "Unable to retrieve revision %s of %s" % (oldr, filename)) | |
252 | if newr != None: | |
270 | "Unable to retrieve revision {} of {}".format(oldr, filename)) | |
271 | if newr is not None: | |
253 | 272 | new = _open(["co", "-q", "-p", "-r" + newr, filename], |
254 | "Unable to retrieve revision %s of %s" % (newr, filename)) | |
273 | "Unable to retrieve revision {} of {}".format(newr, filename)) | |
255 | 274 | if not opts.use_vc: |
256 | 275 | old = _open(oldn, "Unable to open %s" % oldn) |
257 | 276 | if not opts.use_vc or newr is None: |
264 | 283 | try: |
265 | 284 | oldz = dns.zone.from_file(old, origin='.', check_origin=False) |
266 | 285 | except dns.exception.DNSException: |
267 | sys.stderr.write("Incorrect zonefile: %s\n", old) | |
286 | sys.stderr.write("Incorrect zonefile: %s\n" % old) | |
268 | 287 | if opts.tracebacks: |
269 | 288 | traceback.print_exc() |
270 | 289 | try: |
0 | [MASTER] | |
1 | # Pickle collected data for later comparisons. | |
2 | persistent=no | |
3 | ||
4 | # Use multiple processes to speed up Pylint, auto-detect number of cores. | |
5 | jobs=0 | |
6 | ||
7 | [MESSAGES CONTROL] | |
8 | ||
9 | enable= | |
10 | all, | |
11 | python3 | |
12 | ||
13 | # It's worth looking at len-as-condition for optimization, but it's disabled | |
14 | # here as it is not a correctness thing. Similarly eq-without-hash is | |
15 | # probably worth improving. | |
16 | ||
17 | disable= | |
18 | R, | |
19 | I, | |
20 | anomalous-backslash-in-string, | |
21 | arguments-differ, | |
22 | assigning-non-slot, | |
23 | bad-builtin, | |
24 | bad-continuation, | |
25 | broad-except, | |
26 | deprecated-method, | |
27 | fixme, | |
28 | global-statement, | |
29 | invalid-name, | |
30 | missing-docstring, | |
31 | no-absolute-import, | |
32 | no-member, | |
33 | protected-access, | |
34 | redefined-builtin, | |
35 | too-many-lines, | |
36 | unused-argument, | |
37 | unused-variable, | |
38 | wrong-import-order, | |
39 | wrong-import-position, | |
40 | len-as-condition, | |
41 | eq-without-hash, | |
42 | next-method-defined, | |
43 | ||
44 | [REPORTS] | |
45 | ||
46 | # Set the output format. Available formats are text, parseable, colorized, msvs | |
47 | # (visual studio) and html. You can also give a reporter class, eg | |
48 | # mypackage.mymodule.MyReporterClass. | |
49 | output-format=colorized | |
50 | ||
51 | # Tells whether to display a full report or only the messages | |
52 | reports=no | |
53 | ||
54 | # Template used to display messages. This is a python new-style format string | |
55 | # used to format the message information. See doc for all details | |
56 | msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg})' |
0 | [egg_info] | |
1 | tag_build = | |
2 | tag_date = 0 | |
3 | tag_svn_revision = 0 | |
0 | [bdist_wheel] | |
1 | universal = 1 | |
4 | 2 | |
3 | [metadata] | |
4 | license_file = LICENSE |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | #!/usr/bin/env python |
1 | 3 | # |
2 | 4 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
17 | 19 | import sys |
18 | 20 | from setuptools import setup |
19 | 21 | |
20 | version = '1.15.0' | |
22 | version = '1.16.0' | |
23 | ||
24 | try: | |
25 | sys.argv.remove("--cython-compile") | |
26 | except ValueError: | |
27 | compile_cython = False | |
28 | else: | |
29 | compile_cython = True | |
30 | from Cython.Build import cythonize | |
31 | ext_modules = cythonize(['dns/*.py', 'dns/rdtypes/*.py', 'dns/rdtypes/*/*.py']) | |
21 | 32 | |
22 | 33 | kwargs = { |
23 | 34 | 'name' : 'dnspython', |
36 | 47 | 'author_email' : 'halley@dnspython.org', |
37 | 48 | 'license' : 'BSD-like', |
38 | 49 | 'url' : 'http://www.dnspython.org', |
39 | 'packages' : ['dns', 'dns.rdtypes', 'dns.rdtypes.IN', 'dns.rdtypes.ANY'], | |
50 | 'packages' : ['dns', 'dns.rdtypes', 'dns.rdtypes.IN', 'dns.rdtypes.ANY', 'dns.rdtypes.CH'], | |
51 | 'package_data' : {'dns': ['py.typed']}, | |
40 | 52 | 'download_url' : \ |
41 | 'http://www.dnspython.org/kits/%s/dnspython-%s.tar.gz' % (version, version), | |
53 | 'http://www.dnspython.org/kits/{}/dnspython-{}.tar.gz'.format(version, version), | |
42 | 54 | 'classifiers' : [ |
43 | 55 | "Development Status :: 5 - Production/Stable", |
44 | 56 | "Intended Audience :: Developers", |
50 | 62 | "Topic :: Internet :: Name Service (DNS)", |
51 | 63 | "Topic :: Software Development :: Libraries :: Python Modules", |
52 | 64 | "Programming Language :: Python :: 2", |
53 | "Programming Language :: Python :: 2.6", | |
54 | 65 | "Programming Language :: Python :: 2.7", |
55 | 66 | "Programming Language :: Python :: 3", |
56 | "Programming Language :: Python :: 3.3", | |
57 | 67 | "Programming Language :: Python :: 3.4", |
58 | 68 | "Programming Language :: Python :: 3.5", |
69 | "Programming Language :: Python :: 3.6", | |
70 | "Programming Language :: Python :: 3.7", | |
59 | 71 | ], |
72 | 'python_requires': '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', | |
60 | 73 | 'test_suite': 'tests', |
61 | 74 | 'provides': ['dns'], |
75 | 'extras_require': { | |
76 | 'IDNA': ['idna>=2.1'], | |
77 | 'DNSSEC': ['pycryptodome', 'ecdsa>=0.13'], | |
78 | }, | |
79 | 'ext_modules': ext_modules if compile_cython else None, | |
80 | 'zip_safe': False if compile_cython else None, | |
62 | 81 | } |
63 | 82 | |
64 | 83 | setup(**kwargs) |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
194 | 194 | caa04 CAA 0 issue "ca.example.net; account=230123" |
195 | 195 | caa05 CAA 0 issue "ca.example.net; policy=ev" |
196 | 196 | caa06 CAA 128 tbs "Unknown" |
197 | openpgpkey OPENPGPKEY ( | |
198 | mQENBEteQDsBCADYnatn9+5t43AdJlVk9dZC2RM0idPQcmrrKcjeAWDnISqoJzkv | |
199 | Q8ifX6mefquTBsDZC279uXShyTffYzQtvP2r9ewkK7zmSv52Ar563TSULAMwiLpe | |
200 | 0gGQE0ex20mX5ggtYn6czdbEtcKpW0t+AfDqRk5YcpgqfZKXapKQ+A3CwWJKP9i3 | |
201 | ldx2Jz//kuru4YqROLBYyB8D6V2jNUFOdaP6j5C5prh9dxfYFp2O/xFeAKLWlWuH | |
202 | 9o96INUoIhgdEyj9PHPT3c821NMZu8tCvsZgUB+QPbHA/QYGa+aollcdGkJpVxXo | |
203 | Hhbu6aMx/B+pXg55WM5pqOxmoVjyViHIUYfPABEBAAG0IUJvYiBIYWxsZXkgPGhh | |
204 | bGxleUBkbnNweXRob24ub3JnPokBPgQTAQIAKAUCS15AOwIbAwUJA8JnAAYLCQgH | |
205 | AwIGFQgCCQoLBBYCAwECHgECF4AACgkQ6o6Gb8yUnXaflQgAhlhIqZGncRw3LV3d | |
206 | 24JmPD+UEcEGiVh2b/Ic/1TMec46Ts7ZqRXAcOATNteQmpzqexx+BRKDWU8ZgYx1 | |
207 | 2J4GZmC06jABr2JDWxgvbMX9qjkUUgDGZZgAS/B2x5AmKgy2ZnCUlaKfePcKmtKT | |
208 | B9yNJ8v/WERlFdGaUveEUiFU8g75xp1Hj9Wp9sXCg9yeG1K2RwQ3RQd5tLudhyE6 | |
209 | 7EQdFGgqQFynR53md7cmVhAGopKLwMkpCtToKUlxxlfnDfpKZhhXThmhA0PsUQUk | |
210 | JptfGwYwH3O2N3KzfUw3wXRvLa3hona3TlHk3kfg7Qyd7oP4AZGbJKp97YHnfqo1 | |
211 | kp8rObkBDQRLXkA7AQgA0ePG7g5GgZ/1SdtGZlJJiE2X15vTUc3KGfmx/kI5NaUD | |
212 | u4fXb+XK+yFy9I/X+UJ46JSkyhj6QvUxpoI+A7WWk9ThfjbynoZxRD820Kbqidqx | |
213 | BSgtFF36SRWzmX8DZfKKAskT9ZGU1odeSKDXLCJF7qAbZVRTuFRiDFGwtoVIICeE | |
214 | 6Xd65JO6ufhad+ELhgFt95vRwTiFvVrBRjwF7ZgN/nOXfYncxZ/2mpFqfwsnB2eu | |
215 | 0A2XZBm8IngsSmr/Wrz1RQ7+SNMqt77E7CKwBX7UIAZgyoJxIRxWirJoOt1rIm5V | |
216 | UqRR25ubXLuzx9PaHYiC5GiQIU45pWAd0IWcTI/MJQARAQABiQElBBgBAgAPBQJL | |
217 | XkA7AhsMBQkDwmcAAAoJEOqOhm/MlJ12HRsIAKrB9E++9X9W6VTXBfdkShCFv0yk | |
218 | ZVn2eVs6tkqzoub9s4f+Z5ylWw+a5nkMDMdGVe6bn4A3oIAbf0Tjykq1AetZLVPs | |
219 | Hl/QosTbSQluis/PEvJkTQXHaKHB3bFhwA90c/3HNhrLGugt9AmcfLf9LAynXDgN | |
220 | LV5eYdPYqfKE+27qjEBARf6PYh/8WQ8CPKS8DILFbwCZbRxUogyrZf/7AiHAGdJi | |
221 | 8dmpR1WPQYef2hF3kqGX6NngLBPzZ6CQRaHBhD4pHU1S/IRSlx9/3Ytww32PYD9A | |
222 | yO732NmCUcq3bmvqcOWy4Cc1NkEwU0Vg0qzwVBNGb84v/ex2MouwtAYScwc= | |
223 | ) | |
197 | 224 | csync0 CSYNC 12345 0 A MX RRSIG NSEC TYPE1234 |
198 | 225 | avc01 AVC "app-name:WOLFGANG|app-class:OAM|business=yes" |
88 | 88 | nsec03 3600 IN NSEC . NSEC TYPE65535 |
89 | 89 | nsec301 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM |
90 | 90 | nsec302 3600 IN NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM |
91 | openpgpkey 3600 IN OPENPGPKEY mQENBEteQDsBCADYnatn9+5t43AdJlVk 9dZC2RM0idPQcmrrKcjeAWDnISqoJzkv Q8ifX6mefquTBsDZC279uXShyTffYzQt vP2r9ewkK7zmSv52Ar563TSULAMwiLpe 0gGQE0ex20mX5ggtYn6czdbEtcKpW0t+ AfDqRk5YcpgqfZKXapKQ+A3CwWJKP9i3 ldx2Jz//kuru4YqROLBYyB8D6V2jNUFO daP6j5C5prh9dxfYFp2O/xFeAKLWlWuH 9o96INUoIhgdEyj9PHPT3c821NMZu8tC vsZgUB+QPbHA/QYGa+aollcdGkJpVxXo Hhbu6aMx/B+pXg55WM5pqOxmoVjyViHI UYfPABEBAAG0IUJvYiBIYWxsZXkgPGhh bGxleUBkbnNweXRob24ub3JnPokBPgQT AQIAKAUCS15AOwIbAwUJA8JnAAYLCQgH AwIGFQgCCQoLBBYCAwECHgECF4AACgkQ 6o6Gb8yUnXaflQgAhlhIqZGncRw3LV3d 24JmPD+UEcEGiVh2b/Ic/1TMec46Ts7Z qRXAcOATNteQmpzqexx+BRKDWU8ZgYx1 2J4GZmC06jABr2JDWxgvbMX9qjkUUgDG ZZgAS/B2x5AmKgy2ZnCUlaKfePcKmtKT B9yNJ8v/WERlFdGaUveEUiFU8g75xp1H j9Wp9sXCg9yeG1K2RwQ3RQd5tLudhyE6 7EQdFGgqQFynR53md7cmVhAGopKLwMkp CtToKUlxxlfnDfpKZhhXThmhA0PsUQUk JptfGwYwH3O2N3KzfUw3wXRvLa3hona3 TlHk3kfg7Qyd7oP4AZGbJKp97YHnfqo1 kp8rObkBDQRLXkA7AQgA0ePG7g5GgZ/1 SdtGZlJJiE2X15vTUc3KGfmx/kI5NaUD u4fXb+XK+yFy9I/X+UJ46JSkyhj6QvUx poI+A7WWk9ThfjbynoZxRD820Kbqidqx BSgtFF36SRWzmX8DZfKKAskT9ZGU1ode SKDXLCJF7qAbZVRTuFRiDFGwtoVIICeE 6Xd65JO6ufhad+ELhgFt95vRwTiFvVrB RjwF7ZgN/nOXfYncxZ/2mpFqfwsnB2eu 0A2XZBm8IngsSmr/Wrz1RQ7+SNMqt77E 7CKwBX7UIAZgyoJxIRxWirJoOt1rIm5V UqRR25ubXLuzx9PaHYiC5GiQIU45pWAd 0IWcTI/MJQARAQABiQElBBgBAgAPBQJL XkA7AhsMBQkDwmcAAAoJEOqOhm/MlJ12 HRsIAKrB9E++9X9W6VTXBfdkShCFv0yk ZVn2eVs6tkqzoub9s4f+Z5ylWw+a5nkM DMdGVe6bn4A3oIAbf0Tjykq1AetZLVPs Hl/QosTbSQluis/PEvJkTQXHaKHB3bFh wA90c/3HNhrLGugt9AmcfLf9LAynXDgN LV5eYdPYqfKE+27qjEBARf6PYh/8WQ8C PKS8DILFbwCZbRxUogyrZf/7AiHAGdJi 8dmpR1WPQYef2hF3kqGX6NngLBPzZ6CQ RaHBhD4pHU1S/IRSlx9/3Ytww32PYD9A yO732NmCUcq3bmvqcOWy4Cc1NkEwU0Vg 0qzwVBNGb84v/ex2MouwtAYScwc= | |
91 | 92 | ptr01 3600 IN PTR @ |
92 | 93 | px01 3600 IN PX 65535 foo. bar. |
93 | 94 | px02 3600 IN PX 65535 . . |
88 | 88 | nsec03.example. 3600 IN NSEC . NSEC TYPE65535 |
89 | 89 | nsec301.example. 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM |
90 | 90 | nsec302.example. 3600 IN NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM |
91 | openpgpkey.example. 3600 IN OPENPGPKEY mQENBEteQDsBCADYnatn9+5t43AdJlVk 9dZC2RM0idPQcmrrKcjeAWDnISqoJzkv Q8ifX6mefquTBsDZC279uXShyTffYzQt vP2r9ewkK7zmSv52Ar563TSULAMwiLpe 0gGQE0ex20mX5ggtYn6czdbEtcKpW0t+ AfDqRk5YcpgqfZKXapKQ+A3CwWJKP9i3 ldx2Jz//kuru4YqROLBYyB8D6V2jNUFO daP6j5C5prh9dxfYFp2O/xFeAKLWlWuH 9o96INUoIhgdEyj9PHPT3c821NMZu8tC vsZgUB+QPbHA/QYGa+aollcdGkJpVxXo Hhbu6aMx/B+pXg55WM5pqOxmoVjyViHI UYfPABEBAAG0IUJvYiBIYWxsZXkgPGhh bGxleUBkbnNweXRob24ub3JnPokBPgQT AQIAKAUCS15AOwIbAwUJA8JnAAYLCQgH AwIGFQgCCQoLBBYCAwECHgECF4AACgkQ 6o6Gb8yUnXaflQgAhlhIqZGncRw3LV3d 24JmPD+UEcEGiVh2b/Ic/1TMec46Ts7Z qRXAcOATNteQmpzqexx+BRKDWU8ZgYx1 2J4GZmC06jABr2JDWxgvbMX9qjkUUgDG ZZgAS/B2x5AmKgy2ZnCUlaKfePcKmtKT B9yNJ8v/WERlFdGaUveEUiFU8g75xp1H j9Wp9sXCg9yeG1K2RwQ3RQd5tLudhyE6 7EQdFGgqQFynR53md7cmVhAGopKLwMkp CtToKUlxxlfnDfpKZhhXThmhA0PsUQUk JptfGwYwH3O2N3KzfUw3wXRvLa3hona3 TlHk3kfg7Qyd7oP4AZGbJKp97YHnfqo1 kp8rObkBDQRLXkA7AQgA0ePG7g5GgZ/1 SdtGZlJJiE2X15vTUc3KGfmx/kI5NaUD u4fXb+XK+yFy9I/X+UJ46JSkyhj6QvUx poI+A7WWk9ThfjbynoZxRD820Kbqidqx BSgtFF36SRWzmX8DZfKKAskT9ZGU1ode SKDXLCJF7qAbZVRTuFRiDFGwtoVIICeE 6Xd65JO6ufhad+ELhgFt95vRwTiFvVrB RjwF7ZgN/nOXfYncxZ/2mpFqfwsnB2eu 0A2XZBm8IngsSmr/Wrz1RQ7+SNMqt77E 7CKwBX7UIAZgyoJxIRxWirJoOt1rIm5V UqRR25ubXLuzx9PaHYiC5GiQIU45pWAd 0IWcTI/MJQARAQABiQElBBgBAgAPBQJL XkA7AhsMBQkDwmcAAAoJEOqOhm/MlJ12 HRsIAKrB9E++9X9W6VTXBfdkShCFv0yk ZVn2eVs6tkqzoub9s4f+Z5ylWw+a5nkM DMdGVe6bn4A3oIAbf0Tjykq1AetZLVPs Hl/QosTbSQluis/PEvJkTQXHaKHB3bFh wA90c/3HNhrLGugt9AmcfLf9LAynXDgN LV5eYdPYqfKE+27qjEBARf6PYh/8WQ8C PKS8DILFbwCZbRxUogyrZf/7AiHAGdJi 8dmpR1WPQYef2hF3kqGX6NngLBPzZ6CQ RaHBhD4pHU1S/IRSlx9/3Ytww32PYD9A yO732NmCUcq3bmvqcOWy4Cc1NkEwU0Vg 0qzwVBNGb84v/ex2MouwtAYScwc= | |
91 | 92 | ptr01.example. 3600 IN PTR example. |
92 | 93 | px01.example. 3600 IN PX 65535 foo. bar. |
93 | 94 | px02.example. 3600 IN PX 65535 . . |
88 | 88 | nsec03 3600 IN NSEC . NSEC TYPE65535 |
89 | 89 | nsec301 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM |
90 | 90 | nsec302 3600 IN NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM |
91 | openpgpkey 3600 IN OPENPGPKEY mQENBEteQDsBCADYnatn9+5t43AdJlVk 9dZC2RM0idPQcmrrKcjeAWDnISqoJzkv Q8ifX6mefquTBsDZC279uXShyTffYzQt vP2r9ewkK7zmSv52Ar563TSULAMwiLpe 0gGQE0ex20mX5ggtYn6czdbEtcKpW0t+ AfDqRk5YcpgqfZKXapKQ+A3CwWJKP9i3 ldx2Jz//kuru4YqROLBYyB8D6V2jNUFO daP6j5C5prh9dxfYFp2O/xFeAKLWlWuH 9o96INUoIhgdEyj9PHPT3c821NMZu8tC vsZgUB+QPbHA/QYGa+aollcdGkJpVxXo Hhbu6aMx/B+pXg55WM5pqOxmoVjyViHI UYfPABEBAAG0IUJvYiBIYWxsZXkgPGhh bGxleUBkbnNweXRob24ub3JnPokBPgQT AQIAKAUCS15AOwIbAwUJA8JnAAYLCQgH AwIGFQgCCQoLBBYCAwECHgECF4AACgkQ 6o6Gb8yUnXaflQgAhlhIqZGncRw3LV3d 24JmPD+UEcEGiVh2b/Ic/1TMec46Ts7Z qRXAcOATNteQmpzqexx+BRKDWU8ZgYx1 2J4GZmC06jABr2JDWxgvbMX9qjkUUgDG ZZgAS/B2x5AmKgy2ZnCUlaKfePcKmtKT B9yNJ8v/WERlFdGaUveEUiFU8g75xp1H j9Wp9sXCg9yeG1K2RwQ3RQd5tLudhyE6 7EQdFGgqQFynR53md7cmVhAGopKLwMkp CtToKUlxxlfnDfpKZhhXThmhA0PsUQUk JptfGwYwH3O2N3KzfUw3wXRvLa3hona3 TlHk3kfg7Qyd7oP4AZGbJKp97YHnfqo1 kp8rObkBDQRLXkA7AQgA0ePG7g5GgZ/1 SdtGZlJJiE2X15vTUc3KGfmx/kI5NaUD u4fXb+XK+yFy9I/X+UJ46JSkyhj6QvUx poI+A7WWk9ThfjbynoZxRD820Kbqidqx BSgtFF36SRWzmX8DZfKKAskT9ZGU1ode SKDXLCJF7qAbZVRTuFRiDFGwtoVIICeE 6Xd65JO6ufhad+ELhgFt95vRwTiFvVrB RjwF7ZgN/nOXfYncxZ/2mpFqfwsnB2eu 0A2XZBm8IngsSmr/Wrz1RQ7+SNMqt77E 7CKwBX7UIAZgyoJxIRxWirJoOt1rIm5V UqRR25ubXLuzx9PaHYiC5GiQIU45pWAd 0IWcTI/MJQARAQABiQElBBgBAgAPBQJL XkA7AhsMBQkDwmcAAAoJEOqOhm/MlJ12 HRsIAKrB9E++9X9W6VTXBfdkShCFv0yk ZVn2eVs6tkqzoub9s4f+Z5ylWw+a5nkM DMdGVe6bn4A3oIAbf0Tjykq1AetZLVPs Hl/QosTbSQluis/PEvJkTQXHaKHB3bFh wA90c/3HNhrLGugt9AmcfLf9LAynXDgN LV5eYdPYqfKE+27qjEBARf6PYh/8WQ8C PKS8DILFbwCZbRxUogyrZf/7AiHAGdJi 8dmpR1WPQYef2hF3kqGX6NngLBPzZ6CQ RaHBhD4pHU1S/IRSlx9/3Ytww32PYD9A yO732NmCUcq3bmvqcOWy4Cc1NkEwU0Vg 0qzwVBNGb84v/ex2MouwtAYScwc= | |
91 | 92 | ptr01 3600 IN PTR @ |
92 | 93 | px01 3600 IN PX 65535 foo. bar. |
93 | 94 | px02 3600 IN PX 65535 . . |
0 | # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2006-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | 17 | from io import BytesIO |
16 | try: | |
17 | import unittest2 as unittest | |
18 | except ImportError: | |
19 | import unittest | |
18 | import unittest | |
19 | ||
20 | import binascii | |
20 | 21 | |
21 | 22 | import dns.rdata |
22 | 23 | import dns.rdataclass |
23 | 24 | import dns.rdatatype |
25 | import dns.rdtypes.ANY.TXT | |
24 | 26 | import dns.ttl |
25 | 27 | |
26 | 28 | class BugsTestCase(unittest.TestCase): |
67 | 69 | wire, 0, rdlen) |
68 | 70 | self.failUnless(rdata == rdata2) |
69 | 71 | |
72 | def test_trailing_zero_APL(self): | |
73 | in4 = "!1:127.0.0.0/1" | |
74 | rd4 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.APL, in4) | |
75 | out4 = rd4.to_digestable(dns.name.from_text("test")) | |
76 | text4 = binascii.hexlify(out4).decode('ascii') | |
77 | self.failUnless(text4 == '000101817f') | |
78 | in6 = "!2:::1000/1" | |
79 | rd6 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.APL, in6) | |
80 | out6 = rd6.to_digestable(dns.name.from_text("test")) | |
81 | text6 = binascii.hexlify(out6).decode('ascii') | |
82 | self.failUnless(text6 == '0002018f000000000000000000000000000010') | |
83 | ||
84 | def test_TXT_conversions(self): | |
85 | t1 = dns.rdtypes.ANY.TXT.TXT(dns.rdataclass.IN, dns.rdatatype.TXT, | |
86 | [b'foo']) | |
87 | t2 = dns.rdtypes.ANY.TXT.TXT(dns.rdataclass.IN, dns.rdatatype.TXT, | |
88 | b'foo') | |
89 | t3 = dns.rdtypes.ANY.TXT.TXT(dns.rdataclass.IN, dns.rdatatype.TXT, | |
90 | 'foo') | |
91 | t4 = dns.rdtypes.ANY.TXT.TXT(dns.rdataclass.IN, dns.rdatatype.TXT, | |
92 | ['foo']) | |
93 | self.failUnless(t1 == t2) | |
94 | self.failUnless(t1 == t2) | |
95 | self.failUnless(t1 == t4) | |
96 | ||
70 | 97 | if __name__ == '__main__': |
71 | 98 | unittest.main() |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
14 | 16 | |
15 | 17 | from __future__ import print_function |
16 | 18 | |
17 | try: | |
18 | import unittest2 as unittest | |
19 | except ImportError: | |
20 | import unittest | |
21 | ||
22 | try: | |
23 | import Crypto.Util.number # pylint: disable=unused-import | |
24 | import_ok = True | |
25 | except ImportError: | |
26 | import_ok = False | |
19 | import unittest | |
27 | 20 | |
28 | 21 | import dns.dnssec |
29 | 22 | import dns.name |
155 | 148 | abs_ecdsa384_soa_rrsig = dns.rrset.from_text('example.', 86400, 'IN', 'RRSIG', |
156 | 149 | "SOA 14 1 86400 20130929021229 20130921230729 63571 example. CrnCu34EeeRz0fEhL9PLlwjpBKGYW8QjBjFQTwd+ViVLRAS8tNkcDwQE NhSV89NEjj7ze1a/JcCfcJ+/mZgnvH4NHLNg3Tf6KuLZsgs2I4kKQXEk 37oIHravPEOlGYNI") |
157 | 150 | |
158 | @unittest.skipUnless(import_ok, "skipping DNSSEC tests because pycrypto is not" | |
159 | " installed") | |
151 | ||
152 | ||
153 | @unittest.skipUnless(dns.dnssec._have_pycrypto, | |
154 | "Pycryptodome cannot be imported") | |
160 | 155 | class DNSSECValidatorTestCase(unittest.TestCase): |
161 | 156 | |
162 | @unittest.skipUnless(dns.dnssec._have_pycrypto, | |
163 | "PyCrypto cannot be imported") | |
164 | def testAbsoluteRSAGood(self): | |
157 | def testAbsoluteRSAGood(self): # type: () -> None | |
165 | 158 | dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys, None, when) |
166 | 159 | |
167 | @unittest.skipUnless(dns.dnssec._have_pycrypto, | |
168 | "PyCrypto cannot be imported") | |
169 | def testDuplicateKeytag(self): | |
160 | def testDuplicateKeytag(self): # type: () -> None | |
170 | 161 | dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys_duplicate_keytag, None, when) |
171 | 162 | |
172 | @unittest.skipUnless(dns.dnssec._have_pycrypto, | |
173 | "PyCrypto cannot be imported") | |
174 | def testAbsoluteRSABad(self): | |
175 | def bad(): | |
163 | def testAbsoluteRSABad(self): # type: () -> None | |
164 | def bad(): # type: () -> None | |
176 | 165 | dns.dnssec.validate(abs_other_soa, abs_soa_rrsig, abs_keys, None, |
177 | 166 | when) |
178 | 167 | self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) |
179 | 168 | |
180 | @unittest.skipUnless(dns.dnssec._have_pycrypto, | |
181 | "PyCrypto cannot be imported") | |
182 | def testRelativeRSAGood(self): | |
169 | def testRelativeRSAGood(self): # type: () -> None | |
183 | 170 | dns.dnssec.validate(rel_soa, rel_soa_rrsig, rel_keys, |
184 | 171 | abs_dnspython_org, when) |
185 | 172 | |
186 | @unittest.skipUnless(dns.dnssec._have_pycrypto, | |
187 | "PyCrypto cannot be imported") | |
188 | def testRelativeRSABad(self): | |
189 | def bad(): | |
173 | def testRelativeRSABad(self): # type: () -> None | |
174 | def bad(): # type: () -> None | |
190 | 175 | dns.dnssec.validate(rel_other_soa, rel_soa_rrsig, rel_keys, |
191 | 176 | abs_dnspython_org, when) |
192 | 177 | self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) |
193 | 178 | |
194 | def testMakeSHA256DS(self): | |
179 | def testMakeSHA256DS(self): # type: () -> None | |
195 | 180 | ds = dns.dnssec.make_ds(abs_dnspython_org, sep_key, 'SHA256') |
196 | 181 | self.failUnless(ds == good_ds) |
197 | 182 | |
198 | @unittest.skipUnless(dns.dnssec._have_pycrypto, | |
199 | "PyCrypto cannot be imported") | |
200 | def testAbsoluteDSAGood(self): | |
183 | def testAbsoluteDSAGood(self): # type: () -> None | |
201 | 184 | dns.dnssec.validate(abs_dsa_soa, abs_dsa_soa_rrsig, abs_dsa_keys, None, |
202 | 185 | when2) |
203 | 186 | |
204 | @unittest.skipUnless(dns.dnssec._have_pycrypto, | |
205 | "PyCrypto cannot be imported") | |
206 | def testAbsoluteDSABad(self): | |
207 | def bad(): | |
187 | def testAbsoluteDSABad(self): # type: () -> None | |
188 | def bad(): # type: () -> None | |
208 | 189 | dns.dnssec.validate(abs_other_dsa_soa, abs_dsa_soa_rrsig, |
209 | 190 | abs_dsa_keys, None, when2) |
210 | 191 | self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) |
211 | 192 | |
212 | def testMakeExampleSHA1DS(self): | |
193 | def testMakeExampleSHA1DS(self): # type: () -> None | |
213 | 194 | ds = dns.dnssec.make_ds(abs_example, example_sep_key, 'SHA1') |
214 | 195 | self.failUnless(ds == example_ds_sha1) |
215 | 196 | |
216 | def testMakeExampleSHA256DS(self): | |
197 | def testMakeExampleSHA256DS(self): # type: () -> None | |
217 | 198 | ds = dns.dnssec.make_ds(abs_example, example_sep_key, 'SHA256') |
218 | 199 | self.failUnless(ds == example_ds_sha256) |
219 | 200 | |
220 | 201 | @unittest.skipUnless(dns.dnssec._have_ecdsa, |
221 | 202 | "python ECDSA cannot be imported") |
222 | def testAbsoluteECDSA256Good(self): | |
203 | def testAbsoluteECDSA256Good(self): # type: () -> None | |
223 | 204 | dns.dnssec.validate(abs_ecdsa256_soa, abs_ecdsa256_soa_rrsig, |
224 | 205 | abs_ecdsa256_keys, None, when3) |
225 | 206 | |
226 | 207 | @unittest.skipUnless(dns.dnssec._have_ecdsa, |
227 | 208 | "python ECDSA cannot be imported") |
228 | def testAbsoluteECDSA256Bad(self): | |
229 | def bad(): | |
209 | def testAbsoluteECDSA256Bad(self): # type: () -> None | |
210 | def bad(): # type: () -> None | |
230 | 211 | dns.dnssec.validate(abs_other_ecdsa256_soa, abs_ecdsa256_soa_rrsig, |
231 | 212 | abs_ecdsa256_keys, None, when3) |
232 | 213 | self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) |
233 | 214 | |
234 | 215 | @unittest.skipUnless(dns.dnssec._have_ecdsa, |
235 | 216 | "python ECDSA cannot be imported") |
236 | def testAbsoluteECDSA384Good(self): | |
217 | def testAbsoluteECDSA384Good(self): # type: () -> None | |
237 | 218 | dns.dnssec.validate(abs_ecdsa384_soa, abs_ecdsa384_soa_rrsig, |
238 | 219 | abs_ecdsa384_keys, None, when4) |
239 | 220 | |
240 | 221 | @unittest.skipUnless(dns.dnssec._have_ecdsa, |
241 | 222 | "python ECDSA cannot be imported") |
242 | def testAbsoluteECDSA384Bad(self): | |
243 | def bad(): | |
223 | def testAbsoluteECDSA384Bad(self): # type: () -> None | |
224 | def bad(): # type: () -> None | |
244 | 225 | dns.dnssec.validate(abs_other_ecdsa384_soa, abs_ecdsa384_soa_rrsig, |
245 | 226 | abs_ecdsa384_keys, None, when4) |
246 | 227 | self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) |
0 | # -*- coding: utf-8 | |
1 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
2 | ||
3 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
4 | # | |
5 | # Permission to use, copy, modify, and distribute this software and its | |
6 | # documentation for any purpose with or without fee is hereby granted, | |
7 | # provided that the above copyright notice and this permission notice | |
8 | # appear in all copies. | |
9 | # | |
10 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
11 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
13 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
16 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 | ||
18 | from __future__ import print_function | |
19 | ||
20 | import unittest | |
21 | ||
22 | from io import BytesIO | |
23 | ||
24 | import dns.edns | |
25 | ||
26 | class OptionTestCase(unittest.TestCase): | |
27 | def testGenericOption(self): | |
28 | opt = dns.edns.GenericOption(3, b'data') | |
29 | io = BytesIO() | |
30 | opt.to_wire(io) | |
31 | data = io.getvalue() | |
32 | self.assertEqual(data, b'data') | |
33 | ||
34 | def testECSOption_prefix_length(self): | |
35 | opt = dns.edns.ECSOption('1.2.255.33', 20) | |
36 | io = BytesIO() | |
37 | opt.to_wire(io) | |
38 | data = io.getvalue() | |
39 | self.assertEqual(data, b'\x00\x01\x14\x00\x01\x02\xf0') | |
40 | ||
41 | def testECSOption_from_wire(self): | |
42 | opt = dns.edns.option_from_wire(8, b'\x00\x01\x14\x00\x01\x02\xf0', | |
43 | 0, 7) | |
44 | self.assertEqual(opt.otype, dns.edns.ECS) | |
45 | self.assertEqual(opt.address, '1.2.240.0') | |
46 | self.assertEqual(opt.srclen, 20) | |
47 | self.assertEqual(opt.scopelen, 0) | |
48 | ||
49 | def testECSOption(self): | |
50 | opt = dns.edns.ECSOption('1.2.3.4', 24) | |
51 | io = BytesIO() | |
52 | opt.to_wire(io) | |
53 | data = io.getvalue() | |
54 | self.assertEqual(data, b'\x00\x01\x18\x00\x01\x02\x03') | |
55 | ||
56 | def testECSOption_v6(self): | |
57 | opt = dns.edns.ECSOption('2001:4b98::1') | |
58 | io = BytesIO() | |
59 | opt.to_wire(io) | |
60 | data = io.getvalue() | |
61 | self.assertEqual(data, b'\x00\x02\x38\x00\x20\x01\x4b\x98\x00\x00\x00') |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | try: | |
16 | import unittest2 as unittest | |
17 | except ImportError: | |
18 | import unittest | |
17 | import unittest | |
19 | 18 | |
20 | 19 | from dns.exception import DNSException |
21 | 20 | |
22 | 21 | |
23 | 22 | class FormatedError(DNSException): |
24 | 23 | fmt = "Custom format: {parameter}" |
25 | supp_kwargs = set(['parameter']) | |
24 | supp_kwargs = {'parameter'} | |
26 | 25 | |
27 | 26 | |
28 | 27 | class ExceptionTestCase(unittest.TestCase): |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | try: | |
16 | import unittest2 as unittest | |
17 | except ImportError: | |
18 | import unittest | |
17 | import unittest | |
19 | 18 | |
20 | 19 | import dns.flags |
21 | 20 | import dns.rcode |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
15 | 17 | import sys |
16 | 18 | sys.path.insert(0, '../') # Force the local project to be *the* dns |
17 | 19 | |
18 | try: | |
19 | import unittest2 as unittest | |
20 | except ImportError: | |
21 | import unittest | |
20 | import unittest | |
22 | 21 | |
23 | 22 | import dns.exception |
24 | 23 | import dns.rdata |
139 | 138 | |
140 | 139 | class GenerateTestCase(unittest.TestCase): |
141 | 140 | |
142 | def testFromText(self): | |
143 | def bad(): | |
141 | def testFromText(self): # type: () -> None | |
142 | def bad(): # type: () -> None | |
144 | 143 | dns.zone.from_text(example_text, 'example.', relativize=True) |
145 | 144 | self.failUnlessRaises(dns.zone.NoSOA, bad) |
146 | 145 | |
147 | def testFromText1(self): | |
148 | def bad(): | |
146 | def testFromText1(self): # type: () -> None | |
147 | def bad(): # type: () -> None | |
149 | 148 | dns.zone.from_text(example_text1, 'example.', relativize=True) |
150 | 149 | self.failUnlessRaises(dns.zone.NoSOA, bad) |
151 | 150 | |
152 | def testIterateAllRdatas2(self): | |
151 | def testIterateAllRdatas2(self): # type: () -> None | |
153 | 152 | z = dns.zone.from_text(example_text2, 'example.', relativize=True) |
154 | 153 | l = list(z.iterate_rdatas()) |
155 | 154 | l.sort(key=_rdata_sort) |
193 | 192 | exl.sort(key=_rdata_sort) |
194 | 193 | self.failUnless(l == exl) |
195 | 194 | |
196 | def testIterateAllRdatas3(self): | |
195 | def testIterateAllRdatas3(self): # type: () -> None | |
197 | 196 | z = dns.zone.from_text(example_text3, 'example.', relativize=True) |
198 | 197 | l = list(z.iterate_rdatas()) |
199 | 198 | l.sort(key=_rdata_sort) |
232 | 231 | '10.0.0.8'))] |
233 | 232 | exl.sort(key=_rdata_sort) |
234 | 233 | self.failUnless(l == exl) |
235 | def testGenerate1(self): | |
234 | def testGenerate1(self): # type: () -> None | |
236 | 235 | z = dns.zone.from_text(example_text4, 'example.', relativize=True) |
237 | 236 | l = list(z.iterate_rdatas()) |
238 | 237 | l.sort(key=_rdata_sort) |
278 | 277 | exl.sort(key=_rdata_sort) |
279 | 278 | self.assertEqual(l, exl) |
280 | 279 | |
281 | def testGenerate2(self): | |
280 | def testGenerate2(self): # type: () -> None | |
282 | 281 | z = dns.zone.from_text(example_text5, 'example.', relativize=True) |
283 | 282 | l = list(z.iterate_rdatas()) |
284 | 283 | l.sort(key=_rdata_sort) |
321 | 320 | exl.sort(key=_rdata_sort) |
322 | 321 | self.failUnless(l == exl) |
323 | 322 | |
324 | def testGenerate3(self): | |
323 | def testGenerate3(self): # type: () -> None | |
325 | 324 | z = dns.zone.from_text(example_text6, 'example.', relativize=True) |
326 | 325 | l = list(z.iterate_rdatas()) |
327 | 326 | l.sort(key=_rdata_sort) |
364 | 363 | exl.sort(key=_rdata_sort) |
365 | 364 | self.failUnless(l == exl) |
366 | 365 | |
367 | def testGenerate4(self): | |
366 | def testGenerate4(self): # type: () -> None | |
368 | 367 | z = dns.zone.from_text(example_text7, 'example.', relativize=True) |
369 | 368 | l = list(z.iterate_rdatas()) |
370 | 369 | l.sort(key=_rdata_sort) |
407 | 406 | exl.sort(key=_rdata_sort) |
408 | 407 | self.failUnless(l == exl) |
409 | 408 | |
410 | def testGenerate6(self): | |
409 | def testGenerate6(self): # type: () -> None | |
411 | 410 | z = dns.zone.from_text(example_text9, 'example.', relativize=True) |
412 | 411 | l = list(z.iterate_rdatas()) |
413 | 412 | l.sort(key=_rdata_sort) |
457 | 456 | exl.sort(key=_rdata_sort) |
458 | 457 | self.failUnless(l == exl) |
459 | 458 | |
460 | def testGenerate7(self): | |
459 | def testGenerate7(self): # type: () -> None | |
461 | 460 | z = dns.zone.from_text(example_text10, 'example.', relativize=True) |
462 | 461 | l = list(z.iterate_rdatas()) |
463 | 462 | l.sort(key=_rdata_sort) |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
15 | 17 | import sys |
16 | 18 | sys.path.insert(0, '../') |
17 | 19 | |
18 | try: | |
19 | import unittest2 as unittest | |
20 | except ImportError: | |
21 | import unittest | |
20 | import unittest | |
22 | 21 | |
23 | 22 | import dns |
24 | 23 | import dns.exception |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | try: | |
16 | import unittest2 as unittest | |
17 | except ImportError: | |
18 | import unittest | |
17 | import unittest | |
19 | 18 | import binascii |
20 | 19 | |
21 | 20 | import dns.exception |
22 | 21 | import dns.flags |
23 | 22 | import dns.message |
23 | import dns.name | |
24 | import dns.rdataclass | |
25 | import dns.rdatatype | |
24 | 26 | from dns._compat import xrange |
25 | 27 | |
26 | 28 | query_text = """id 1234 |
198 | 200 | m = dns.message.make_query('foo', 'A', options=[]) |
199 | 201 | self.failUnless(m.edns == 0) |
200 | 202 | |
203 | def test_FindRRset(self): | |
204 | a = dns.message.from_text(answer_text) | |
205 | n = dns.name.from_text('dnspython.org.') | |
206 | rrs1 = a.find_rrset(a.answer, n, dns.rdataclass.IN, dns.rdatatype.SOA) | |
207 | rrs2 = a.find_rrset(dns.message.ANSWER, n, dns.rdataclass.IN, | |
208 | dns.rdatatype.SOA) | |
209 | self.failUnless(rrs1 == rrs2) | |
210 | ||
201 | 211 | if __name__ == '__main__': |
202 | 212 | unittest.main() |
0 | 0 | # -*- coding: utf-8 |
1 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
1 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
2 | ||
3 | # Copyright (C) 2003-2017 Nominum, Inc. | |
2 | 4 | # |
3 | 5 | # Permission to use, copy, modify, and distribute this software and its |
4 | 6 | # documentation for any purpose with or without fee is hereby granted, |
15 | 17 | |
16 | 18 | from __future__ import print_function |
17 | 19 | |
18 | try: | |
19 | import unittest2 as unittest | |
20 | except ImportError: | |
21 | import unittest | |
20 | from typing import Dict # pylint: disable=unused-import | |
21 | import unittest | |
22 | 22 | |
23 | 23 | from io import BytesIO |
24 | 24 | |
26 | 26 | import dns.reversename |
27 | 27 | import dns.e164 |
28 | 28 | |
29 | # pylint: disable=line-too-long | |
29 | # pylint: disable=line-too-long,unsupported-assignment-operation | |
30 | 30 | |
31 | 31 | |
32 | 32 | class NameTestCase(unittest.TestCase): |
379 | 379 | def testToWire1(self): |
380 | 380 | n = dns.name.from_text('FOO.bar') |
381 | 381 | f = BytesIO() |
382 | compress = {} | |
382 | compress = {} # type: Dict[dns.name.Name,int] | |
383 | 383 | n.to_wire(f, compress) |
384 | 384 | self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00') |
385 | 385 | |
386 | 386 | def testToWire2(self): |
387 | 387 | n = dns.name.from_text('FOO.bar') |
388 | 388 | f = BytesIO() |
389 | compress = {} | |
389 | compress = {} # type: Dict[dns.name.Name,int] | |
390 | 390 | n.to_wire(f, compress) |
391 | 391 | n.to_wire(f, compress) |
392 | 392 | self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00\xc0\x00') |
395 | 395 | n1 = dns.name.from_text('FOO.bar') |
396 | 396 | n2 = dns.name.from_text('foo.bar') |
397 | 397 | f = BytesIO() |
398 | compress = {} | |
398 | compress = {} # type: Dict[dns.name.Name,int] | |
399 | 399 | n1.to_wire(f, compress) |
400 | 400 | n2.to_wire(f, compress) |
401 | 401 | self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00\xc0\x00') |
404 | 404 | n1 = dns.name.from_text('FOO.bar') |
405 | 405 | n2 = dns.name.from_text('a.foo.bar') |
406 | 406 | f = BytesIO() |
407 | compress = {} | |
407 | compress = {} # type: Dict[dns.name.Name,int] | |
408 | 408 | n1.to_wire(f, compress) |
409 | 409 | n2.to_wire(f, compress) |
410 | 410 | self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00\x01\x61\xc0\x00') |
413 | 413 | n1 = dns.name.from_text('FOO.bar') |
414 | 414 | n2 = dns.name.from_text('a.foo.bar') |
415 | 415 | f = BytesIO() |
416 | compress = {} | |
416 | compress = {} # type: Dict[dns.name.Name,int] | |
417 | 417 | n1.to_wire(f, compress) |
418 | 418 | n2.to_wire(f, None) |
419 | 419 | self.assertEqual(f.getvalue(), |
428 | 428 | def bad(): |
429 | 429 | n = dns.name.from_text('FOO.bar', None) |
430 | 430 | f = BytesIO() |
431 | compress = {} | |
431 | compress = {} # type: Dict[dns.name.Name,int] | |
432 | 432 | n.to_wire(f, compress) |
433 | 433 | self.failUnlessRaises(dns.name.NeedAbsoluteNameOrOrigin, bad) |
434 | 434 | |
763 | 763 | |
764 | 764 | def testForwardIPv4(self): |
765 | 765 | n = dns.name.from_text('1.0.0.127.in-addr.arpa.') |
766 | e = b'127.0.0.1' | |
766 | e = '127.0.0.1' | |
767 | 767 | text = dns.reversename.to_address(n) |
768 | 768 | self.assertEqual(text, e) |
769 | 769 | |
781 | 781 | |
782 | 782 | def testEnumToE164(self): |
783 | 783 | n = dns.name.from_text('2.1.2.1.5.5.5.0.5.6.1.e164.arpa.') |
784 | e = b'+16505551212' | |
784 | e = '+16505551212' | |
785 | 785 | text = dns.e164.to_e164(n) |
786 | 786 | self.assertEqual(text, e) |
787 | 787 |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | try: | |
16 | import unittest2 as unittest | |
17 | except ImportError: | |
18 | import unittest | |
17 | import unittest | |
19 | 18 | |
20 | 19 | import dns.name |
21 | 20 | import dns.namedict |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2006-2017 Nominum, Inc. | |
3 | # | |
4 | # Permission to use, copy, modify, and distribute this software and its | |
5 | # documentation for any purpose with or without fee is hereby granted, | |
6 | # provided that the above copyright notice and this permission notice | |
7 | # appear in all copies. | |
8 | # | |
9 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | ||
17 | import unittest | |
18 | ||
19 | import dns.rdata | |
20 | import dns.rdataclass | |
21 | import dns.rdatatype | |
22 | import dns.rdtypes.ANY.TXT | |
23 | import dns.ttl | |
24 | ||
25 | class NSEC3TestCase(unittest.TestCase): | |
26 | def test_NSEC3_bitmap(self): | |
27 | rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NSEC3, | |
28 | u"1 0 100 ABCD SCBCQHKU35969L2A68P3AD59LHF30715 A CAA TYPE65534") | |
29 | bitmap = bytearray(b'\0' * 32) | |
30 | bitmap[31] = bitmap[31] | 2 | |
31 | self.assertEqual(rdata.windows, [(0, bytearray(b'@')), | |
32 | (1, bytearray(b'@')), # CAA = 257 | |
33 | (255, bitmap) | |
34 | ]) | |
35 | ||
36 | if __name__ == '__main__': | |
37 | unittest.main() |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
14 | 16 | |
15 | 17 | from __future__ import print_function |
16 | 18 | |
17 | try: | |
18 | import unittest2 as unittest | |
19 | except ImportError: | |
20 | import unittest | |
19 | import unittest | |
21 | 20 | import binascii |
22 | 21 | |
23 | 22 | import dns.exception |
24 | 23 | import dns.ipv4 |
25 | 24 | import dns.ipv6 |
25 | import dns.inet | |
26 | 26 | |
27 | 27 | # for convenience |
28 | 28 | aton4 = dns.ipv4.inet_aton |
169 | 169 | self.failUnlessRaises(ValueError, bad) |
170 | 170 | |
171 | 171 | def test_good_v4_aton(self): |
172 | pairs = [(b'1.2.3.4', b'\x01\x02\x03\x04'), | |
173 | (b'255.255.255.255', b'\xff\xff\xff\xff'), | |
174 | (b'0.0.0.0', b'\x00\x00\x00\x00')] | |
172 | pairs = [('1.2.3.4', b'\x01\x02\x03\x04'), | |
173 | ('255.255.255.255', b'\xff\xff\xff\xff'), | |
174 | ('0.0.0.0', b'\x00\x00\x00\x00')] | |
175 | 175 | for (t, b) in pairs: |
176 | 176 | b1 = aton4(t) |
177 | 177 | t1 = ntoa4(b1) |
213 | 213 | self.failUnless(dns.ipv6.is_mapped(aton6(t2))) |
214 | 214 | self.failIf(dns.ipv6.is_mapped(aton6(t3))) |
215 | 215 | |
216 | def test_is_multicast(self): | |
217 | t1 = '223.0.0.1' | |
218 | t2 = '240.0.0.1' | |
219 | t3 = '224.0.0.1' | |
220 | t4 = '239.0.0.1' | |
221 | t5 = 'fe00::1' | |
222 | t6 = 'ff00::1' | |
223 | self.failIf(dns.inet.is_multicast(t1)) | |
224 | self.failIf(dns.inet.is_multicast(t2)) | |
225 | self.failUnless(dns.inet.is_multicast(t3)) | |
226 | self.failUnless(dns.inet.is_multicast(t4)) | |
227 | self.failIf(dns.inet.is_multicast(t5)) | |
228 | self.failUnless(dns.inet.is_multicast(t6)) | |
229 | ||
216 | 230 | if __name__ == '__main__': |
217 | 231 | unittest.main() |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | try: | |
16 | import unittest2 as unittest | |
17 | except ImportError: | |
18 | import unittest | |
17 | import unittest | |
19 | 18 | |
20 | 19 | import dns.rdata |
21 | 20 | import dns.rdataclass |
22 | 21 | import dns.rdatatype |
22 | ||
23 | import tests.ttxt_module | |
23 | 24 | |
24 | 25 | class RdataTestCase(unittest.TestCase): |
25 | 26 | |
31 | 32 | rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, u"1.2.3.4") |
32 | 33 | self.failUnless(rdata.address == "1.2.3.4") |
33 | 34 | |
35 | def test_module_registration(self): | |
36 | TTXT = 64001 | |
37 | dns.rdata.register_type(tests.ttxt_module, TTXT, 'TTXT') | |
38 | rdata = dns.rdata.from_text(dns.rdataclass.IN, TTXT, 'hello world') | |
39 | self.failUnless(rdata.strings == [b'hello', b'world']) | |
40 | self.failUnless(dns.rdatatype.to_text(TTXT) == 'TTXT') | |
41 | self.failUnless(dns.rdatatype.from_text('TTXT') == TTXT) | |
42 | ||
43 | def test_module_reregistration(self): | |
44 | def bad(): | |
45 | TTXTTWO = dns.rdatatype.TXT | |
46 | dns.rdata.register_type(tests.ttxt_module, TTXTTWO, 'TTXTTWO') | |
47 | self.failUnlessRaises(dns.rdata.RdatatypeExists, bad) | |
48 | ||
34 | 49 | if __name__ == '__main__': |
35 | 50 | unittest.main() |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | try: | |
16 | import unittest2 as unittest | |
17 | except ImportError: | |
18 | import unittest | |
17 | import unittest | |
19 | 18 | |
20 | 19 | import dns.rdataclass |
21 | 20 | import dns.rdatatype |
13 | 13 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
14 | 14 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | 15 | |
16 | try: | |
17 | import unittest2 as unittest | |
18 | except ImportError: | |
19 | import unittest | |
16 | import unittest | |
20 | 17 | |
21 | 18 | import dns.rrset |
22 | 19 | import dns.rdtypes.ANY.DNSKEY |
23 | 20 | |
21 | from typing import Set # pylint: disable=unused-import | |
24 | 22 | |
25 | 23 | class RdtypeAnyDnskeyTestCase(unittest.TestCase): |
26 | 24 | |
27 | def testFlagsEmpty(self): | |
25 | def testFlagsEmpty(self): # type: () -> None | |
28 | 26 | '''Test DNSKEY flag to/from text conversion for zero flag/empty set.''' |
29 | good_s = set() | |
27 | good_s = set() #type: Set[str] | |
30 | 28 | good_f = 0 |
31 | 29 | from_flags = dns.rdtypes.ANY.DNSKEY.flags_to_text_set(good_f) |
32 | 30 | self.failUnless(from_flags == good_s, |
33 | '"%s" != "%s"' % (from_flags, good_s)) | |
31 | '"{}" != "{}"'.format(from_flags, good_s)) | |
34 | 32 | from_set = dns.rdtypes.ANY.DNSKEY.flags_from_text_set(good_s) |
35 | 33 | self.failUnless(from_set == good_f, |
36 | '"0x%x" != "0x%x"' % (from_set, good_f)) | |
34 | '"0x{:x}" != "0x{:x}"'.format(from_set, good_f)) | |
37 | 35 | |
38 | def testFlagsAll(self): | |
36 | def testFlagsAll(self): # type: () -> None | |
39 | 37 | '''Test that all defined flags are recognized.''' |
40 | good_s = set(['SEP', 'REVOKE', 'ZONE']) | |
38 | good_s = {'SEP', 'REVOKE', 'ZONE'} | |
41 | 39 | good_f = 0x181 |
42 | 40 | from_flags = dns.rdtypes.ANY.DNSKEY.flags_to_text_set(good_f) |
43 | 41 | self.failUnless(from_flags == good_s, |
44 | '"%s" != "%s"' % (from_flags, good_s)) | |
42 | '"{}" != "{}"'.format(from_flags, good_s)) | |
45 | 43 | from_text = dns.rdtypes.ANY.DNSKEY.flags_from_text_set(good_s) |
46 | 44 | self.failUnless(from_text == good_f, |
47 | '"0x%x" != "0x%x"' % (from_text, good_f)) | |
45 | '"0x{:x}" != "0x{:x}"'.format(from_text, good_f)) | |
48 | 46 | |
49 | def testFlagsUnknownToText(self): | |
47 | def testFlagsUnknownToText(self): # type: () -> None | |
50 | 48 | '''Test that undefined flags are returned in hexadecimal notation.''' |
51 | unk_s = set(['0x8000']) | |
49 | unk_s = {'0x8000'} | |
52 | 50 | flags_s = dns.rdtypes.ANY.DNSKEY.flags_to_text_set(0x8000) |
53 | self.failUnless(flags_s == unk_s, '"%s" != "%s"' % (flags_s, unk_s)) | |
51 | self.failUnless(flags_s == unk_s, '"{}" != "{}"'.format(flags_s, unk_s)) | |
54 | 52 | |
55 | def testFlagsUnknownToFlags(self): | |
53 | def testFlagsUnknownToFlags(self): # type: () -> None | |
56 | 54 | '''Test that conversion from undefined mnemonic raises error.''' |
57 | 55 | self.failUnlessRaises(NotImplementedError, |
58 | 56 | dns.rdtypes.ANY.DNSKEY.flags_from_text_set, |
59 | 57 | (['0x8000'])) |
60 | 58 | |
61 | def testFlagsRRToText(self): | |
59 | def testFlagsRRToText(self): # type: () -> None | |
62 | 60 | '''Test that RR method returns correct flags.''' |
63 | 61 | rr = dns.rrset.from_text('foo', 300, 'IN', 'DNSKEY', '257 3 8 KEY=')[0] |
64 | rr_s = set(['ZONE', 'SEP']) | |
62 | rr_s = {'ZONE', 'SEP'} | |
65 | 63 | flags_s = rr.flags_to_text_set() |
66 | self.failUnless(flags_s == rr_s, '"%s" != "%s"' % (flags_s, rr_s)) | |
64 | self.failUnless(flags_s == rr_s, '"{}" != "{}"'.format(flags_s, rr_s)) | |
67 | 65 | |
68 | 66 | |
69 | 67 | if __name__ == '__main__': |
13 | 13 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
14 | 14 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | 15 | |
16 | try: | |
17 | import unittest2 as unittest | |
18 | except ImportError: | |
19 | import unittest | |
16 | import unittest | |
20 | 17 | from io import BytesIO |
21 | 18 | |
22 | 19 | import dns.rrset |
13 | 13 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
14 | 14 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | 15 | |
16 | try: | |
17 | import unittest2 as unittest | |
18 | except ImportError: | |
19 | import unittest | |
16 | import unittest | |
20 | 17 | |
21 | 18 | import dns.rrset |
22 | 19 | import dns.rdtypes.ANY.LOC |
30 | 27 | r2 = dns.rrset.from_text('FOO', 600, 'in', 'loc', |
31 | 28 | '49 11 42.400 N 16 36 29.600 E 227.64m ' |
32 | 29 | '1.00m 10000.00m 10.00m') |
33 | self.failUnless(r1 == r2, '"%s" != "%s"' % (r1, r2)) | |
30 | self.failUnless(r1 == r2, '"{}" != "{}"'.format(r1, r2)) | |
34 | 31 | |
35 | 32 | def testEqual2(self): |
36 | 33 | '''Test default values for size, horizontal and vertical precision.''' |
41 | 38 | (16, 36, 29, 600, 1), |
42 | 39 | 22764.0, # centimeters |
43 | 40 | 100.0, 1000000.00, 1000.0) # centimeters |
44 | self.failUnless(r1 == r2, '"%s" != "%s"' % (r1, r2)) | |
41 | self.failUnless(r1 == r2, '"{}" != "{}"'.format(r1, r2)) | |
45 | 42 | |
46 | 43 | def testEqual3(self): |
47 | 44 | '''Test size, horizontal and vertical precision parsers: 100 cm == 1 m. |
53 | 50 | r2 = dns.rrset.from_text('FOO', 600, 'in', 'loc', |
54 | 51 | '49 11 42.400 N 16 36 29.600 E 227.64m ' |
55 | 52 | '2.00m 10.00m 2.00m')[0] |
56 | self.failUnless(r1 == r2, '"%s" != "%s"' % (r1, r2)) | |
53 | self.failUnless(r1 == r2, '"{}" != "{}"'.format(r1, r2)) | |
57 | 54 | |
58 | 55 | def testEqual4(self): |
59 | 56 | '''Test size, horizontal and vertical precision parsers without unit. |
66 | 63 | r2 = dns.rrset.from_text('FOO', 600, 'in', 'loc', |
67 | 64 | '49 11 42.400 N 16 36 29.600 E 227.64 ' |
68 | 65 | '2 10 2')[0] # meters without explicit unit |
69 | self.failUnless(r1 == r2, '"%s" != "%s"' % (r1, r2)) | |
66 | self.failUnless(r1 == r2, '"{}" != "{}"'.format(r1, r2)) | |
70 | 67 | |
71 | 68 | if __name__ == '__main__': |
72 | 69 | unittest.main() |
0 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. | |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
2 | # Copyright (C) 2003-2017 Nominum, Inc. | |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
3 | 5 | # documentation for any purpose with or without fee is hereby granted, |
17 | 19 | import sys |
18 | 20 | import socket |
19 | 21 | import time |
20 | try: | |
21 | import unittest2 as unittest | |
22 | except ImportError: | |
23 | import unittest | |
22 | import unittest | |
24 | 23 | |
25 | 24 | import dns.message |
26 | 25 | import dns.name |
27 | 26 | import dns.rdataclass |
28 | 27 | import dns.rdatatype |
29 | 28 | import dns.resolver |
30 | from dns._compat import xrange | |
29 | from dns._compat import xrange, PY3 | |
31 | 30 | |
32 | 31 | # Some tests require the internet to be available to run, so let's |
33 | 32 | # skip those if it's not there. |
132 | 131 | time.sleep(2) |
133 | 132 | self.failUnless(cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) |
134 | 133 | is None) |
134 | ||
135 | def testIndexErrorOnEmptyRRsetAccess(self): | |
136 | def bad(): | |
137 | message = dns.message.from_text(message_text) | |
138 | name = dns.name.from_text('example.') | |
139 | answer = dns.resolver.Answer(name, dns.rdatatype.MX, | |
140 | dns.rdataclass.IN, message, | |
141 | False) | |
142 | return answer[0] | |
143 | self.failUnlessRaises(IndexError, bad) | |
144 | ||
145 | def testIndexErrorOnEmptyRRsetDelete(self): | |
146 | def bad(): | |
147 | message = dns.message.from_text(message_text) | |
148 | name = dns.name.from_text('example.') | |
149 | answer = dns.resolver.Answer(name, dns.rdatatype.MX, | |
150 | dns.rdataclass.IN, message, | |
151 | False) | |
152 | del answer[0] | |
153 | self.failUnlessRaises(IndexError, bad) | |
135 | 154 | |
136 | 155 | @unittest.skipIf(not _network_available, "Internet not reachable") |
137 | 156 | def testZoneForName1(self): |
259 | 278 | def test_nxdomain_compatible(self): |
260 | 279 | n1 = dns.name.Name(('a', 'b', '')) |
261 | 280 | n2 = dns.name.Name(('a', 'b', 's', '')) |
262 | py3 = (sys.version_info[0] > 2) | |
263 | 281 | |
264 | 282 | try: |
265 | 283 | raise dns.resolver.NXDOMAIN |
266 | 284 | except dns.exception.DNSException as e: |
267 | if not py3: | |
285 | if not PY3: | |
286 | # pylint: disable=exception-message-attribute | |
268 | 287 | self.assertTrue((e.message == e.__doc__)) |
269 | 288 | self.assertTrue((e.args == (e.__doc__,))) |
270 | 289 | self.assertTrue(('kwargs' in dir(e))) |
275 | 294 | try: |
276 | 295 | raise dns.resolver.NXDOMAIN("errmsg") |
277 | 296 | except dns.exception.DNSException as e: |
278 | if not py3: | |
297 | if not PY3: | |
298 | # pylint: disable=exception-message-attribute | |
279 | 299 | self.assertTrue((e.message == "errmsg")) |
280 | 300 | self.assertTrue((e.args == ("errmsg",))) |
281 | 301 | self.assertTrue(('kwargs' in dir(e))) |
286 | 306 | try: |
287 | 307 | raise dns.resolver.NXDOMAIN("errmsg", -1) |
288 | 308 | except dns.exception.DNSException as e: |
289 | if not py3: | |
309 | if not PY3: | |
310 | # pylint: disable=exception-message-attribute | |
290 | 311 | self.assertTrue((e.message == "")) |
291 | 312 | self.assertTrue((e.args == ("errmsg", -1))) |
292 | 313 | self.assertTrue(('kwargs' in dir(e))) |
313 | 334 | raise dns.resolver.NXDOMAIN(qnames=[n1]) |
314 | 335 | except dns.exception.DNSException as e: |
315 | 336 | MSG = "The DNS query name does not exist: a.b." |
316 | if not py3: | |
337 | if not PY3: | |
338 | # pylint: disable=exception-message-attribute | |
317 | 339 | self.assertTrue((e.message == MSG), e.message) |
318 | 340 | self.assertTrue((e.args == (MSG,)), repr(e.args)) |
319 | 341 | self.assertTrue(('kwargs' in dir(e))) |
325 | 347 | |
326 | 348 | try: |
327 | 349 | raise dns.resolver.NXDOMAIN(qnames=[n2, n1]) |
328 | except Exception as e: | |
350 | except dns.resolver.NXDOMAIN as e: | |
329 | 351 | e0 = dns.resolver.NXDOMAIN("errmsg") |
330 | 352 | e = e0 + e |
331 | 353 | MSG = "None of DNS query names exist: a.b.s., a.b." |
332 | if not py3: | |
354 | if not PY3: | |
355 | # pylint: disable=exception-message-attribute | |
333 | 356 | self.assertTrue((e.message == MSG), e.message) |
334 | 357 | self.assertTrue((e.args == (MSG,)), repr(e.args)) |
335 | 358 | self.assertTrue(('kwargs' in dir(e))) |
346 | 369 | |
347 | 370 | try: |
348 | 371 | raise dns.resolver.NXDOMAIN(qnames=[n1], responses={n1: 'r1.1'}) |
349 | except Exception as e: | |
372 | except dns.resolver.NXDOMAIN as e: | |
350 | 373 | MSG = "The DNS query name does not exist: a.b." |
351 | if not py3: | |
374 | if not PY3: | |
375 | # pylint: disable=exception-message-attribute | |
352 | 376 | self.assertTrue((e.message == MSG), e.message) |
353 | 377 | self.assertTrue((e.args == (MSG,)), repr(e.args)) |
354 | 378 | self.assertTrue(('kwargs' in dir(e))) |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | try: | |
16 | import unittest2 as unittest | |
17 | except ImportError: | |
18 | import unittest | |
17 | import unittest | |
19 | 18 | |
20 | 19 | import dns.rrset |
21 | 20 |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | try: | |
16 | import unittest2 as unittest | |
17 | except ImportError: | |
18 | import unittest | |
17 | import unittest | |
19 | 18 | |
20 | 19 | import dns.set |
21 | 20 |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | try: | |
16 | import unittest2 as unittest | |
17 | except ImportError: | |
18 | import unittest | |
17 | import unittest | |
19 | 18 | |
20 | 19 | import dns.exception |
21 | 20 | import dns.tokenizer |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
12 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 16 | |
15 | try: | |
16 | import unittest2 as unittest | |
17 | except ImportError: | |
18 | import unittest | |
17 | import unittest | |
19 | 18 | import binascii |
20 | 19 | |
21 | 20 | import dns.update |
62 | 61 | |
63 | 62 | class UpdateTestCase(unittest.TestCase): |
64 | 63 | |
65 | def test_to_wire1(self): | |
64 | def test_to_wire1(self): # type: () -> None | |
66 | 65 | update = dns.update.Update('example') |
67 | 66 | update.id = 1 |
68 | 67 | update.present('foo') |
77 | 76 | update.delete('blaz2') |
78 | 77 | self.failUnless(update.to_wire() == goodwire) |
79 | 78 | |
80 | def test_to_wire2(self): | |
79 | def test_to_wire2(self): # type: () -> None | |
81 | 80 | update = dns.update.Update('example') |
82 | 81 | update.id = 1 |
83 | 82 | update.present('foo') |
92 | 91 | update.delete('blaz2') |
93 | 92 | self.failUnless(update.to_wire() == goodwire) |
94 | 93 | |
95 | def test_to_wire3(self): | |
94 | def test_to_wire3(self): # type: () -> None | |
96 | 95 | update = dns.update.Update('example') |
97 | 96 | update.id = 1 |
98 | 97 | update.present('foo') |
107 | 106 | update.delete('blaz2') |
108 | 107 | self.failUnless(update.to_wire() == goodwire) |
109 | 108 | |
110 | def test_from_text1(self): | |
109 | def test_from_text1(self): # type: () -> None | |
111 | 110 | update = dns.message.from_text(update_text) |
112 | 111 | w = update.to_wire(origin=dns.name.from_text('example'), |
113 | 112 | want_shuffle=False) |
5 | 5 | # provided that the above copyright notice and this permission notice |
6 | 6 | # appear in all copies. |
7 | 7 | |
8 | try: | |
9 | import unittest2 as unittest | |
10 | except ImportError: | |
11 | import unittest | |
8 | import unittest | |
12 | 9 | |
13 | 10 | from dns.exception import FormError |
14 | 11 | from dns.wiredata import WireData |
0 | # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license | |
1 | ||
0 | 2 | # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. |
1 | 3 | # |
2 | 4 | # Permission to use, copy, modify, and distribute this software and its |
15 | 17 | from io import BytesIO, StringIO |
16 | 18 | import filecmp |
17 | 19 | import os |
18 | try: | |
19 | import unittest2 as unittest | |
20 | except ImportError: | |
21 | import unittest | |
20 | import unittest | |
21 | from typing import cast | |
22 | 22 | |
23 | 23 | import dns.exception |
24 | 24 | import dns.rdata |
25 | import dns.rdataset | |
25 | 26 | import dns.rdataclass |
26 | 27 | import dns.rdatatype |
27 | 28 | import dns.rrset |
28 | 29 | import dns.zone |
30 | import dns.node | |
29 | 31 | |
30 | 32 | def here(filename): |
31 | 33 | return os.path.join(os.path.dirname(__file__), filename) |
75 | 77 | ns2 1w1D1h1m1S a 10.0.0.2 |
76 | 78 | """ |
77 | 79 | |
80 | # No $TTL so default TTL for RRs should be inherited from SOA minimum TTL ( | |
81 | # not from the last explicit RR TTL). | |
82 | ttl_from_soa_text = """$ORIGIN example. | |
83 | @ 1h soa foo bar 1 2 3 4 5 | |
84 | @ 1h ns ns1 | |
85 | @ 1h ns ns2 | |
86 | ns1 1w1D1h1m1S a 10.0.0.2 | |
87 | ns2 a 10.0.0.1 | |
88 | """ | |
89 | ||
90 | # No $TTL and no SOA, so default TTL for RRs should be inherited from last | |
91 | # explicit RR TTL. | |
92 | ttl_from_last_text = """$ORIGIN example. | |
93 | @ 1h ns ns1 | |
94 | @ 1h ns ns2 | |
95 | ns1 a 10.0.0.1 | |
96 | ns2 1w1D1h1m1S a 10.0.0.2 | |
97 | """ | |
98 | ||
99 | # No $TTL and no SOA should raise SyntaxError as no TTL can be determined. | |
100 | no_ttl_text = """$ORIGIN example. | |
101 | @ ns ns1 | |
102 | @ ns ns2 | |
103 | ns1 a 10.0.0.1 | |
104 | ns2 a 10.0.0.2 | |
105 | """ | |
106 | ||
78 | 107 | no_soa_text = """$TTL 1h |
79 | 108 | $ORIGIN example. |
80 | 109 | @ ns ns1 |
107 | 136 | |
108 | 137 | class ZoneTestCase(unittest.TestCase): |
109 | 138 | |
110 | def testFromFile1(self): | |
139 | def testFromFile1(self): # type: () -> None | |
111 | 140 | z = dns.zone.from_file(here('example'), 'example') |
112 | 141 | ok = False |
113 | 142 | try: |
119 | 148 | os.unlink(here('example1.out')) |
120 | 149 | self.failUnless(ok) |
121 | 150 | |
122 | def testFromFile2(self): | |
151 | def testFromFile2(self): # type: () -> None | |
123 | 152 | z = dns.zone.from_file(here('example'), 'example', relativize=False) |
124 | 153 | ok = False |
125 | 154 | try: |
131 | 160 | os.unlink(here('example2.out')) |
132 | 161 | self.failUnless(ok) |
133 | 162 | |
134 | def testToFileTextualStream(self): | |
163 | def testToFileTextualStream(self): # type: () -> None | |
135 | 164 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
136 | 165 | f = StringIO() |
137 | 166 | z.to_file(f) |
139 | 168 | f.close() |
140 | 169 | self.assertEqual(out, example_text_output) |
141 | 170 | |
142 | def testToFileBinaryStream(self): | |
171 | def testToFileBinaryStream(self): # type: () -> None | |
143 | 172 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
144 | 173 | f = BytesIO() |
145 | 174 | z.to_file(f) |
147 | 176 | f.close() |
148 | 177 | self.assertEqual(out, example_text_output.encode()) |
149 | 178 | |
150 | def testToFileTextual(self): | |
179 | def testToFileTextual(self): # type: () -> None | |
151 | 180 | z = dns.zone.from_file(here('example'), 'example') |
152 | 181 | try: |
153 | 182 | f = open(here('example3-textual.out'), 'w') |
160 | 189 | os.unlink(here('example3-textual.out')) |
161 | 190 | self.failUnless(ok) |
162 | 191 | |
163 | def testToFileBinary(self): | |
192 | def testToFileBinary(self): # type: () -> None | |
164 | 193 | z = dns.zone.from_file(here('example'), 'example') |
165 | 194 | try: |
166 | 195 | f = open(here('example3-binary.out'), 'wb') |
173 | 202 | os.unlink(here('example3-binary.out')) |
174 | 203 | self.failUnless(ok) |
175 | 204 | |
176 | def testToFileFilename(self): | |
205 | def testToFileFilename(self): # type: () -> None | |
177 | 206 | z = dns.zone.from_file(here('example'), 'example') |
178 | 207 | try: |
179 | z.to_file('example3-filename.out') | |
208 | z.to_file(here('example3-filename.out')) | |
180 | 209 | ok = filecmp.cmp(here('example3-filename.out'), |
181 | 210 | here('example3.good')) |
182 | 211 | finally: |
184 | 213 | os.unlink(here('example3-filename.out')) |
185 | 214 | self.failUnless(ok) |
186 | 215 | |
187 | def testToText(self): | |
216 | def testToText(self): # type: () -> None | |
188 | 217 | z = dns.zone.from_file(here('example'), 'example') |
189 | 218 | ok = False |
190 | 219 | try: |
199 | 228 | os.unlink(here('example3.out')) |
200 | 229 | self.failUnless(ok) |
201 | 230 | |
202 | def testFromText(self): | |
231 | def testFromText(self): # type: () -> None | |
203 | 232 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
204 | 233 | f = StringIO() |
205 | 234 | names = list(z.nodes.keys()) |
209 | 238 | f.write(u'\n') |
210 | 239 | self.assertEqual(f.getvalue(), example_text_output) |
211 | 240 | |
212 | def testTorture1(self): | |
241 | def testTorture1(self): # type: () -> None | |
213 | 242 | # |
214 | 243 | # Read a zone containing all our supported RR types, and |
215 | 244 | # for each RR in the zone, convert the rdata into wire format |
230 | 259 | origin=o) |
231 | 260 | self.failUnless(rd == rd2) |
232 | 261 | |
233 | def testEqual(self): | |
262 | def testEqual(self): # type: () -> None | |
234 | 263 | z1 = dns.zone.from_text(example_text, 'example.', relativize=True) |
235 | 264 | z2 = dns.zone.from_text(example_text_output, 'example.', |
236 | 265 | relativize=True) |
237 | 266 | self.failUnless(z1 == z2) |
238 | 267 | |
239 | def testNotEqual1(self): | |
268 | def testNotEqual1(self): # type: () -> None | |
240 | 269 | z1 = dns.zone.from_text(example_text, 'example.', relativize=True) |
241 | 270 | z2 = dns.zone.from_text(something_quite_similar, 'example.', |
242 | 271 | relativize=True) |
243 | 272 | self.failUnless(z1 != z2) |
244 | 273 | |
245 | def testNotEqual2(self): | |
274 | def testNotEqual2(self): # type: () -> None | |
246 | 275 | z1 = dns.zone.from_text(example_text, 'example.', relativize=True) |
247 | 276 | z2 = dns.zone.from_text(something_different, 'example.', |
248 | 277 | relativize=True) |
249 | 278 | self.failUnless(z1 != z2) |
250 | 279 | |
251 | def testNotEqual3(self): | |
280 | def testNotEqual3(self): # type: () -> None | |
252 | 281 | z1 = dns.zone.from_text(example_text, 'example.', relativize=True) |
253 | 282 | z2 = dns.zone.from_text(something_different, 'example2.', |
254 | 283 | relativize=True) |
255 | 284 | self.failUnless(z1 != z2) |
256 | 285 | |
257 | def testFindRdataset1(self): | |
286 | def testFindRdataset1(self): # type: () -> None | |
258 | 287 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
259 | 288 | rds = z.find_rdataset('@', 'soa') |
260 | 289 | exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') |
261 | 290 | self.failUnless(rds == exrds) |
262 | 291 | |
263 | def testFindRdataset2(self): | |
264 | def bad(): | |
292 | def testFindRdataset2(self): # type: () -> None | |
293 | def bad(): # type: () -> None | |
265 | 294 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
266 | 295 | z.find_rdataset('@', 'loc') |
267 | 296 | self.failUnlessRaises(KeyError, bad) |
268 | 297 | |
269 | def testFindRRset1(self): | |
298 | def testFindRRset1(self): # type: () -> None | |
270 | 299 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
271 | 300 | rrs = z.find_rrset('@', 'soa') |
272 | 301 | exrrs = dns.rrset.from_text('@', 300, 'IN', 'SOA', 'foo bar 1 2 3 4 5') |
273 | 302 | self.failUnless(rrs == exrrs) |
274 | 303 | |
275 | def testFindRRset2(self): | |
276 | def bad(): | |
304 | def testFindRRset2(self): # type: () -> None | |
305 | def bad(): # type: () -> None | |
277 | 306 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
278 | 307 | z.find_rrset('@', 'loc') |
279 | 308 | self.failUnlessRaises(KeyError, bad) |
280 | 309 | |
281 | def testGetRdataset1(self): | |
310 | def testGetRdataset1(self): # type: () -> None | |
282 | 311 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
283 | 312 | rds = z.get_rdataset('@', 'soa') |
284 | 313 | exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') |
285 | 314 | self.failUnless(rds == exrds) |
286 | 315 | |
287 | def testGetRdataset2(self): | |
316 | def testGetRdataset2(self): # type: () -> None | |
288 | 317 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
289 | 318 | rds = z.get_rdataset('@', 'loc') |
290 | 319 | self.failUnless(rds is None) |
291 | 320 | |
292 | def testGetRRset1(self): | |
321 | def testGetRRset1(self): # type: () -> None | |
293 | 322 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
294 | 323 | rrs = z.get_rrset('@', 'soa') |
295 | 324 | exrrs = dns.rrset.from_text('@', 300, 'IN', 'SOA', 'foo bar 1 2 3 4 5') |
296 | 325 | self.failUnless(rrs == exrrs) |
297 | 326 | |
298 | def testGetRRset2(self): | |
327 | def testGetRRset2(self): # type: () -> None | |
299 | 328 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
300 | 329 | rrs = z.get_rrset('@', 'loc') |
301 | 330 | self.failUnless(rrs is None) |
302 | 331 | |
303 | def testReplaceRdataset1(self): | |
332 | def testReplaceRdataset1(self): # type: () -> None | |
304 | 333 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
305 | 334 | rdataset = dns.rdataset.from_text('in', 'ns', 300, 'ns3', 'ns4') |
306 | 335 | z.replace_rdataset('@', rdataset) |
307 | 336 | rds = z.get_rdataset('@', 'ns') |
308 | 337 | self.failUnless(rds is rdataset) |
309 | 338 | |
310 | def testReplaceRdataset2(self): | |
339 | def testReplaceRdataset2(self): # type: () -> None | |
311 | 340 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
312 | 341 | rdataset = dns.rdataset.from_text('in', 'txt', 300, '"foo"') |
313 | 342 | z.replace_rdataset('@', rdataset) |
314 | 343 | rds = z.get_rdataset('@', 'txt') |
315 | 344 | self.failUnless(rds is rdataset) |
316 | 345 | |
317 | def testDeleteRdataset1(self): | |
346 | def testDeleteRdataset1(self): # type: () -> None | |
318 | 347 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
319 | 348 | z.delete_rdataset('@', 'ns') |
320 | 349 | rds = z.get_rdataset('@', 'ns') |
321 | 350 | self.failUnless(rds is None) |
322 | 351 | |
323 | def testDeleteRdataset2(self): | |
352 | def testDeleteRdataset2(self): # type: () -> None | |
324 | 353 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
325 | 354 | z.delete_rdataset('ns1', 'a') |
326 | 355 | node = z.get_node('ns1') |
327 | 356 | self.failUnless(node is None) |
328 | 357 | |
329 | def testNodeFindRdataset1(self): | |
358 | def testNodeFindRdataset1(self): # type: () -> None | |
330 | 359 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
331 | 360 | node = z['@'] |
332 | 361 | rds = node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) |
333 | 362 | exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') |
334 | 363 | self.failUnless(rds == exrds) |
335 | 364 | |
336 | def testNodeFindRdataset2(self): | |
337 | def bad(): | |
365 | def testNodeFindRdataset2(self): # type: () -> None | |
366 | def bad(): # type: () -> None | |
338 | 367 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
339 | 368 | node = z['@'] |
340 | 369 | node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) |
341 | 370 | self.failUnlessRaises(KeyError, bad) |
342 | 371 | |
343 | def testNodeGetRdataset1(self): | |
372 | def testNodeGetRdataset1(self): # type: () -> None | |
344 | 373 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
345 | 374 | node = z['@'] |
346 | 375 | rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) |
347 | 376 | exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') |
348 | 377 | self.failUnless(rds == exrds) |
349 | 378 | |
350 | def testNodeGetRdataset2(self): | |
379 | def testNodeGetRdataset2(self): # type: () -> None | |
351 | 380 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
352 | 381 | node = z['@'] |
353 | 382 | rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) |
354 | 383 | self.failUnless(rds is None) |
355 | 384 | |
356 | def testNodeDeleteRdataset1(self): | |
385 | def testNodeDeleteRdataset1(self): # type: () -> None | |
357 | 386 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
358 | 387 | node = z['@'] |
359 | 388 | node.delete_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) |
360 | 389 | rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) |
361 | 390 | self.failUnless(rds is None) |
362 | 391 | |
363 | def testNodeDeleteRdataset2(self): | |
392 | def testNodeDeleteRdataset2(self): # type: () -> None | |
364 | 393 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
365 | 394 | node = z['@'] |
366 | 395 | node.delete_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) |
367 | 396 | rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) |
368 | 397 | self.failUnless(rds is None) |
369 | 398 | |
370 | def testIterateRdatasets(self): | |
399 | def testIterateRdatasets(self): # type: () -> None | |
371 | 400 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
372 | 401 | ns = [n for n, r in z.iterate_rdatasets('A')] |
373 | 402 | ns.sort() |
374 | 403 | self.failUnless(ns == [dns.name.from_text('ns1', None), |
375 | 404 | dns.name.from_text('ns2', None)]) |
376 | 405 | |
377 | def testIterateAllRdatasets(self): | |
406 | def testIterateAllRdatasets(self): # type: () -> None | |
378 | 407 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
379 | 408 | ns = [n for n, r in z.iterate_rdatasets()] |
380 | 409 | ns.sort() |
384 | 413 | dns.name.from_text('ns1', None), |
385 | 414 | dns.name.from_text('ns2', None)]) |
386 | 415 | |
387 | def testIterateRdatas(self): | |
416 | def testIterateRdatas(self): # type: () -> None | |
388 | 417 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
389 | 418 | l = list(z.iterate_rdatas('A')) |
390 | 419 | l.sort() |
398 | 427 | '10.0.0.2'))] |
399 | 428 | self.failUnless(l == exl) |
400 | 429 | |
401 | def testIterateAllRdatas(self): | |
430 | def testIterateAllRdatas(self): # type: () -> None | |
402 | 431 | z = dns.zone.from_text(example_text, 'example.', relativize=True) |
403 | 432 | l = list(z.iterate_rdatas()) |
404 | 433 | l.sort(key=_rdata_sort) |
429 | 458 | exl.sort(key=_rdata_sort) |
430 | 459 | self.failUnless(l == exl) |
431 | 460 | |
432 | def testTTLs(self): | |
461 | def testTTLs(self): # type: () -> None | |
433 | 462 | z = dns.zone.from_text(ttl_example_text, 'example.', relativize=True) |
434 | n = z['@'] | |
435 | rds = n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) | |
463 | n = z['@'] # type: dns.node.Node | |
464 | rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA)) | |
436 | 465 | self.failUnless(rds.ttl == 3600) |
437 | 466 | n = z['ns1'] |
438 | rds = n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A) | |
467 | rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) | |
439 | 468 | self.failUnless(rds.ttl == 86401) |
440 | 469 | n = z['ns2'] |
441 | rds = n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A) | |
470 | rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) | |
442 | 471 | self.failUnless(rds.ttl == 694861) |
443 | 472 | |
444 | def testNoSOA(self): | |
445 | def bad(): | |
473 | def testTTLFromSOA(self): # type: () -> None | |
474 | z = dns.zone.from_text(ttl_from_soa_text, 'example.', relativize=True) | |
475 | n = z['@'] | |
476 | rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA)) | |
477 | self.failUnless(rds.ttl == 3600) | |
478 | soa_rd = rds[0] | |
479 | n = z['ns1'] | |
480 | rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) | |
481 | self.failUnless(rds.ttl == 694861) | |
482 | n = z['ns2'] | |
483 | rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) | |
484 | self.failUnless(rds.ttl == soa_rd.minimum) | |
485 | ||
486 | def testTTLFromLast(self): # type: () -> None | |
487 | z = dns.zone.from_text(ttl_from_last_text, 'example.', check_origin=False) | |
488 | n = z['@'] | |
489 | rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.NS)) | |
490 | self.failUnless(rds.ttl == 3600) | |
491 | n = z['ns1'] | |
492 | rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) | |
493 | self.failUnless(rds.ttl == 3600) | |
494 | n = z['ns2'] | |
495 | rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) | |
496 | self.failUnless(rds.ttl == 694861) | |
497 | ||
498 | def testNoTTL(self): # type: () -> None | |
499 | def bad(): # type: () -> None | |
500 | dns.zone.from_text(no_ttl_text, 'example.', check_origin=False) | |
501 | self.failUnlessRaises(dns.exception.SyntaxError, bad) | |
502 | ||
503 | def testNoSOA(self): # type: () -> None | |
504 | def bad(): # type: () -> None | |
446 | 505 | dns.zone.from_text(no_soa_text, 'example.', relativize=True) |
447 | 506 | self.failUnlessRaises(dns.zone.NoSOA, bad) |
448 | 507 | |
449 | def testNoNS(self): | |
450 | def bad(): | |
508 | def testNoNS(self): # type: () -> None | |
509 | def bad(): # type: () -> None | |
451 | 510 | dns.zone.from_text(no_ns_text, 'example.', relativize=True) |
452 | 511 | self.failUnlessRaises(dns.zone.NoNS, bad) |
453 | 512 | |
454 | def testInclude(self): | |
513 | def testInclude(self): # type: () -> None | |
455 | 514 | z1 = dns.zone.from_text(include_text, 'example.', relativize=True, |
456 | 515 | allow_include=True) |
457 | 516 | z2 = dns.zone.from_file(here('example'), 'example.', relativize=True) |
458 | 517 | self.failUnless(z1 == z2) |
459 | 518 | |
460 | def testBadDirective(self): | |
461 | def bad(): | |
519 | def testBadDirective(self): # type: () -> None | |
520 | def bad(): # type: () -> None | |
462 | 521 | dns.zone.from_text(bad_directive_text, 'example.', relativize=True) |
463 | 522 | self.failUnlessRaises(dns.exception.SyntaxError, bad) |
464 | 523 | |
465 | def testFirstRRStartsWithWhitespace(self): | |
524 | def testFirstRRStartsWithWhitespace(self): # type: () -> None | |
466 | 525 | # no name is specified, so default to the initial origin |
467 | # no ttl is specified, so default to the initial TTL of 0 | |
468 | z = dns.zone.from_text(' IN A 10.0.0.1', origin='example.', | |
526 | z = dns.zone.from_text(' 300 IN A 10.0.0.1', origin='example.', | |
469 | 527 | check_origin=False) |
470 | 528 | n = z['@'] |
471 | rds = n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A) | |
472 | self.failUnless(rds.ttl == 0) | |
473 | ||
474 | def testZoneOrigin(self): | |
529 | rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) | |
530 | self.failUnless(rds.ttl == 300) | |
531 | ||
532 | def testZoneOrigin(self): # type: () -> None | |
475 | 533 | z = dns.zone.Zone('example.') |
476 | 534 | self.failUnless(z.origin == dns.name.from_text('example.')) |
477 | def bad1(): | |
535 | def bad1(): # type: () -> None | |
478 | 536 | o = dns.name.from_text('example', None) |
479 | 537 | dns.zone.Zone(o) |
480 | 538 | self.failUnlessRaises(ValueError, bad1) |
481 | def bad2(): | |
482 | dns.zone.Zone(1.0) | |
539 | def bad2(): # type: () -> None | |
540 | dns.zone.Zone(cast(str, 1.0)) | |
483 | 541 | self.failUnlessRaises(ValueError, bad2) |
484 | 542 | |
485 | def testZoneOriginNone(self): | |
486 | dns.zone.Zone(None) | |
543 | def testZoneOriginNone(self): # type: () -> None | |
544 | dns.zone.Zone(cast(str, None)) | |
487 | 545 | |
488 | 546 | if __name__ == '__main__': |
489 | 547 | unittest.main() |
0 | import dns.rdtypes.txtbase | |
1 | ||
2 | class TTXT(dns.rdtypes.txtbase.TXTBase): | |
3 | """Test TXT-like record""" |
0 | 0 | import os.path |
1 | 1 | import sys |
2 | try: | |
3 | import unittest2 as unittest | |
4 | except ImportError: | |
5 | import unittest | |
2 | import unittest | |
6 | 3 | |
7 | 4 | if __name__ == '__main__': |
8 | 5 | sys.path.insert(0, os.path.realpath('..')) |
9 | suites = unittest.defaultTestLoader.discover('.') | |
6 | if len(sys.argv) > 1: | |
7 | pattern = sys.argv[1] | |
8 | else: | |
9 | pattern = 'test*.py' | |
10 | suites = unittest.defaultTestLoader.discover('.', pattern) | |
10 | 11 | if not unittest.TextTestRunner(verbosity=2).run(suites).wasSuccessful(): |
11 | 12 | sys.exit(1) |
0 | [tox] | |
1 | envlist = | |
2 | py27, | |
3 | py34, | |
4 | py35, | |
5 | py36, | |
6 | py37, | |
7 | flake8, | |
8 | pylint, | |
9 | mypy, | |
10 | coverage | |
11 | ||
12 | [testenv] | |
13 | ||
14 | commands= | |
15 | python setup.py test | |
16 | ||
17 | deps= | |
18 | ||
19 | [testenv:py27] | |
20 | deps = | |
21 | typing | |
22 | ||
23 | ||
24 | [testenv:flake8] | |
25 | commands = | |
26 | pip install flake8 | |
27 | flake8 dns | |
28 | ||
29 | [testenv:pylint] | |
30 | commands = | |
31 | pip install pylint | |
32 | pylint dns | |
33 | ||
34 | [testenv:mypy] | |
35 | commands = | |
36 | pip install mypy | |
37 | mypy examples tests | |
38 | ||
39 | [testenv:coverage] | |
40 | basepython = python2 | |
41 | ||
42 | deps = | |
43 | coverage | |
44 | ||
45 | commands = | |
46 | python setup.py install | |
47 | coverage run --rcfile=.coverage.ini setup.py test | |
48 | coverage report | |
49 | ||
50 | [pep8] | |
51 | show-pep8 = True | |
52 | show-source = True | |
53 |